C#: строки с одинаковым содержимым

Я слышал и читал, что строка не может быть изменена (неизменной?). Это должно быть правильно, я думаю. Но я также слышал, что две строки с одинаковым содержимым совместно используют одно и то же пространство памяти (или как вы это называете). Это правильно?

И если это так, значит ли это, что если я создам список с тысячами строк, он не будет занимать много места вообще, если большинство этих строк будут равны друг другу?

4 ответа

Решение

РЕДАКТИРОВАТЬ: В ответе ниже я упомянул пул интерна как специфичный для AppDomain; Я почти уверен, что это то, что я наблюдал ранее, но документы MSDN для String.Intern предполагают, что для всего процесса существует единый пул интернирования, что делает это еще более важным.

Оригинальный ответ

(Я собирался добавить это как комментарий, но я думаю, что это достаточно важный момент, чтобы нуждаться в дополнительном ответе...)

Как объяснили другие, интернирование строк происходит для всех строковых литералов, но не для "динамически создаваемых" строк (например, тех, которые считываются из базы данных или файла или создаются с использованием StringBuilder или же String.Format.)

Однако я бы не советовал звонить String.Intern чтобы обойти последний пункт: он будет заполнять пул стажеров в течение всей жизни вашегоAppDomain, Вместо этого используйте пул, который является локальным только для вашего использования. Вот пример такого пула:

public class StringPool
{
    private readonly Dictionary<string,string> contents =
        new Dictionary<string,string>();

    public string Add(string item)
    {
        string ret;
        if (!contents.TryGetValue(item, out ret))
        {
            contents[item] = item;
            ret = item;
        }
        return ret;
    }
}

Вы бы тогда просто использовали что-то вроде:

string data = pool.Add(ReadItemFromDatabase());

(Обратите внимание, что пул не ориентирован на многопотоковое исполнение; для нормального использования этого не требуется).

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

РЕДАКТИРОВАТЬ: просто чтобы уточнить, почему это лучше, чем с помощью String.Intern... предположим, что вы прочитали несколько строк из базы данных или файла журнала, обработали их, а затем перешли к другой задаче. Если вы позвоните String.Intern на этих строках они никогда не будут собирать мусор, пока ваш AppDomain жив - и, возможно, даже тогда. Если вы загрузите несколько разных файлов журналов, вы будете постепенно накапливать строки в вашем пуле, пока не закончите или не исчерпаете память. Вместо этого я предлагаю такую ​​схему:

void ProcessLogFile(string file)
{
    StringPool pool = new StringPool();
    // Process the log file using strings in the pool
} // The pool can now be garbage collected

Здесь вы получаете преимущество нескольких строк в одном и том же файле, которые существуют только один раз в памяти (или, по крайней мере, только один раз проходят мимо gen0), но вы не загрязняете "глобальный" ресурс (внутренний пул).

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

http://msmvps.com/blogs/manoj/archive/2004/01/09/1549.aspx

Если я правильно помню, строки, которые жестко закодированы в коде, объединяются отдельно. Это называется "Interned", и существует метод для запроса, является ли строка: String.IsInterned Method

На этой странице в разделе "Замечания" вы можете прочитать:

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

Надеюсь, это вам немного поможет, и поправьте меня, если я ошибаюсь.

Матиас

Чтобы сделать строки "разделяющими" их места в памяти, нужно интернировать их в пул интернирования, который содержит одну ссылку на каждую уникальную строку литерала, объявленную или созданную программным способом в вашей программе.

Обратите внимание, что все строковые литералы в коде автоматически интернируются.

Другие вопросы по тегам