«Углубленное понимание JVM» G1

Java
«Углубленное понимание JVM» G1

Примечание. Следующий контент в основном сочетается с 3-м резюме «Углубленное понимание JVM».

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


-XX:+UseG1GCвключиG1 GC

Раздел памяти G1

G1выглядит иCMSПохоже, но реализация сильно отличается.

традиционное поколениеGCРазделите общую память на несколько больших областей, таких какEden,S0,S1,TenuredЖдать.

а такжеG1Разделить область памяти на Nпрерывистый,тот же размеризRegion,RegionКонкретный размер составляет от 1 до 32 МБ, в зависимости от общего объема памяти, а цель — не более 2048. Как показано ниже:

image-20200701114120927
image-20200701114120927

каждыйRegionсуществуетG1играл разные роли, напримерEden(новый район), напримерSurvivor(зона выживания), илиOld(старость)

В дополнение к традиционной старости новое поколение,G1также разделенHumongousплощадь, используемая для хранения огромных предметов (humongous object,H-obj).

Для огромных объектов стоит отметить следующие моменты:

  • H-objопределяется как большее или равноеRegionполовина объекта

  • H-objназначить непосредственно наOld gen, чтобы предотвратить частое копирование. ноH-objпереработка неMixed GCэтап, ноconcurrent markingна сценеclean upпроцесс иfull GC

    Обратите внимание на этот момент, в процессе настройки выGCЭто предложение часто встречается в журнале

    [GC pause (G1 Humongous Allocation) (young) (initial-mark), 0.0029216 secs]

    Вопрос в том, почемуHumongous Allocationно вызвалyong gc.

    Причина в том, чтобы пройтиyong gcизinitial-markНачалоconcurrent marking, а затем черезclean upПерерабатывайте большие предметы

    Если вы хотите увидетьG1При регистрации, чтобы облегчить и быстро добратьсяGCэффект, вы можете выделить несколько больших объектов напрямую, чтобы заполнить всю кучу и вызватьGC, но если свет представляет собой большой объект, вы можете найтиGCжурнал неMixed GC, но частоYong GCа такжеConcurrent Marking, вот почему

  • H-objникогда не будет перемещен, хотяG1Алгоритм повторного использования обычно основан на сопоставлении меток, но дляH-objОн никогда не будет перемещен и либо будет переработан напрямую, либо будет существовать всегда. следовательноH-objМожет вызывать большую фрагментацию памяти и вызывать частыеGC


G1процесс переработки

G1Форма разбиения памяти определяетG1Необходимо управлять как молодым, так и старым поколением. В зависимости от области утилизации,G1Существует два режима рециркуляции:

  • Восстанавливается лишь часть памяти молодого поколения:Yong GC
  • Восстановите всю память молодого поколения и немного памяти старого поколения:Mixed GC

когдаmixed gcСкорость рециркуляции не поспевает за скоростью выделения памяти,G1будет использовать один поток (используяSerial Oldкод) дляFull GC

Среди них весьYong GCпроцесс назадSTW,а такжеMixed GCВ основном он включает в себя два этапа: первый этап - параллельный цикл маркировки (Concurrent Marking), этот процесс в основном для маркировки, а после завершения параллельной маркировки выполняется сборка мусора старого поколения (Evacuation): Этот процесс в основном копирует и перемещает объекты, организуетRegion.


Mixed GC

По логике надо сказать сначалаYong Gc,НоYong GCкажется нетG1фокус на рециркуляции, и нет параметров для контроляYong GC, так что пока пропустите это здесь. Но вы должны знать, чтоYong GCПроцесс утилизации аналогичен другим сборщикам мусора, и он также сначала помечается, а затем копируется. Но весь процесс будетSTW, и из-заYong GCПроцесс маркировки и оборотная сторонаMixed GCМаркеры параллелизма в (Concurrent Marking) на первом этапе начальная отметка (initial marking) выполняет ту же работу, поэтомуConcurrent MarkingНачальная фаза маркировки всегда несетYong GCпровести.

Mixed GCРазделенный на два этапа, первый этап представляет собой одновременную маркировку, а второй этап — скрининг и переработку. Параллельный процесс маркировки выглядит следующим образом:

  • начальная маркировка
  • одновременная маркировка
  • итоговая маркировка (окончательная маркировка, перемаркировка)
  • уборка

Очистка здесь заключается не в очистке объекта (не то же самое, что в CMS).Хотя «Понимание JVM» будет очищать, фильтровать и перерабатывать в процесс, ноGCВ журнале это совершенно отдельные процессы. здесь сGCЖурнал имеет преимущественную силу

