ОЗУ не очищается даже после вызова System.gc () - PullRequest
0 голосов
/ 07 июня 2019

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

public static ArrayList<String> my = new ArrayList<>();
public static int val = 0;
// Code for Start Button 
try {
    new Thread(new Runnable() {
        @Override
        public void run() {
            String ss = "";
            for (int i = 0; i < 10000; i++) {
                ss += "ABC";
            }
            while (true) {
                if (val == 0) {
                    for (int i = 0; i < 30; i++) {
                        my.add(ss + new SimpleDateFormat("yyyyMMddHHmmssSSS"));
                    }
                    try {
                        Thread.sleep(50);
                    } catch (InterruptedException ex) {
                    }
                } else {
                    Thread.yield();
                    break;
                }
            }
        }
    }).start();
} catch (Exception e) {
    e.printStackTrace();
}
// Code for Stop Button
val = 1;
my.clear();
my = null;
System.gc();
Runtime.getRuntime().freeMemory();

1 Ответ

2 голосов
/ 07 июня 2019

Сборка мусора зависит от различных факторов, таких как то, какой сборщик вы используете, физическую память машины, а также версию JVM, которую вы используете.Поскольку о них здесь не так много говорится, предсказать, что может быть причиной этого, довольно сложно.Я предполагаю, что вы используете Java 8, так как в настоящее время это более популярная версия.

Начиная с Java 8, в модели памяти JVM произошли изменения.Что, теперь нет места Permanent Generation.Это место, где расположен String Pool (я использую String, следовательно, вы используете конкатенацию String в циклах).См. Этот документ Модель памяти Java (JVM) - Управление памятью в Java .* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *}}} * * * * * * * * * * * * * С тех пор, как в Java 8 появилась новая ячейка памяти с именем Metaspace, которая находится в основной памяти за пределами основной памяти за пределами основной памяти *1013*JVM.

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

Даже если Metaspace находится внутри основной памяти / физической памяти и может динамически расширяться, все же имеет ограничение физической памяти.Вот почему я ранее говорил о физической памяти компьютера как о факторе зависимости.

Когда мы приступаем к сборке мусора, вы не упомянули никакой конфигурации GC.Поэтому я предполагаю, что вы используете Parallel GC , который является сборщиком Java 8 по умолчанию (вы можете узнать больше о GC по той же ссылке, что и выше).Я полагаю, что производительность Parallel GC достаточна для этой задачи.Поэтому достаточно вызвать System.gc() без какого-либо флага JVM.

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

Обычно строки, созданные с использованием строкового литерала (String s = "abc"), не становятся приемлемыми для мусора.Это потому, что в коде каждого метода, использующего литерал, есть неявная ссылка на объект String (проверьте этот ответ Когда строка будет собирать мусор в java ).Таким образом, вы должны потерять эти неявные ссылки, прекратив выполнение функции.

Поскольку вы используете новый Thread для выполнения этой конкатенации, и я не могу найти место, где вы прерываете потоки вы вызываете Thread.yield() ( Thread.yield ), чтобы сообщить планировщику потока о необходимости использования ЦП для этого конкретного потока и пометить, что поток желает быть запланированным как можно скорее снова, проясните, что этот Thread объект все еще живет и ссылается на эти объекты String, не делая их пригодными для мусора.Это может быть причиной того, что вызов System.gc() не работает.

В качестве решения попробуйте interrupt поток вместо yield.

Обновление 1:

До Java 7 String Pool находился в PermGen, что не подходит для сборки мусора.PermGen имеет фиксированный размер и не способен расширяться во время выполнения.Если у PermGen недостаточно места, выдается ошибка java.lang.OutOfMemoryError: PermGen.В качестве временного исправления мы можем увеличить размер PermGen, используя флаг -XX:MaxPermSize=512m.

Но помните, что это работает только на JVM до Java 8 и в Java 7, это не делает различий в смысле увеличения доступности String Pool размера, следовательно, Java 7 и более, String Pool перешел наHeap пробел.

...