Подтвердить что ты не робот

Как эффективно управлять памятью с помощью строк?

Рассмотрим образец кода.

public void testString()
{     
    int i = 0;
    while(i < 100000000)
    {
        String s ="Hi hello bye" +i;
        i++;          
    }
}

В каждой итерации создается новая String, и ее значение больше не требуется для следующей итерации. Я попробовал распечатать память, израсходованную pre, и опубликовать эту функцию testString(). Вот их значения.

Before invoking testString():

Total Memory: 91684864 (87.4375 MB)
Max Memory:   1360855040 (1297.8125 MB)
Free Memory:  72163552 (68.82052612304688 MB)

After invoking testString():
Total Memory: 424280064 (404.625 MB)
Max Memory:   1360855040 (1297.8125 MB)
Free Memory:  171766816 (163.80960083007812 MB).

Я вижу, что используется большой объем памяти, и я боюсь, что JVM Heap может выйти за пределы из-за текущего способа обработки строк. Строка, сгенерированная для итерации 1, больше не нужна на итерации 2, и ее пространство для хранения может быть освобождено. Я считаю, что здесь этого не происходит.

Я попытался использовать объекты StringBuffer и StringBuilder, и, кажется, очень незначительное улучшение в использовании памяти.

Просьба помочь мне лучше и оптимально подойти.

4b9b3361

Ответ 1

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

Это определенно происходит.

Вы создаете 100 миллионов строк, каждый из которых имеет не менее 13 символов, и большинство из них будет содержать около 20 символов. Каждая строка состоит из объекта (который имеет служебные данные) и char[] - поэтому я бы предположил, что он берет около 60 байтов для 20-символьной строки.

Если сбор мусора не был эффективным, 100 миллионов объектов, требующих по 60 байт, потребуют 6 ГБ, тогда как вы видите общую память, которая составляет всего около 300 Мбайт больше, чем начиналось с.

Строки собираются - просто не сразу.

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

Ответ 2

Что произойдет при первом запуске

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

Что произойдет, если функция часто вызывается

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

Просьба помочь мне лучше и оптимально подойти.

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

Ответ 3

Это зависит от того, как вы использовали StringBuilder. Это

    StringBuilder sb = new StringBuilder("");
    while (i < 100000000) {
        sb.delete(0, sb.length());
        sb.append("Hi hello bye").append(i);
        i++;
    }

будет намного более эффективным как в потреблении памяти, так и в скорости

Ответ 4

Строка неизменна, когда вы последовательно добавляете строку, которая создает строковый объект для каждой итерации, поэтому память превысила. если вы используете StringBuilder, он работает в одном объекте, даже если он обрабатывает более одной итерации. StringBuilder изменен.

StringBuilder iter = new StringBuilder("");
while (i < 100000000) 
{
    iter.delete(0, iter.length());
    iter.append("Hi hello bye").append(i);
    i++;
} 

Ответ 5

JVM никогда не должен выходить из памяти кучи из объектов без ссылок, таких как строки в вашей примерной программе, потому что перед тем, как он выдает исключение OutOfMemory, он будет запускать сборку мусора. См. Семантику этого исключения из Спецификация виртуальной машины Java, раздел 6.3:

OutOfMemoryError: реализация виртуальной машины Java закончилась либо виртуальной, либо физической памятью, и менеджер автоматического хранилища не смог восстановить достаточное количество памяти для удовлетворения запроса на создание объекта.

Ответ 6

Использование строковых сборщиков - лучший вариант, который у вас есть. Поскольку количество требуемых вами строк/строковых построек огромно, вы не можете ожидать, что JVM практически не сможет использовать большую часть памяти в любом случае. Ссылаясь на вашу статистику выше:

С использованием строки:% свободной памяти на общей памяти 40.48% ((163.80960083007812 MB/404.625 MB )*100).

С использованием строковых сборщиков:% свободной памяти на общей памяти 69.35 % ((252.659 MB/364.3125 MB)*100), что является довольно значительным улучшением. Кроме того, использование вышеперечисленной переменной находится только внутри области цикла, поэтому JVM-сборщик мусора будет работать, чтобы очистить память, как только это потребуется.

Ответ 7

Так как область видимости переменной только для итерации цикла while, так что здесь вам не нужно беспокоиться о переполнении памяти, так как при следующем запуске Garbage Collector она освободит всю память:

while(i < 100000000)
{
    String s ="Hi hello bye" +i;
    i++;          
}// no more required the s afterward

В каждой итерации строка s создаст новый объект, но предыдущий не требуется сейчас, поэтому он находится в памяти, пока сборщик мусора не очистит его.