После завершения маркировкиG1Затем выберите некоторые области для проверки и переработки. Обратите внимание, что эти этапы могут выполняться отдельно, то есть методы выполнения могут быть следующими:

启动程序
-> young GC
-> young GC
-> young GC
-> young GC + initial marking
(... concurrent marking ...)
-> young GC (... concurrent marking ...)
(... concurrent marking ...)
-> young GC (... concurrent marking ...)
-> final marking
-> cleanup
-> mixed GC
-> mixed GC
-> mixed GC
...
-> mixed GC
-> young GC + initial marking
(... concurrent marking ...)

Далее мы подробно объясним, что делает каждый этап маркировки.

начальная маркировка:МогуStop The World, от маркировки всехGC RootОбъекты, до которых можно добраться непосредственно выездом. Хотя процесс и будет приостановлен, он заимствованYong GC, поэтому нет дополнительной отдельной фазы приостановки.

одновременная маркировка: Параллельная стадия. Начиная с объектов, проверенных на предыдущем этапе, он обходит и ищет один за другим, и каждый объект помечается как живой. Примечание. Этот процесс также сканируетSATB(Параллельный снимок) Записанная ссылка.

Вызов моментального снимка параллелизма: это решение проблемы, связанной с тем, что объекты могут быть неправильно помечены из-за того, что пользователи изменяют ссылочное отношение во время параллельного процесса.CMSЗдесь используется инкрементное обновлениеG1Используется одновременный снимок, который записывает все отношения ссылок в начале параллельной метки.

окончательная маркировка (перемаркировка): МогуSTW, хотя предыдущий параллельный процесс маркировки сканируетSATB, но ведь предыдущий этап — это все-таки параллельный процесс, поэтому после завершения параллельной маркировки все пользовательские потоки нужно снова приостановить и снова разметитьSATB. Этот процесс также обрабатывает слабые ссылки.

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

ноCMSЗавершающий этап маркировки требует повторного сканирования всейYong gen, так что можноCMSизremarkЭтапы будут очень медленными.

очистить: Фаза паузы. Очистите и сбросьте состояние маркера. считать каждыйregionКоличество объектов, помеченных как уцелевшие на данном этапе. Если на данном этапе будет найден регион, в котором нет живых объектов, он целиком будет возвращен в список выделяемых регионов.


После завершения маркировки выполняется очистка (Evacuation), этот этап полностью приостановлен. он отвечает за размещение частиregionСкопируйте живой объект в пустойregionвнутри, а затем утилизируйте оригиналregionпробел, вы можете выбрать столько, сколько хотите на этом этапеregionсформировать набор коллекций (Collection Set), выбрав набор коллекций, вы можетеCollection SetОбъекты копируются параллельно в новыйregionсередина.


ПонялG1Общий процесс переработки, следующее сравнениеCMSмы можем видетьG1Как решить некоторые проблемы в параллельном процессе:

  1. Помните набор: как упоминалось ранее, для ссылок между поколениямиCMSВыбрано не поддерживать набор памяти нового поколения для старого поколения, потому что новое поколение меняется слишком быстро, а стоимость обслуживания относительно велика, иG1Решение, независимо отYong GCвсе ещеMixed GC, будетYong GenПрисоединяйсяCollection SetКороче говоря, либо только новое поколение перерабатывается, либо все новое поколение и старое поколение перерабатываются вместе, что позволяет избежать сохранения памяти старого поколения, установленной новым поколением.

    Здесь обсуждается только обслуживание набора памяти для ссылки старого поколения на старое поколение.Ссылка старого поколения на новое поколение по-прежнему будет поддерживать набор памяти.

  2. Справочные изменения во время параллелизма: здесьRemarkingэтапе мы уже сказали,CMSиспользовать схему инкрементного обновления, аG1используется параллельный снимок (STAB snapshot-at-the-beginning)

  3. Что касается обслуживания наборов памяти и одновременных моментальных снимков,G1Тоже через барьер записи(write barrier) для поддержания.


Журнал утилизации G1

talk is cheap, show me the code

Вышеупомянутый процесс в основном суммируется с помощью "Глубокого понимания JVM" и некоторой информации в Интернете. Так ли это? Им еще нужно управлять. Давайте посмотрим.G1Журнал утилизации:

Yong GC

