Поиск использования памяти в Java

Ниже приведен сценарий, который мне нужно решить. Я нашел два решения.

Мне нужно поддерживать кеш данных, извлеченных из базы данных, для отображения в графическом интерфейсе Swing. Всякий раз, когда моя память JVM превышает 70% выделенной памяти, мне нужно предупредить пользователя о чрезмерном использовании. И как только использование памяти JVM превысит 80%, я должен остановить все запросы к базе данных и очистить существующий кеш, полученный как часть пользовательских операций, и уведомить пользователя. В процессе очистки я вручную удаляю некоторые данные на основе некоторых правил и инструктирую JVM для сборщика мусора. Всякий раз, когда происходит GC, если память очищается и достигает 60% выделенной памяти, мне нужно перезапустить всю обработку базы данных и вернуть контроль пользователю.

Для проверки статистики памяти JVM я нашел следующие два решения. Не мог решить, как лучше и почему.

  1. Runtime.freeMemory () — поток, созданный для запуска каждые 10 секунд и проверки свободной памяти, и если память превышает указанные ограничения, необходимые всплывающие окна сообщат пользователю и вызовут методы для остановки операций и освобождения памяти.

  2. MemoryPoolMXBean.getUsage() — в Java 5 появился JMX для получения моментального снимка памяти во время выполнения. В JMX я не могу использовать уведомление о пороге, поскольку оно будет уведомлять только тогда, когда память достигает/превышает заданный порог. Единственный способ использовать это опрос в MemoryMXBean и проверить статистику памяти за период.

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

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


person Satti    schedule 17.02.2009    source источник


Ответы (8)


Просто примечание: Runtime.freeMemory() не указывает объем памяти, оставшийся от выделения, это просто объем памяти, который свободен в текущей выделенной памяти (которая изначально меньше максимальной памяти, которую виртуальная машина настроена использовать), но растет со временем.

При запуске виртуальной машины максимальная память (Runtime.maxMemory()) просто определяет верхний предел памяти, которую может выделить виртуальная машина (настраивается с помощью параметра -Xmx VM). Общий объем памяти (Runtime.totalMemory()) — это начальный размер памяти, выделенной для процесса виртуальной машины (настраивается с помощью параметра -Xms VM), и он будет динамически увеличиваться каждый раз, когда вы выделяете больше, чем текущая свободная часть (Runtime.freeMemory()), до тех пор, пока не будет достигает максимальной памяти.

Метрика, которая вас интересует, — это память, доступная для дальнейшего выделения:

long usableFreeMemory= Runtime.getRuntime().maxMemory()
    -Runtime.getRuntime().totalMemory()
    +Runtime.getRuntime().freeMemory()

or:

double usedPercent=(double)(Runtime.getRuntime().totalMemory()
    -Runtime.getRuntime().freeMemory())/Runtime.getRuntime().maxMemory()
person Peter Walser    schedule 17.02.2009

Обычный способ справиться с такими вещами — использовать WeakReferences и SoftReferences. Вам нужно использовать оба: слабая ссылка означает, что вы не храните несколько копий вещей, а мягкие ссылки означают, что сборщик мусора будет цепляться за вещи, пока не начнет заканчиваться память.

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

person paulmurray    schedule 18.02.2009

Для JVM совершенно нормально использовать память до 100%, а они возвращаются к 10% после GC и делают это каждые несколько секунд.

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

Я предлагаю вам понять, чего вы на самом деле пытаетесь достичь, и посмотреть на проблему с другой стороны.

person Peter Lawrey    schedule 17.02.2009
comment
имхо, это не совсем нормально. Эти параметры можно настраивать (размеры пулов, полные интервалы GC и т. д.). - person Yoni Roit; 17.02.2009

Упомянутые вами требования явно противоречат тому, как работает сборка мусора в JVM.

из-за поведения JVM будет очень сложно правильно предупредить пользователей. В целом останавливать все манипуляции с базой данных, очищать вещи и начинать снова - это не выход.

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

В таких статьях, как http://www.kodewerk.com/advice_on_jvm_heap_tuning_dont_touch_that_dial.htm упоминаются плюсы и минусы и предложите хорошее объяснение того, что делает для вас виртуальная машина

person Peter    schedule 17.02.2009

Я использовал только первый метод для аналогичной задачи, и все было в порядке.

Одна вещь, которую вы должны отметить для обоих методов, заключается в том, чтобы реализовать своего рода устранение дребезга - т.е. как только вы обнаружите, что достигли 70% памяти, подождите минуту (или любое другое время, которое вы сочтете подходящим) - GC может работать в это время. время и очистить много памяти.

Если вы реализуете график Runtime.freeMemory() в своей системе, вы увидите, как память постоянно увеличивается и уменьшается, увеличивается и уменьшается.

person Yoni Roit    schedule 17.02.2009

VisualVM немного лучше, чем JConsole, потому что он дает вам хорошее визуальное представление сборщика мусора.

person djangofan    schedule 08.12.2009

Посмотрите в JConsole. Он графически отображает необходимую вам информацию, поэтому вам нужно адаптировать ее к вашим потребностям (учитывая, что вы работаете на Sun Java 6).

Это также позволяет вам отделить процесс наблюдения от того, на что вы хотите смотреть.

person Thorbjørn Ravn Andersen    schedule 17.02.2009

Я знаю, что очень поздно после исходного поста, но я решил опубликовать пример того, как я это сделал. Надеюсь, это будет кому-то полезно (подчеркну, это доказательство принципиального примера, ничего больше... тоже не особенно элегантно :) )

Просто вставьте эти две функции в класс, и все должно работать.

РЕДАКТИРОВАТЬ: О, и import java.util.ArrayList; import java.util.List;

public static int MEM(){
    return (int)(Runtime.getRuntime().maxMemory()-Runtime.getRuntime().totalMemory() +Runtime.getRuntime().freeMemory())/1024/1024;
}

public static void main(String[] args) throws InterruptedException
{
    List list = new ArrayList();

    //get available memory before filling list
    int initMem = MEM();
    int lowMemWarning = (int) (initMem * 0.2);
    int highMem = (int) (initMem *0.8);


    int iteration =0;
    while(true)
    {
        //use up some memory
        list.add(Math.random());

        //report
        if(++iteration%10000==0)
        {
            System.out.printf("Available Memory: %dMb \tListSize: %d\n", MEM(),list.size());

            //if low on memory, clear list and await garbage collection before continuing
            if(MEM()<lowMemWarning)
            {
                System.out.printf("Warning! Low memory (%dMb remaining). Clearing list and cleaning up.\n",MEM());

                //clear list
                list = new ArrayList();  //obviously, here is a good place to put your warning logic

                //ensure garbage collection occurs before continuing to re-add to list, to avoid immediately entering this block again
                while(MEM()<highMem)
                {
                    System.out.printf("Awaiting gc...(%dMb remaining)\n",MEM());
                    //give it a nudge
                    Runtime.getRuntime().gc(); 
                    Thread.sleep(250);
                }

                System.out.printf("gc successful! Continuing to fill list (%dMb remaining). List size: %d\n",MEM(),list.size());
                Thread.sleep(3000); //just to view output
            }
        }
    }
}

РЕДАКТИРОВАТЬ: Однако этот подход по-прежнему зависит от разумной настройки памяти в jvm с использованием -Xmx.

EDIT2: кажется, что строка запроса gc действительно помогает, по крайней мере, на моем jvm. ммв.

person miklatov    schedule 15.09.2011