Почему строки копируются в .NET? - PullRequest
4 голосов
/ 05 марта 2009

Поскольку строки являются неизменяемыми в .NET, почему они копируются для простых операций, таких как Substring или Split? Например, сохраняя char[] value, int start и int length, можно создать подстроку, чтобы просто указать на существующую строку, и мы могли бы сэкономить на копировании строки для многих простых операций. Поэтому мне интересно, почему было выбрано решение копировать строки для таких операций?

Например, было ли это сделано для поддержки текущей реализации StringBuilder? Или чтобы не хранить ссылку на большой char[], когда требуется всего несколько символов? Или какая-то другая причина, о которой вы можете подумать? Можете ли вы предложить плюсы и минусы для такого дизайна?

Как уже упоминалось @cletus и поддерживается @Jon Skeet, это больше похоже на вопрос, почему строки .NET были построены иначе, чем Java в этом аспекте.

Ответы [ 6 ]

10 голосов
/ 05 марта 2009

Это в основном то, как работает Java. У способа .NET есть несколько преимуществ, IMO:

  • Местонахождение ссылки - данные и длина находятся в одном месте
  • Меньше разыменований - данные находятся в фиксированной точке внутри самого строкового объекта; нет необходимости разыменовывать другой массив символов
  • Отсутствие псевдонимов, когда у вас есть одиночная символьная подстрока изначально большой строки, как упомянуто Рено.
  • В результате вы получите меньше объектов и переменных. В случае строки .NET (при условии, что не используется неиспользуемое пространство буфера), общий размер (на x86) составляет приблизительно 20+2*n байт. В Java у вас есть размер массива (12 + 2*n) байтов и самой строки (24 байта: служебные данные объекта, ссылка, start и count; он также кэширует хэш, если он когда-либо вычислял его). Таким образом, для пустой строки .NET-версия занимает около 20 байт по сравнению с Java-36. Конечно, это наихудший случай, и это будет лишь «постоянная разница», но если вы используете много независимых строк, которые могут в конечном итоге быть значительным. Еще больше о сборщике мусора.

Конечно, преимущества заключаются в том, что требуется меньше места, когда псевдоним выше не .

В конце концов, это будет зависеть от вашего использования - компилятор и среда выполнения не могут предсказать, какой шаблон использования более вероятен в вашем точном коде.

Может также быть преимущество взаимодействия текущего строкового представления, но я не знаю достаточно об этом, чтобы сказать наверняка.

РЕДАКТИРОВАТЬ: Я не уверен, почему ваш вопрос получил так много враждебных ответов. Это, конечно, не «тупой» способ представления строки, и он явно работает. Я полагаю, что страхи по поводу потери и сложности данных в данном случае в значительной степени просто FUD - реализация строки Java проста и надежна. Лично я подозреваю , что .NET способ делать вещи более эффективен в большинстве программ, и я подозреваю, что MS провела исследование, чтобы проверить это, но, безусловно, будут ситуации, когда "общий доступ" Модель работает лучше.

5 голосов
/ 05 марта 2009

Если бы вы повторно использовали одну и ту же строку для возврата подстрок, что произойдет, если основная строка выйдет из области видимости?

В лучшем случае он должен оставаться в памяти и не может быть собран до тех пор, пока не будут освобождены все подстроки, так что в итоге вы будете использовать фактически больше памяти.

Это только одна из проблем.

По сути, сборщик мусора будет иметь несколько вариантов:

  • сохранить всю исходную строку в памяти, даже если ее можно использовать только очень короткой подстрокой.

  • Освободить части исходной строки, на которые нет ссылок, и сохранить только подстроку там, где она есть. Это создаст большую фрагментацию, а это означает, что сборщику мусора, вероятно, придется в какой-то момент переместить строки: в конечном итоге мы все равно сделаем копию.

Я уверен, что он имеет свои варианты использования, и он может иногда быть более эффективным при работе с подстроками (скажем, при работе с большими документами XML).
Однако, как сказал Джон, объектам Java-строк требуется больше места, поэтому, если у вас много маленьких строк, они могут фактически использовать больше памяти, чем .Net.

Это компромисс.
Я думаю, что если вы находитесь в ситуации, когда действительно важно, как управляется память, и вам нужно иметь абсолютно предсказуемое поведение, ни Java, ни .Net не будут лучшими инструментами.

Мы используем сборщики мусора, потому что они оптимизированы для эффективной работы в подавляющем большинстве случаев.
Важно знать, как они работают, но независимо от того, используют ли они строки повторно или нет, это скорее оптимизация, оставленная базовой структуре, и она не должна слишком сильно просачиваться на поверхность.
В конце концов, ГК здесь, чтобы помочь нам.

1 голос
/ 05 марта 2009

В вашем примере подстроки это будет означать, что мы повторно выполняем логику подстроки каждый раз, когда делаем ссылку на «новую» строку. Из-за одних только издержек становится очевидным, почему мы копируем строки.

0 голосов
/ 05 марта 2009

Если строковый объект будет содержать ссылку на символьные данные, это будет означать, что большинство строк будет двумя объектами вместо одного.

0 голосов
/ 05 марта 2009

Поверь мне, ты бы ненавидел это, если бы строки не были неизменными. Чтобы дать вам пример из Java: java.util.Date изменчив и это кошмар. В основном это заставляет любого, кто получает данные в качестве параметра или функцию, возвращать его, должен защищенно копировать их.

Я не могу говорить о строках .Net, но операция подстроки в Java на самом деле ссылается на основную строку, что означает, что каждая строка в Java имеет около 16-20 байт (указатель на строку, начальный индекс, конечный индекс, длина и, возможно, что-то еще). Это имеет как преимущества, так и недостатки. Это может быть настоящая «ошибка» с точки зрения памяти. В одном проекте, над которым я работал, у нас было большое использование памяти. Оказалось, что мы получаем большие сообщения (тысячи символов) и обрабатываем их с помощью подстрок. Поскольку подстроки сохраняли ссылку на исходную строку, исходная строка никогда не очищалась.

Теперь вы можете обойти это, используя конструктор String, но это неочевидно, и многие люди этого не знают.

По сути, подстроки, о которых вы говорите, - это настоящая банка червей. Будьте осторожны с тем, что вы хотите.

0 голосов
/ 05 марта 2009

Полагаю, ключ подсвечивает разницу между:

  1. неизменная строка
  2. неизменяемая строка существует вечно

То, что вы говорите, сработало бы, если бы строки были №2. Однако, хотя строки неизменны, они могут быть уничтожены.

Как вы можете видеть дальше, у них есть свои расходы:

  1. неизменяемая строка - всегда делайте копии, как вы упомянули
  2. Строка, которая является неизменной, существует вечно, сохраняя каждую строку, созданную навсегда

Легко понять, почему # 1 лучше:)

(но я не имею в виду, что № 2 плохой или тупой)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...