//GC 原因:分配大对象 GC类型yong gc  此次带有并发标记的initial mark  此次GC花费0.0130262s
[GC pause (G1 Humongous Allocation) (young) (initial-mark), 0.0130262 secs]
   //暂停过程中,并行收集花费4.5ms  使用4个线程同时收集
   [Parallel Time: 4.5 ms, GC Workers: 4]
      //并发工作开始时间戳
      [GC Worker Start (ms): Min: 1046.3, Avg: 1046.3, Max: 1046.4, Diff: 0.1]
      //扫描root集合(线程栈、JNI、全局变量、系统表等等)花费的时间
      [Ext Root Scanning (ms): Min: 0.9, Avg: 1.0, Max: 1.2, Diff: 0.3, Sum: 4.0]
      //更新Remember Set的时间
      //由于Remember Set的维护是通过写屏障结合缓冲区实现的,这里Update RS就是
      //处理完缓冲区里的元素的时间,用来保证当前Remember Set是最新
      [Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
           //Update RS 过程中处理了多少缓冲区
           [Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0]
      //扫描记忆集的时间
      [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
      //扫描代码中的Root(局部变量)节点时间
      [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.1, Sum: 0.1]
      //拷贝(疏散)对象的时间
      [Object Copy (ms): Min: 3.0, Avg: 3.0, Max: 3.1, Diff: 0.0, Sum: 12.1]
      //线程窃取算法,每个线程完成任务后会尝试帮其他线程完成剩余的任务
      [Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
            //线程成功窃取任务的次数
   [Termination Attempts: Min: 1, Avg: 1.3, Max: 2, Diff: 1, Sum: 5]
      //GC 过程中完成其他任务的时间
      [GC Worker Other (ms): Min: 0.1, Avg: 0.2, Max: 0.3, Diff: 0.2, Sum: 0.8]
      //展示每个垃圾收集线程的最小、最大、平均、差值和总共时间。
      [GC Worker Total (ms): Min: 4.2, Avg: 4.3, Max: 4.3, Diff: 0.1, Sum: 17.1]
      //min表示最早的完成任务的线程的时间,max表示最晚接受任务的线程时间
      [GC Worker End (ms): Min: 1050.6, Avg: 1050.6, Max: 1050.6, Diff: 0.0]
   //释放管理并行垃圾收集活动数据结构
   [Code Root Fixup: 0.0 ms]
   //清理其他数据结构
   [Code Root Purge: 0.0 ms]
   //清理card table (Remember Set)
   [Clear CT: 0.8 ms]
   //其他功能
   [Other: 7.8 ms]
      //评估需要收集的区域。YongGC 并不是全部收集,而是根据期望收集
      [Choose CSet: 0.0 ms]
      //处理Java中各种引用soft、weak、final、phantom、JNI等等。
      [Ref Proc: 5.2 ms]
      //遍历所有的引用,将不能回收的放入pending列表
      [Ref Enq: 0.1 ms]
      //在回收过程中被修改的card将会被重置为dirty
      [Redirty Cards: 0.4 ms]
      [Humongous Register: 0.0 ms]
      [Humongous Reclaim: 0.0 ms]
      //将要释放的分区还回到free列表。
      [Free CSet: 0.1 ms]
   //Eden 回收前使用3072K 总共12M ——> 回收后使用0B,总共11M
   //Survivors 回收前使用0B,回收后使用1024K
   //整个堆回收前使用101M 总共256M,回收后使用99M,总共256M
   //可以看到这里新生代没有占满就开始Yong GC,其目的是为了开启Concurrent Marking
   [Eden: 3072.0K(12.0M)->0.0B(11.0M) Survivors: 0.0B->1024.0K Heap: 101.0M(256.0M)->99.0M(256.0M)]
 [Times: user=0.00 sys=0.00, real=0.01 secs] 

Concurrent Marking

Параллельная маркировка обычно происходит вYong GCПозже.Yong GCпосле этогоinitial markохватывать

//扫描GC Roots
[GC concurrent-root-region-scan-start]
//扫描GC Roots完成,花费0.0004341s
[GC concurrent-root-region-scan-end, 0.0004341 secs]
//并发标记阶段开始
[GC concurrent-mark-start]
//并发标记介绍。花费0.0002240s
[GC concurrent-mark-end, 0.0002240 secs]
//重新标记开始,会STW.
//Finalize Marking花费0.0006341s
//处理引用:主要是若引用。花费0.0000478 secs
//卸载类。花费0.0008091 secs
//总共花费0.0020776 secs
[GC remark [Finalize Marking, 0.0006341 secs] [GC ref-proc, 0.0000478 secs] [Unloading, 0.0008091 secs], 0.0020776 secs]
 [Times: user=0.00 sys=0.00, real=0.00 secs]
//清理阶段 会STW
//主要: 标记所有`initial mark`阶段之后分配的对象,标记至少有一个存活对象的Region
//清理没有存活对象的Old Region和Humongous Region
//处理没有任何存活对象的RSet
//对所有Old Region 按照对象的存活率进行排序
//清理Humongous Region前使用了150M,清理后使用了150M ,耗时0.0013110s
[GC cleanup 150M->150M(256M), 0.0013110 secs]
 [Times: user=0.00 sys=0.00, real=0.00 secs] 

Mixed GC

Когда одновременная маркировка завершена, вы можете продолжитьMixed GCсейчас,Mixed GCОсновная работа заключается в переработке выбранных в процессе параллельной маркировки.Region

Mixed GCпроделанная работа иYong GCПроцесс в основном такой же, за исключением того, что переработанный контент основан на параллельной марке.

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

  [GC pause (G1 Evacuation Pause) (mixed), 0.0080519 secs]
   [Parallel Time: 7.6 ms, GC Workers: 4]
      [GC Worker Start (ms): Min: 140411.4, Avg: 140415.1, Max: 140418.9, Diff: 7.4]
      [Ext Root Scanning (ms): Min: 0.0, Avg: 0.2, Max: 0.5, Diff: 0.4, Sum: 1.0]
      [Update RS (ms): Min: 0.0, Avg: 0.1, Max: 0.1, Diff: 0.1, Sum: 0.3]
         [Processed Buffers: Min: 0, Avg: 1.3, Max: 4, Diff: 4, Sum: 5]
      [Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
      [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
      [Object Copy (ms): Min: 0.0, Avg: 2.6, Max: 5.2, Diff: 5.2, Sum: 10.3]
      [Termination (ms): Min: 0.0, Avg: 0.9, Max: 1.8, Diff: 1.8, Sum: 3.4]
         [Termination Attempts: Min: 1, Avg: 1.0, Max: 1, Diff: 0, Sum: 4]
      [GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]
      [GC Worker Total (ms): Min: 0.1, Avg: 3.8, Max: 7.5, Diff: 7.4, Sum: 15.2]
      [GC Worker End (ms): Min: 140418.9, Avg: 140418.9, Max: 140418.9, Diff: 0.0]
   [Code Root Fixup: 0.0 ms]
   [Code Root Purge: 0.0 ms]
   [Clear CT: 0.1 ms]
   [Other: 0.4 ms]
      [Choose CSet: 0.0 ms]
      [Ref Proc: 0.1 ms]
      [Ref Enq: 0.0 ms]
      [Redirty Cards: 0.1 ms]
      [Humongous Register: 0.0 ms]
      [Humongous Reclaim: 0.0 ms]
      [Free CSet: 0.0 ms]
   [Eden: 4096.0K(4096.0K)->0.0B(138.0M) Survivors: 8192.0K->2048.0K Heap: 68.0M(256.0M)->65.5M(256.0M)]
 [Times: user=0.03 sys=0.00, real=0.01 secs] 

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


Full GC

G1 Full GC

//GC类型:Full GC  GC原因:调用System.gc() GC前内存使用298M GC后使用509K 花费时间:0.0101774s 
[Full GC (System.gc()) 298M->509K(512M), 0.0101774 secs]
  //新生代:GC前使用122M GC后使用0B 总容量由154M扩容为230M
  //幸存区:GC前使用4096K GC后使用0B 
  //总内存:GC前使用298M GC后使用509K 总量量512M不变
  //元空间:GC前使用3308K GC后使用3308K 总容量1056768K
  [Eden: 122.0M(154.0M)->0.0B(230.0M) Survivors: 4096.0K->0.0B Heap: 298.8M(512.0M)->509.4K(512.0M)], [Metaspace: 3308K->3308K(1056768K)]

[Times: user=0.01 sys=0.00, real=0.01 secs]

можно посмотреть на этот разGCИспользуемое время составляет 10 мс, но это только тогда, когда вся куча составляет всего 512 МБ, а используется только 300 МБ,G1да нетFull GCмеханизм,G1 GCиспользуетсяSerial Old(позже оптимизирован для многопоточности, но все еще относительно медленный), поэтомуFull GCОн будет приостановлен на долгое время, поэтому в производственной среде обязательно обратите вниманиеFull GC, обычно раз в несколько днейFull GCЭто приемлемо.

G1 Full GCПричины, как правило, следующие:

  • Mixed GCНе могу угнаться за скоростью выделения памяти, толькоFull GCЧтобы освободить память, решение в этом случае будет рассмотрено позже

  • MetaSpaceНедостаточно, для большого количества классов, использующих отражение и динамические прокси, каждый класс динамических прокси будет генерировать новый класс, и в то же времяclassИнформация хранится в метапространстве, поэтому, если метапространства недостаточно,G1будет полагаться наFull GCЧтобы расширить метапространство, решение в этом случае состоит в том, чтобы увеличить начальный размер метапространства.

  • HumongousВыделение не удалось, как упоминалось ранееG1При выделении крупных объектов утилизация производится путемConcurrent MarkingилиFull GC, поэтому, если выделение большого объекта не удается, это может вызватьFull GC

    Конкретные правила здесь не очень ясны, потому что при тестированииHumongousвызваныConcurrent Marking.


Настройка G1

Я так много сказал раньше, просто чтобы понять, когдаGCКак настроить, когда это влияет на онлайн-среду. Поэтому поймитеG1В процессе переработки вы обычно можете понять роль каждого параметра и способы его изменения.

  • -XX:+UseG1GC: использоватьG1переработчик.

  • -XX:MaxGCPauseMillis = 200: Установите максимальную цель паузы. По умолчанию 200,G1Он приложит все усилия для достижения этой цели. Это целевое значение необходимо скорректировать в соответствии с проектом. Если время слишком мало, это может привести к падению пропускной способности.Mixed GCПерерабатывается слишком мало мусора, что приводит к окончательному накоплению мусораFull GC, если время слишком велико, это может ухудшить работу пользователя.

  • -XX:InitiatingHeapOccupancyPercent = 45: указывает на начало параллелизмаGCПорог использования кучи для цикла, когда использование всей кучи достигает порога, запускается параллельный цикл. этот параметр иCMSТо же самое в основном используется для предотвращенияMixed GCПараллелизм в процессе не выполняется.Если параллельный сбор выполняется слишком поздно, оставшейся памяти в параллельном процессе может не хватить для удовлетворения памяти древовидного демона, что приведет кG1Отказаться от одновременной маркировки, перейти наFull GC, Эта ситуация вообще может бытьGCвидеть вto-space exhaustedшрифт.

    Этот параметр нельзя отрегулировать слишком маленьким, слишком маленький приведет к непрерывному циклу, занимающемуCPUресурс.

  • -XX:G1MixedGCCountTarget=8: каждый разMixed GCОбъем старой памяти, подлежащей переработке, по умолчанию равен 8, это тоже для решения проблемыto-space exhaustedпроблема каждый разMixed GCRecycle больше, у старого поколения будет больше свободной памяти, но соответствующее время паузы может увеличиться.

  • -XX:ConcGCThreads: каждый разGCв использованииCPUКоличество, стоимость не фиксирована. Также для решенияto-space exhaustedПроблема использования большего количества потоков, тогдаGCбудет быстрее, за счет пользователяCPUВремя будет занято.

  • -XX:G1ReservePercent=10%: Количество подвесных потолков, функция резервирования10%Пространство не используется и зарезервировано для параллельных циклов, которые могут возникнуть, когдаto-space exhaustedиспользовать, когда есть проблема, чтобы предотвратить возникновениеto-space exhausted, слишком большое резервирование может привести к трате памяти

  • Не устанавливать размер молодого поколения: не использовать-Xmn,так какG1заключается в необходимости увеличить или уменьшить размер молодого поколения, что, если установить, приведет кG1Цели времени паузы не могут быть использованы.


выше, простоG1Общие параметры, которые требуют внимания, конечно, могут быть и другие проблемы, такие как выделение больших объектов, размер метапространства и т. д. В общем, поймитеGCПроцесс утилизации, много практики, в общем можно пройтиGCвойти, чтобы найти проблему.

использованная литература:

Некоторые ключевые технологии Java Hotspot G1 GC -- Техническая команда Meituan

Спросите принцип алгоритма G1

Глубокое понимание журнала сборщика мусора G1 (1)

Garbage First Garbage Collector Tuning

Руководство по оптимизации сборки мусора виртуальной машины HotSpot — G1

— сказал Жирный Мао, резюмируя заметки о чтении классических книг по Java.