Интернирование строк в .Net Framework - Каковы преимущества и когда использовать интернирование - PullRequest
39 голосов
/ 08 ноября 2011

Я хочу знать процесс и внутреннюю структуру интернирования строк , специфичные для .Net framework . Также хотелось бы узнать о преимуществах использования интернирования и сценариях / ситуациях, когда мы должны использовать интернирование строк для повышения производительности. Хотя я изучал интернатуру по книге CLR Джеффри Рихтера, но я все еще в замешательстве и хотел бы узнать это более подробно.

[Редактирование], чтобы задать конкретный вопрос с примером кода, как показано ниже:

private void MethodA()
{
    string s = "String"; // line 1 - interned literal as explained in the answer        

    //s.intern(); // line 2 - what would happen in line 3 if we uncomment this line, will it make any difference?
}

private bool MethodB(string compareThis)
{
    if (compareThis == "String") // line 3 - will this line use interning (with and without uncommenting line 2 above)?
    {
        return true;
    }
    return false;
}

Ответы [ 5 ]

30 голосов
/ 08 ноября 2011

В общем, интернирование - это то, что происходит автоматически, когда вы используете строковые литеральные значения.Interning обеспечивает преимущество, заключающееся в том, что в памяти хранится только одна копия литерала, независимо от того, как часто он используется.

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

Потенциально есть некоторые преимущества, если вы собираетесь много работать со сравнениями потенциально идентичных сгенерированных во время выполнения строк (поскольку интернирование может ускорить сравнение с помощьюReferenceEquals).Тем не менее, это узкоспециализированное использование, которое потребует значительного количества профилирования и тестирования, и я бы не подумал об оптимизации, если бы не было решенной проблемы.

20 голосов
/ 08 ноября 2011

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

Преимущества микрооптимизации, связанные с интернированием строк вручную, минимальные , поэтому, как правило, не рекомендуется.

Это, вероятно, описывает это:

class Program
{
    const string SomeString = "Some String"; // gets interned

    static void Main(string[] args)
    {
        var s1 = SomeString; // use interned string
        var s2 = SomeString; // use interned string
        var s = "String";
        var s3 = "Some " + s; // no interning 

        Console.WriteLine(s1 == s2); // uses interning comparison
        Console.WriteLine(s1 == s3); // do NOT use interning comparison
    }
}
18 голосов
/ 13 сентября 2013

Это «старый» вопрос, но у меня под ним другой угол.

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

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

Большинство полей в этих объектах являются строками, и пул значений довольно мал (во всяком случае, намного меньше, чем число экземпляров).

Если бы это были временные объекты, это не имело бы значения, потому что строковые поля часто собирались бы мусором. Но поскольку ссылки на них сохранялись, их использование памяти начало накапливаться (даже когда новые уникальные значения не добавлялись).

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

8 голосов
/ 02 марта 2017

Интернализация строк влияет на потребление памяти.

Например, если вы читаете строки и храните их в списке для кэширования; и та же самая строка встречается 10 раз, строка фактически сохраняется только один раз в памяти, если используется string.Intern. Если нет, строка сохраняется 10 раз.

В приведенном ниже примере вариант string.Intern потребляет около 44 МБ, а без версии (без комментариев) - 1195 МБ.

static void Main(string[] args)
{
    var list = new List<string>();

    for (int i = 0; i < 5 * 1000 * 1000; i++)
    {
        var s = ReadFromDb();
        list.Add(string.Intern(s));
        //list.Add(s);
    }

    Console.WriteLine(Process.GetCurrentProcess().PrivateMemorySize64 / 1024 / 1024 + " MB");
}

private static string ReadFromDb()
{
    return "abcdefghijklmnopqrstuvyxz0123456789abcdefghijklmnopqrstuvyxz0123456789abcdefghijklmnopqrstuvyxz0123456789" + 1;
}

Интернализация также улучшает производительность для сравнения. Пример ниже интерновской версии занимает примерно 1 единицу времени, в то время как неопытный занимает 7 единиц времени.

static void Main(string[] args)
{
    var a = string.Intern(ReadFromDb());
    var b = string.Intern(ReadFromDb());
    //var a = ReadFromDb();
    //var b = ReadFromDb();

    int equals = 0;
    var stopwatch = Stopwatch.StartNew();
    for (int i = 0; i < 250 * 1000 * 1000; i++)
    {
        if (a == b) equals++;
    }
    stopwatch.Stop();

    Console.WriteLine(stopwatch.Elapsed + ", equals: " + equals);
}
5 голосов
/ 26 июня 2017

Сроковые строки имеют следующие характеристики:

  • Две одинаковые интернированные строки будут иметь один и тот же адрес в памяти.
  • Память, занимаемая интернированными строками, не освобождается до тех пор, пока ваше приложениезавершается.
  • Использование строки включает вычисление хэша и поиск его в словаре, который потребляет циклы ЦП.
  • Если несколько потоков одновременно обрабатывают строки, они блокируют друг друга, потому что доступ ксловарь интернированных строк сериализуется.

Последствия этих характеристик:

  • Вы можете проверить две интернированные строки на равенство, просто сравнив указатель адресачто намного быстрее, чем сравнение каждого символа в строке.Это особенно верно, если строки очень длинные и начинаются с одинаковых символов.Вы можете сравнить интернированные строки с помощью метода Object.ReferenceEquals, но безопаснее использовать оператор string ==, потому что он проверяет, являются ли строки первым в Интернете.

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

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

  • Если у вас очень большое количество интернированных строк, интернирование строк может стать медленным, и потоки будут блокировать друг друга при доступе к словарю интернированных строк.

Вы должны использовать интернирование строк, только если:

  1. Набор строк, которые вы интернируетедовольно мал.
  2. Вы сравниваете эти строки много раз для каждого интернирования их.
  3. Вы действительноВам небезразлична мелкая оптимизация производительности.
  4. У вас мало потоков, агрессивно интернирующих строк.
...