Вопрос по java, garbage-collection, jconsole, memory, heap-memory – System.gc () против кнопки GC в JVisualVM / JConsole

5

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

Чтобы получить порядок величины для прогона с реальным пиком в 100 МБ (я тестировал несколько раз точно такую же конфигурацию ввода / параметров, заставляя память jvm с -Xmx и -Xms уменьшать значение, я получаюИсключение в теме "главный" java.lang.OutOfMemoryError: Превышен лимит накладных расходов GC < 100 МБ, со стабильными и воспроизводимыми результатами), он занимает около 1,1 ГБ, чтоВот почему для меня крайне важно получить реальные цифры, потому что они сильно отличаются!

я провел последние 10 дней, читая вопросы в Интернете и в stackoverflow, что я на самом деле знаю:

System.gc () "предложить" GC запускается, не вынуждает его каким-либо образом, поэтому на него нельзя положиться для обнаружения пиков использования памяти

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

Нет никакого способа легко получить занятость памяти метода (выраженную как пик выделения памяти объекта)

Дело в том, что я сам испытал, что вызовы System.gc () ненадежны (например, разные прогоны одной и той же конфигурации, разное чтение памяти после System.gc () из-за того, что GC действительно вызывается или нет), но когда Я нажимаюКнопка GC " в JVisualVM или Jconsole этоникогда не запускает GC или отказывается это делать.

Итак, мой вопрос: вызывая их реализацию этой кнопки (я непока не попробую, но для чего яВы читали до сих пор, кажется возможным с помощью jconsole.jar сприложить API) будет отличаться от вызова System.gc () непосредственно из моего кода, что решит мою проблему? Если нет, то как вы объяснитедетерминированное поведение " этой кнопки?

До сих пор я проводил ручное тестирование реального пика памяти при 10 увеличивающихся размерах схем (для этого вида измерений схемы генерируются автоматически из одного "параметр сложности ") и я построил ожидаемую кривую, если я не смогу найти лучшего решения, я хочу запустить свой код в качестве внешнего jar с -Xmx / -Xms, равным немного меньшему, чем мой прогноз ожидаемого пика памяти, ловя OutMemoryException во внешнем процессе ErrorStream и перезапуск с увеличенным объемом памяти до полного выполнения. (Если наивный прогноз памяти не будет достаточно надежным, я буду применять соответствующие методы машинного обучения). Я знаю, что это не элегантное решение, но в моем сценарии (академическом) я могу позволить себе потратить дополнительное время на эти измерения. Если у вас есть другие предложения или улучшения к этому методу грубой силы, вы (крайне) можете поделиться ими.

Информация о системе (машина Fedora 17, 64 бит):

Java-версия "1.7.0_04" Java (TM) SE Runtime Environment (сборка 1.7.0_04-b20) Java HotSpot (TM) 64-битная виртуальная машина сервера (сборка 23.0-b21, смешанный режим)

Заранее спасибо, Алессандро

Вы пытались проверить просто с двумя или тремяSystem.gc() звонки подряд, с возможнымsleep между ними? Потому что я еще не видел, чтобы этот метод потерпел неудачу. Первый вызов может вызвать незначительную коллекцию, но второй уже наверняка вызовет полный сборщик мусора. Marko Topolnik
@MarkoTopolnik: в последние часы я реализовал свой "GetMemory» Метод, использующий ваше предложение, после нескольких попыток кажется достаточно стабильным с 2 вызовами и только 1сек сна. Его стабильность позволила мне найти действительно хорошую точку для оценки пиков памяти, поэтому моя проблема решена (используя 2 разных профиля для памяти / времени). Если вы положите это в ответ, я приму это! Еще раз большое спасибо. Alessandro S.
Нет @MarkoTopolnik, я пробовал только один вызов System.gc (), я попробую, если он докажет "вполне» стабильный может быть достаточно для построения чего-то. Спасибо! Alessandro S.

Ваш Ответ

4   ответа
-1

Вы можете заставить GC, как это ....

