В то время как максимальный размер строки составляет ~ 0,75 ГБ, Visual Studio показывает 5 ГБ использования и объем памяти, почему разница? - PullRequest
0 голосов
/ 06 октября 2019

Этот код не делает ничего практичного, я просто смотрел, что произойдет.

Насколько я могу судить, единственными двумя переменными, которые сохраняются, являются (в конце концов) массивная строка и ничтожноеsize int отслеживая длину строки.

На моей машине строка достигает примерно 0,75 ГБ, и в этот момент появляется OutOfMemoryException. На этом этапе Visual Studio показывает около 5 ГБ использования. Поэтому мне интересно, почему существует несоответствие.

var initialText = "Test content";
var text = initialText;
var length = text.Length;
while (true)
{
    try
    {
        var currentLength = text.Length;
        Console.WriteLine($"Current Length - {currentLength}");
        Console.WriteLine($"Current Size in GB - {System.Text.Encoding.UTF8.GetByteCount(text)/1024.0/1024.0/1024.0}");
        text = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(text));
        Console.WriteLine($"Change In Size - {currentLength / (length + 0.0)}");
        length = currentLength;
    }
    catch (OutOfMemoryException)
    {
        break;
    }
}

В качестве второго вопроса, когда я начинаю запускать код, у моей машины есть около 11 ГБ свободного места в соответствии с диспетчером задач, и когда оно попадает в исключение,он вырос примерно на 3 ГБ, что не соответствует приведенным выше цифрам. Есть идеи?

1 Ответ

1 голос
/ 06 октября 2019

Во-первых, строки в .net - это последовательность слов UTF-16, поэтому каждый символ занимает 2 байта. Чтобы получить размер строки в памяти в байтах, нужно умножить ее длину на 2 (игнорируя заголовок экземпляра CLR).

Console.WriteLine($"Current Size in GB - {text.Length * 2.0 /1024/1024/1024}");

Еще одно ограничение - это размер массива в .NET, прочитайте примечания здесь , как заметил @TheGenral. Существует 2 ограничения: максимальный размер (2 ГБ) и максимальный индекс.

Ниже приведена измененная версия вашего теста:

var text = "Test content";
long length = text.Length;
try
{

    while (true)
    {
        var currentLength = text.Length;
        Console.WriteLine($"Current Length - {currentLength}");
        Console.WriteLine($"Current Size in GB - {text.Length * 2.0 / 1024 / 1024 / 1024}");
        text += new string('a', 500 * 1024*1024);
        length = currentLength;
        GC.Collect();
    }
}
catch (OutOfMemoryException e)
{
    Console.WriteLine(e);
}

StringBuilder разница версий:

var text = new StringBuilder("Test content");
...
text.Append('a', 500 * 1024*1024);

Если вы не включите gcAllowVeryLargeObjects , вы получите OOM с элементами 1B.

Мне не удалось получить элементы 2B с использованием конкатенации строк, но если вы переделаете этот тест с использованием StringBuilder, то вы сможете получить 2B символов. В этом случае вы столкнетесь со вторым ограничением: массивы не могут содержать более 2 миллиардов элементов. Здесь - обсуждение верхнего предела.

В обсуждается этот поток максимальная длина строки.

Если вы запустите этот код в Release В режиме вы увидите, что потребление памяти процессом почти равно размеру строки в выводе консоли.

Еще одна интересная вещь, которую я заметил и не могу объяснить, это то, что в StringBuilder, gcAllowVeryLargeObjects и Debug mode I 'Я могу достичь 4 ГБ, но в режиме Release он почти достигает 3 ГБ. Приветствуются комментарии, почему это происходит:)

...