Струны и сборка мусора - PullRequest
16 голосов
/ 11 марта 2010

Я слышал противоречивые истории на эту тему и ищу немного ясности.

Как можно избавиться от объекта string немедленно или с наименьшими его следами?

Ответы [ 8 ]

7 голосов
/ 11 марта 2010

Это зависит. Строковые литералы имеют значение interned по умолчанию, поэтому, даже если ваше приложение больше не ссылается на него, оно не будет собрано, так как на него ссылается внутренняя структура. Другие строки похожи на любой другой управляемый объект. Как только они перестают ссылаться на ваше приложение, они получают право на сборку мусора.

Подробнее об интернировании здесь в этом вопросе: Где находятся строковые литералы Java и .NET?

6 голосов
/ 11 марта 2010

Если вам нужно защитить строку и иметь возможность распоряжаться ею в любое время, используйте System.Security.SecureString class.

Защита конфиденциальных данных с помощью класса SecureString в .NET 2.0

5 голосов
/ 10 февраля 2014

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

public unsafe static void Clear(this string s)
{
  fixed(char* ptr = s)
  {
    for(int i = 0; i < s.Length; i++)
    {
      ptr[i] = '\0';
    }
  }
}
3 голосов
/ 25 марта 2010

Я отвечу на этот вопрос с точки зрения безопасности.

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

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

Хитрость в том, чтобы вообще никогда не помещать информацию в строку.

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

Способ, которым я разбираюсь с этим, заключается в следующем: я помещаю пароль в байтовый массив, записывая события нажатия клавиш в элементе управления textbox. Текстовое поле никогда не содержало ничего, кроме звездочек и отдельных символов. Пароль никогда не существовал как строка в любое время. Затем я хэшировал массив байтов и обнулял оригинал. Затем хэш был XOR-код со случайным жестко закодированным ключом, который использовался для шифрования всех конфиденциальных данных.

После того, как все было зашифровано, ключ был обнулен.

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

2 голосов
/ 11 марта 2010

Не существует детерминированного способа очистки всех следов строки (System.String) из памяти. Ваши единственные варианты - использовать массив символов или объект SecureString.

2 голосов
/ 11 марта 2010

Это все зависит от сборщика мусора, чтобы справиться с этим для вас. Вы можете принудительно запустить очистку, вызвав GC.Collect(). Из документов:

Используйте этот метод, чтобы попытаться вернуть все память, которая недоступна.

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

Это самое близкое, что ты мне думаешь !!

1 голос
/ 11 марта 2010

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

Для начинающих разработчиков распространена ошибка объявлять свои строки 'private string ...' в самом классе.

Я также видел опытных разработчиков из лучших побуждений, которые пытались кэшировать некоторую сложную конкатенацию строк (a + b + c + d ...) в закрытой переменной-члене, чтобы им не приходилось продолжать ее вычислять. Большая ошибка - перерасчет почти не занимает много времени, временные строки собираются почти сразу же, когда происходит первое поколение GC, а память, поглощаемая кэшированием всех этих строк, просто отбирает доступную память у более важных элементов, таких как записи кэшированной базы данных. или вывод на кешированную страницу.

0 голосов
/ 11 марта 2010

Установите строковую переменную равной нулю, если она вам не нужна.

string s = "dispose me!";
...
...
s = null;

и затем вызовите GC.Collect(), чтобы отозвать сборщик мусора, но GC НЕ МОЖЕТ гарантировать, что строка будет собрана немедленно.

...