private static void force_gc()
{
    Object obj = new Object();
    WeakReference<object> ref = new WeakReference<object>(obj);
    obj = null;
    while (ref.get() != null)
    {
        Log.d(LOGTAG, "Forcing gc() ...");
        System.gc();
    }
}
<p>кроме этого ... яМне интересно посмотреть, где этот вопрос идет.</p></object></object>
Вы'Я был бы удивлен, узнав, что в некоторых обстоятельствах это действительно может быть лучше, но это, безусловно, проще, при достижении точно таких же гарантий. Код со слабыми ссылками выглядит просто такделает что-то умное, так чтовводит в заблуждение. Marko Topolnik
@MarkoTopolnik "Код со слабыми ссылками выглядит просто такделает что-то умное, так чтовводит в заблуждение ". не мог»не сказал это лучше - именно поэтому я думал, что это было лучше. Я не' заботиться о понижении, я знаю, ответ неЭто не имеет отношения к задаваемому вопросу. Shark
Обратите внимание, что это не я, кто положил отрицательный ответ на ваш ответ. Я просто прокомментировал свой опыт. Marko Topolnik
Я играл с этим раньше, это ненадежно. Слабый реф очищается нене гарантирую вещь. В частности, это неt гарантировать, что референт был собран. Marko Topolnik
2

У меня есть довольно положительный опыт с этим тривиальным подходом:

System.gc();
Thread.sleep(500);
System.gc();

Одного прогона GC часто недостаточно из-за проблем с завершением объекта, когда объект может быть воскрешен в процессе завершения. Поэтому дополнительная память освобождается во втором прогоне ГХ.

Заметьте, это, как и другие, казалось бы, "умнее», подходы, все эвристические и весьма зависят от точной версии JVM, включая его конфигурацию GC. Но во многих случаях вас не так сильно интересует общность: если она работает прямо сейчас и позволяет вам проводить измерения, это путь.

0

1) System.gc () "предложить" GC запускается, не вынуждает его каким-либо образом, поэтому на него нельзя положиться для обнаружения пиков использования памяти

Это то, что написано в спецификации, но если вы используете OpenJDK или HotSpot, он всегда будет выполнять Full GC, если вы не отключите его.

Обычно предлагается подсчитать занятость объекта

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

Нет никакого способа легко получить занятость памяти метода (выраженную как пик выделения памяти объекта)

Единственная память, которую использует метод, находится в стеке. Вы можете отследить, сколько объектов (количество, классы, размер) было создано в методе, но эти объекты неt принадлежат этому методу и могут использоваться где угодно, даже после того, как метод вернулся.

Если нет, то как вы объяснитедетерминированное поведение " этой кнопки?

Я бы отнес это к субъективному анализу. ;)

В идеале вы должны использовать JVM с 2-3-кратным минимальным объемом памяти, необходимым для эффективной работы. Попытка сэкономить несколько 100 МБ, которые стоят менее 1 доллара, не всегда полезна. ;)

@PeterLawrey Нет, я хочу сэкономить 99 МБ на 100 экземплярах, всего 10 ГБ. Это может означать разницу между незаметной работой на одном хосте и необходимостью работы всего кластера. Разница в количестве настроек, экспертизе, бизнес-решениях и т. Д. Огромна. Marko Topolnik
Я добавил некоторую системную информацию к вопросу, спасибо @PeterLawrey. Под занятием метода я имею в виду кучу, используемую посредством выделения объектов, в данном случае всего этого итератора. Alessandro S.
Да, этов соответствии с моими выводами, где я бегу два или даже три GC 'с льготным периодом между ними. Marko Topolnik
Несмотря на то, что первым может быть полный сборщик мусора, все еще есть проблемы с финализацией и т. Д., Когда многим объектам на самом деле требуется два полных прохода сборщика мусора для сбора. Marko Topolnik
4

Насколько я знаю, Jconsole или любой другой инструмент использует только System.gc (). Там нет другого варианта. Как все знают, Java говорит всем не полагаться на System.gc (), но это незначит, это не такне работает вообще.

Итак, что касается вашего запроса, вы, похоже, обеспокоены тем, что при нажатии этой кнопки вызывается GC напрямую и до сих пор Java говорит только System.gc "предполагает» позвонить в GC. Я говорю, эта кнопка также вызывает System.gc () & это толькопредполагает» Java, чтобы попробовать для GC, и случается так, что java решает выполнить GC в это время сам (это не гарантировано, но каким-то образом это делает java).

Итак, чтобы доказать этот факт, я просто создал простую программу, которая просто создает множество объектов. Он прокомментировал строку с "System.gc ()», Теперь попробуйте сначала запустить эту же программу с комментарием System.gc () & затем, раскомментировав System.gc (). Убедитесь, что аргументы VM указаны как -verbose: gc -XX: + PrintGCTimeStamps -XX: + PrintGCDetails.

