
Вместо того, чтобы поддаться искушению настроить случайные части приложения, вам необходимо убедиться, что вы понимаете текущую ситуацию и желаемый результат. В общем, это так же просто, как следующий процесс:
- Укажите ваши цели производительности
- Запустить тесты
- Мера
- Сравните с целями
- Внести изменения и вернуться к выполнению тестов
Важно, чтобы цели могли быть установлены и измерены в трех измерениях, все они имеют отношение к настройке производительности. Эти цели включают в себя задержку, пропускную способность и емкость, понимание которых я могу рекомендовать, чтобы взглянуть на соответствующую главу в Руководстве по сборке мусора .
Давайте посмотрим, как мы можем начать исследовать, как постановка и достижение таких целей выглядит на практике. Для этого давайте рассмотрим пример кода:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
//imports skipped for brevitypublic class Producer implements Runnable { private static ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2); private Deque<byte[]> deque; private int objectSize; private int queueSize; public Producer(int objectSize, int ttl) { this.deque = new ArrayDeque<byte[]>(); this.objectSize = objectSize; this.queueSize = ttl * 1000; } @Override public void run() { for (int i = 0; i < 100; i++) { deque.add(new byte[objectSize]); if (deque.size() > queueSize) { deque.poll(); } } } public static void main(String[] args) throws InterruptedException { executorService.scheduleAtFixedRate(new Producer(200 * 1024 * 1024 / 1000, 5), 0, 100, TimeUnit.MILLISECONDS); executorService.scheduleAtFixedRate(new Producer(50 * 1024 * 1024 / 1000, 120), 0, 100, TimeUnit.MILLISECONDS); TimeUnit.MINUTES.sleep(10); executorService.shutdownNow(); }} |
Код передает две работы для запуска каждые 100 мс. Каждое задание эмулирует объекты с определенным сроком службы: оно создает объекты, позволяет им уходить на заранее определенный промежуток времени, а затем забывает о них, позволяя GC освободить память.
При запуске примера с включенным ведением журнала GC со следующими параметрами
|
1
|
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps |
мы сразу же увидим влияние GC в файлах журналов, как показано ниже:
|
1
2
3
|
2015-06-04T13:34:16.119-0200: 1.723: [GC (Allocation Failure) [PSYoungGen: 114016K->73191K(234496K)] 421540K->421269K(745984K), 0.0858176 secs] [Times: user=0.04 sys=0.06, real=0.09 secs] 2015-06-04T13:34:16.738-0200: 2.342: [GC (Allocation Failure) [PSYoungGen: 234462K->93677K(254976K)] 582540K->593275K(766464K), 0.2357086 secs] [Times: user=0.11 sys=0.14, real=0.24 secs] 2015-06-04T13:34:16.974-0200: 2.578: [Full GC (Ergonomics) [PSYoungGen: 93677K->70109K(254976K)] [ParOldGen: 499597K->511230K(761856K)] 593275K->581339K(1016832K), [Metaspace: 2936K->2936K(1056768K)], 0.0713174 secs] [Times: user=0.21 sys=0.02, real=0.07 secs] |
Основываясь на информации в журнале, мы можем начать улучшать ситуацию с тремя разными целями.
- Убедиться, что пауза GC в худшем случае не превышает заданный порог
- Убедиться, что общее время, в течение которого потоки приложений останавливаются, не превышает заранее установленный порог
- Снижение затрат на инфраструктуру при одновременном обеспечении достижения разумных задержек и / или пропускной способности.
Для этого приведенный выше код был запущен в течение 10 минут в трех разных конфигурациях, в результате чего были получены три совершенно разных результата, обобщенных в следующей таблице:
| отвал | GC Алгоритм | Полезная работа | Самая длинная пауза |
|---|---|---|---|
| -Xmx12g | -XX: + UseConcMarkSweepGC | 89,8% | 560 мс |
| -Xmx12g | -XX: + UseParallelGC | 91,5% | 1 104 мс |
| -Xmx8g | -XX: + UseConcMarkSweepGC | 66,3% | 1610 мс |
В эксперименте использовался один и тот же код с разными алгоритмами GC и разным размером кучи, чтобы измерить продолжительность пауз сборки мусора с учетом задержки и пропускной способности. Детали экспериментов и интерпретация результатов представлены в нашем Справочнике по сборке мусора . Посмотрите в справочнике примеры того, как простые изменения в конфигурации приводят к тому, что пример ведет себя совершенно по-разному в отношении задержки и пропускной способности.
Обратите внимание, что для того, чтобы сделать пример как можно более простым, было изменено только ограниченное количество входных параметров, например, эксперименты не тестируют на другом количестве ядер или с разным расположением кучи.
| Ссылка: | GC-тюнинг на практике от нашего партнера JCG Никиты Сальникова Тарновского в блоге Plumbr Blog . |