Вибір і налаштування Garbage Collector для Highload системи в Hotspot JVM



Введення
При роботі в сфері RTB (Real Time Bidding) однією з ключових характеристик є час, витрачений на показ реклами користувачеві, зайшов на сайт. Воно складається з декількох етапів, один з яких – аукціон за рекламне місце, проводиться SSP (Supply Side Platform) між кількома DSP (Demand Side Platform) системами. У цьому випадку критичною величиною є час, за який DSP встигне відповісти своїм інвентарем і грошовою ставкою за цей показ. Як правило, верхня межа цього часу становить приблизно 100 мілісекунд. З урахуванням того, що для оптимальної продуктивності рекламних кампаній потрібно десятки тисяч запитів в секунду, виконання цієї вимоги може стати вельми нетривіальним завданням.

Наш Ad Server, який відповідає за основну роботу GetIntent DSP розроблений на мові Java і працює на стандартній Hotspot JVM, що має загальновідомі механізми сміття (GC). Тому найбільш оптимальний варіант лежить в аналізі того, як саме відбувається робота з пам'яттю, і як наслідок вибір найбільш підходящого алгоритму складання сміття і його оптимальна настройка. Про це і піде мова в даній статті.

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

Як ми тестували
Для тестування використовувався 2 робочих станції. На першій JVM запускалася з:
-Xmx4500m

На другий:
-Xmx12g

Порівнювалися збирачі сміття CMS (Concurrent Mark Sweep) і G1 (Garbage First)
Тестування проводилося протягом 16 годин на навантаженні повністю відповідної бойової.

CMS (Concurrent Mark Sweep)
CMS дозволяє значно скорочувати затримки, пов'язані зі збиранням сміття. Однак при його використанні неминуче доводиться стикатися з двома основними проблемами, які і створюють необхідність додаткового налаштування:
  • Фрагментація пам'яті
  • Високий allocation rate
Позитивно вплинути на перший параметр можна шляхом контролювання promotion rate показника. Для цього необхідно визначити, який обсяг об'єктів потрапляє в Штатну, а який «вмирає молодим» в Eden області.

Тестування проводилося з наступними параметрами:
-XX:+UseConcMarkSweepGC
-XX:NewRatio=1, 3, 5

для логування використовувалися:
-XX:+PrintGCDetails -XX:+PrintGC -XX:+PrintGCTimeStamps -XX:+PrintTenuringDistribution -XX:+PrintGCDateStamps -XX:+PrintCMSInitiationStatistics -XX:PrintCMSStatistics=1


G1 (Garbage First)
G1 GC виглядає привабливим вибором для RTB bidder-а, так як його основна мета – витримувати стабільні і передбачувані Stop The World (STW) паузи. Це також обумовлює простоту і наочність його налаштування. Фактично треба оперувати тільки одним параметром – максимально допустимої тривалістю STW паузи: -XX:MaxGCPauseMillis
У нашому випадку заради виключення випадкових довгих пауз можна пожертвувати невеликою часткою throughput.

Щодо G1 GC, з моменту його появи в якості доступного для експериментів збирача сміття, сформувалися певні упередження, головне з яких є те, що MaxGCPauseMillis не витримується. Також є озвучена Oracle рекомендація використовувати його на досить великих розмірах heap ( >= 6 Gb).
Наскільки все це актуально ми дізнаємося після нашого тестування. Також приділимо трохи часу такою ексклюзивною для G1 GC функції як String Deduplication.

Тестування проводилося з наступними параметрами:
-XX:+UseG1GC
-XX:MaxGCPauseMillis=100, 60, 40

Додатково були проведені тести з параметром:
-XX:MaxTenuringThreshold=8

для логування використовувалися:
-XX:+PrintGCDetails -XX:+PrintGC -XX:+PrintGCTimeStamps -XX:+PrintTenuringDistribution -XX:+PrintGCDateStamps -XX:+PrintAdaptiveSizePolicy -XX:+PrintReferenceGC


Max Heap Size 4.5 Gb
Зведена таблиця розподілу Stop The World пауз:

Однозначним переможцем у даній конфігурації виходить CMS з прапором
-XX:NewRatio=5

Як можна помітити, незважаючи на те, що показник ms/sec паузи у даній конфігурації трохи гірше ніж у інших, вона все одно показує себе як найбільш стабільна – ~12 ms середня пауза і практично 98% вкладається в норму – хороший для нас результат. При таких показниках на один Full GC протягом 16 годин можна закрити очі.

Графік розподілу latency для кращих показників G1 і CMS:

Аналіз результатів CMS
Ми проекспериментували з наборами параметрів, у яких розмір Eden (-XX:NewRatio) був 1/2, 1/4, 1/6 від загального розміру пам'яті. Середній promotion rate для цих конфігурацій розподілився відповідним чином: 1.7, 2.75 і 2.79 mb/sec, що цілком логічно – чим менше розмір Eden, тим більше сміття встигає просочитися безпосередньо в Old Gen. Як можна помітити, з певного моменту, розмір Eden області починає слабо впливати на цей показник. В нашому випадку ми можемо пожертвувати більш високим promotion rate (як наслідок більш часті OldGen складання і велика ймовірність фрагментації) заради мінімально можливою середньою затримкою протягом роботи програми.

Аналіз результатів G1
Видно, що G1 тісно в такому маленькому heap. Mixed паузи дуже часті,
-XX:MaxGCPauseMillis надає маленьке вплив на кінцевий результат, а конфігурація з бажаної паузою 40ms не змогла обійтися без Full GC.
Однак є ще один момент, який нас збентежив. За замовчуванням G1 вибирає 15 ages для Survivor області. Ми вирішили подивитися, чи дійсно нам потрібно стільки:

Очевидно дивний знак. Починаючи приблизно з age 8 розмір завжди залишається приблизно на одному рівні; це говорить про те, що це довгоживучі об'єкти, які швидше за все в будь-якому випадку потраплять в Штатну область, а до цього при кожній мінорній збірці ми просто переливаємо з пустого в порожнє, тоді як могли б відразу помістити все це в OldGen. Хороше рішення – поставити в значення MaxTenuringThreshold=8.
Однак, у випадку з heap 4.5 Gb ми не помітили великої різниці в результатах, тому для стислості опустимо їх. Подивимося чи зміниться щось на великому heap.

Max Heap Size 12Gb
Зведена таблиця розподілу Stop-the-World пауз:

Склад представників G1 трохи змінився, т. к. параметр MaxTenuringThreshold=8 (в таблиці mtt=8) у даній конфігурації почав приносити помітний результат.
На великому heap G1 розправив крила і вийшов вперед як за загальним розподілом пауз, так і з дуже короткою максимальної паузі. При цьому середній час витрачається на GC склало менше 7ms кожну секунду, тобто менше 0.7%

Графік розподілу latency для кращих показників G1 і CMS:

Аналіз результатів CMS
Вважається, що основна проблема CMS це питання масштабованості. Наше тестування це підтверджують. Практично всі показники гірші, ніж при використанні маленького розміру heap. З плюсів можна відзначити, що завдяки більшому об'єму пам'яті, впливу фрагментації тут помітно нижче – жодного Full GC за весь час експерименту.

Аналіз результатів G1
Результат явно показує, що G1 дійсно набагато стабільніше на великих обсягах пам'яті; досить чітко виконуються умови, встановлені в налаштуваннях. Тут безперечний переможець з 40 ms latency. Середня пауза зросла всього на 3 ms, коли розмір пам'яті виріс майже в 2.4 рази! Що вже говорити про показник ms/sec – в два рази краще.

G1 String Deduplication
Оскільки наш біддер працює з текстовим OpenRTB протоколом, пише безліч строкових логів, зберігає рядкові кеші, і т. д., то цілком логічно очікувати великий ефект від цієї нової функції. У теорії кількість збірок сміття має скоротитися при тому, що середній час складання збільшиться. Ми додали цей прапор для конфігурації з MaxGCPauseMillis = 100ms і Xmx=4500m:

Хоч середня пауза і знаходиться в зазначених межах, кількість пауз перевищують 1000ms перевищила допустимі межі. Це видно на графіку:

Спроби встановити меншу тривалість паузи призводили до дуже сильного зростання споживання CPU. Від використання цього параметра було вирішено відмовитися.

Підсумки
Ми провели детальний аналіз CMS і G1 збирачів сміття, основною метою якого було розуміння того, як сильно ми можемо знизити вплив GC на latency – найбільш критичний показник для нашої системи.

Цілком очікуваний результат – однозначних висновків тут немає. Для VM з розміром пам'яті 5Gb, вийшов переможцем CMS з конфігурацією -XX:NewRatio=5; незважаючи на велику максимальну паузу, протягом життя додатки він показував більш стабільний результат, кращий percentile і середню затримку. Однак на VM з розміром heap 12Gb G1 з великою перевагою випередив CMS, що виправдовує рекомендації Oracle; ms/sec затримка краще в 1.94 рази, max пауза у 13.3 рази!

Завдяки цим дослідженням ми могли більше не працювати наосліп, керуючись лише окремими рекомендаціями і різнорідними думками; навпаки, ми змогли знайти ідеальний баланс для нашої неоднорідною в плані конфігурації системи, отримуючи максимальну стабільність і як наслідок прибуток з того, що ми маємо сьогодні.

Автори статті — absorbb і dmart28
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.