package ravi.tutorial.java.gc;

/**
 * Just to test GC. RUn with below VM arguments.
 * 
 * -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails
 * 
 * 
 * @author ravi.k
 * 
 */
public class TestGC {

    public static A a;

    /**
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {

        for (int i = 0; i < 100; i++) {
            populateObjects();
            System.out.println("population done for batch: " + i);
        }

    }

    public static void populateObjects() {
        for (int i = 0; i < 100000; i++) {
            a = new A("A");
        }
        //System.gc();
    }

}

class A {
    String s;

    public A(String s) {
        this.s = s;
    }
}

Вот частичные выходы из моей машины.

Commened System.gc (): здесь GC вызывается по желанию jre.

population done for batch: 0
population done for batch: 1
population done for batch: 2
population done for batch: 3
population done for batch: 4
population done for batch: 5
population done for batch: 6
population done for batch: 7
population done for batch: 8
population done for batch: 9
0.332: [GC 0.332: [ParNew: 17024K->410K(19136K), 0.0024479 secs] 17024K->410K(83008K), 0.0025219 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
population done for batch: 10
population done for batch: 11
population done for batch: 12
population done for batch: 13
population done for batch: 14
population done for batch: 15
population done for batch: 16
population done for batch: 17
population done for batch: 18
population done for batch: 19
0.344: [GC 0.344: [ParNew: 17434K->592K(19136K), 0.0011238 secs] 17434K->592K(83008K), 0.0011645 secs] [Times: user=0.00 sys=0.01, real=0.00 secs] 
population done for batch: 20
population done for batch: 21
population done for batch: 22
population done for batch: 23
population done for batch: 24
population done for batch: 25
population done for batch: 26
population done for batch: 27
population done for batch: 28
population done for batch: 29
population done for batch: 30
0.353: [GC 0.353: [ParNew: 17616K->543K(19136K), 0.0011398 secs] 17616K->543K(83008K), 0.0011770 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
population done for batch: 31
population done for batch: 32
population done for batch: 33

Uncommented System.gc (): здесь GC вызывается для каждого пакета. Теперь System.gc () предлагает только GC, но java выбирает сам запуск GC в это время. Это точно такой же случай для этой волшебной кнопки GC в других инструментах :)

0.337: [Full GC (System) 0.337: [CMS: 0K->400K(63872K), 0.0219250 secs] 3296K->400K(83008K), [CMS Perm : 4423K->4422K(21248K)], 0.0220152 secs] [Times: user=0.04 sys=0.00, real=0.02 secs] 
population done for batch: 0
0.364: [Full GC (System) 0.364: [CMS: 400K->394K(63872K), 0.0161792 secs] 2492K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0162336 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 
population done for batch: 1
0.382: [Full GC (System) 0.382: [CMS: 394K->394K(63872K), 0.0160193 secs] 2096K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0160834 secs] [Times: user=0.01 sys=0.00, real=0.01 secs] 
population done for batch: 2
0.399: [Full GC (System) 0.399: [CMS: 394K->394K(63872K), 0.0160866 secs] 2096K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0161489 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
population done for batch: 3
0.417: [Full GC (System) 0.417: [CMS: 394K->394K(63872K), 0.0156326 secs] 2096K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0156924 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
population done for batch: 4
0.434: [Full GC (System) 0.434: [CMS: 394K->394K(63872K), 0.0157274 secs] 2096K->394K(83008K), [CMS Perm : 4425K->4425K(21248K)], 0.0157897 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
population done for batch: 5

Чтобы добавить больше, это так же, как темы. Нет никакой гарантии, когда поток запускается, но всякий раз, когда мы пишем какую-либо примерную программу потока, поток запускает это время сам. Таким образом, мы не должны обвинять Java, почему он запустился, как только начался поток :). Ява только говорит не полагаться на эти вещи, но они действительно работают. Кроме того, хотя они работают в некоторых случаях неЯ имею в виду, они будут работать каждый раз. Даже те инструменты jconsole могут не выполнять GC, просто мы этого никогда не видели.

Спасибо, хорошо, я знаю, что всегда вызывается System.gc (), поэтому не стоит тратить время на вызов jconsole из моей программы, вы сэкономили много моего времени! Alessandro S.

Похожие вопросы