Что заставляет сборщик мусора G1 в Java 7 прерывать фазу одновременной маркировки?

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

Цикл от одного начала сканирования области до другого показан ниже. По адресу 61807.406 регистрируется полный GC, за которым следует запись для concurrent-mark-abort. Что я хочу знать, так это то, почему сборщик мусора посчитал необходимым выполнить полную сборку мусора, останавливающую мир, и как я могу этого избежать.

Обратите внимание, что этот вопрос уже задавался на Список рассылки OpenJDK без ответов.

Я обрезал детали молодых GC для краткости, но я могу опубликовать полный фрагмент где-нибудь, если это необходимо.

61805.878: [GC concurrent-root-region-scan-start]
61805.882: [GC concurrent-root-region-scan-end, 0.0033586]
61805.882: [GC concurrent-mark-start]
61806.133: [GC pause (young), 0.02836202 secs]
   [Eden: 498M(498M)->0B(478M) Survivors: 14M->34M Heap: 3025M(4096M)->2548M(4096M)]
 [Times: user=0.19 sys=0.00, real=0.03 secs] 
61806.426: [GC pause (young), 0.02766222 secs]
   [Eden: 478M(478M)->0B(480M) Survivors: 34M->32M Heap: 3050M(4096M)->2576M(4096M)]
 [Times: user=0.19 sys=0.00, real=0.03 secs] 
61806.717: [GC pause (young), 0.02214895 secs]
   [Eden: 480M(480M)->0B(502M) Survivors: 32M->10M Heap: 3056M(4096M)->2571M(4096M)]
 [Times: user=0.09 sys=0.00, real=0.02 secs] 
61807.000: [GC pause (young), 0.01899188 secs]
   [Eden: 502M(502M)->0B(502M) Survivors: 10M->10M Heap: 3074M(4096M)->2573M(4096M)]
 [Times: user=0.09 sys=0.00, real=0.02 secs] 
61807.201: [GC pause (young), 0.02619259 secs]
   [Eden: 162M(502M)->0B(500M) Survivors: 10M->12M Heap: 3036M(4096M)->2876M(4096M)]
 [Times: user=0.11 sys=0.00, real=0.03 secs] 
61807.283: [GC pause (young), 0.02068515 secs]
   [Eden: 102M(500M)->0B(500M) Survivors: 12M->12M Heap: 3058M(4096M)->2957M(4096M)]
 [Times: user=0.09 sys=0.00, real=0.02 secs] 
61807.350: [GC pause (young), 0.01606520 secs]
   [Eden: 52M(500M)->0B(498M) Survivors: 12M->14M Heap: 3020M(4096M)->2969M(4096M)]
 [Times: user=0.11 sys=0.00, real=0.02 secs] 
61807.389: [GC pause (young), 0.01573865 secs]
   [Eden: 42M(498M)->0B(500M) Survivors: 14M->12M Heap: 3021M(4096M)->2978M(4096M)]
 [Times: user=0.09 sys=0.00, real=0.02 secs] 
61807.406: [Full GC 2978M->2498M(4096M), 4.8896638 secs]
 [Times: user=6.37 sys=0.08, real=4.89 secs] 
61812.296: [GC concurrent-mark-abort]
61812.542: [GC pause (young), 0.01526403 secs]
   [Eden: 512M(500M)->0B(510M) Survivors: 0B->2048K Heap: 3018M(4096M)->2506M(4096M)]
 [Times: user=0.09 sys=0.00, real=0.02 secs] 
61812.793: [GC pause (young) (initial-mark), 0.01391544 secs]
   [Eden: 510M(510M)->0B(508M) Survivors: 2048K->4096K Heap: 3016M(4096M)->2508M(4096M)]
 [Times: user=0.09 sys=0.00, real=0.01 secs] 
61812.807: [GC concurrent-root-region-scan-start]

Здесь используется версия Java Hotspot 1.7.0_7 со следующими интересными настройками:

-XX:PermSize=128m
-XX:MaxPermSize=128m
-XX:NewSize=512m
-XX:MaxNewSize=512m
-Xms4096m
-Xmx4096m
-XX:+UnlockDiagnosticVMOptions
-XX:+UnsyncloadClass
-XX:+UseTLAB
-XX:+UseG1GC
-XX:SurvivorRatio=10
-Xloggc:./workspace/gc.log
-verbose:gc
-XX:+PrintGC
-XX:+PrintGCTimeStamps
-XX:+PrintGCDetails

person sharakan    schedule 29.10.2012    source источник


Ответы (1)


Я полагаю, вы знаете об этой ссылке. Эта страница также полезна.

Полный GC происходит, когда постоянные объекты — те, которые пережили сбор в эфемерном (молодом) поколении — заполняют пространство, выделенное для них. Когда происходит полный сборщик мусора, любая эфемерная маркировка, которая выполнялась, должна быть прервана.

Уменьшение скорости, с которой заполняется стационарное поколение, требует либо добавления большего количества кучи/ОЗУ, либо возни с разделением доступной памяти между стационарными и молодыми пространствами. Параметры NewSize, MaxNewSize и NewRatio предназначены для последнего. Эксперимент — единственный способ найти то, что будет работать.

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

Кажется, это не ваш случай: 2978M->2498M. Единственный выход может состоять в том, чтобы увеличить кучу, покупая больше памяти по мере необходимости. Тем не менее, почти все системы, которые работают долгое время, время от времени будут иметь полную коллекцию.

person Gene    schedule 01.11.2012
comment
Пара вопросов: «Полный GC происходит, когда постоянные объекты ... заполняют пространство, выделенное для них». Мое постоянное пространство должно быть 3,5 г (куча 4 г - 512 м MaxNewSize). Из молодых коллекций непосредственно перед полной сборкой мусора, похоже, что объем данных в постоянной памяти составлял 3 г. 3.5 › 3, так что думаю не полный и не GC. Я неправильно это истолковываю? - person sharakan; 01.11.2012
comment
«Тем не менее, почти все системы, которые работают долгое время, время от времени будут иметь полную коллекцию». Я знаю, что это верно для CMS в Java6, хотя, на мой взгляд, в основном из-за фрагментации. Я думал, что с G1 это будет только в том случае, если маркирующие нити просто не справятся с производством мусора, которого при правильной настройке можно было бы избежать. По какой причине вы думаете, что с G1 вам придется время от времени запускать полный сборщик мусора? - person sharakan; 01.11.2012
comment
G1 использует много регионов. Полные коллекции возникают при сбое продвижения: для уплотнения нет пустой области. Чем дольше работает система, тем больше шансов, что в каждом регионе будет хотя бы один живой объект. - person Gene; 03.11.2012
comment
К сожалению, ничего авторитетного. Есть свободное место для раскрутки настраиваемого папаметра. Когда регионы › 50% заполнены больше, чем допустимо, GC больше ничего не может сделать! Люди, ссылающиеся на такую ​​фрагментацию между регионами, включают в себя: a> и comments.gmane.org/gmane.comp.db.cassandra .пользователь/28034. Не могу найти другие, которые я видел. См. также openjdk.java.net/jeps/156 о выгрузке классов и quora.com / для проблемы, похожей на вашу. - person Gene; 08.11.2012
comment
К вашему сведению, одни и те же два человека являются основными авторами вашей первой, второй и четвертой ссылок. (Второй и четвертый — это посты, сделанные независимо друг от друга, а первый — это разговор между ними.) Это не обесценивает достоверность их опыта, просто хочу сказать, что размер вашей выборки немного меньше, чем кажется на первый взгляд. - person Tim; 28.10.2014
comment
Вы, ребята, понимаете, что когда вы указываете параметры GC для молодого поколения, такие как NewRatio, вы отключаете адаптивную эвристику в G1, верно? С G1 GC вы должны начать со значений по умолчанию, а затем иметь очень вескую причину для их изменения. См. infoq.com/presentations/. - person steinybot; 03.06.2015