1
2
Об одной неприятной
проблеме сборщика
Concurrent Mark-Sweep
3
Обо мне
• Java-разработчик сервисов
Яндекс.Метрика (большой метрики,
метрики приложений, внутренних
сервисов метрики)
• ...
4
Содержание
• Постановка задачи
• Какие сборщики мусора мы бы могли выбрать
• Почему мы выбрали Concurrent Mark-Sweep и
к...
5
Постановка задачи
• Есть N машин, на которых развёрнуто Java-приложение
• Приложение имеет внутренний сервис (джоб) – со...
6
Куда и что смотреть?
• Смотреть в логи машины, с которой произошло
переключение
• А там что-то типа этого:
2014.01.24 17...
7
Какой у нас Java Heap Space?
• Находим наш процесс через что-то типа
ps –ef | grep java
jps -v
и смотрим на параметры -X...
8
Java Heap Space
9
Какие у нас алгоритмы GC?
• Настройки GC:
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
10
Расширенные настройки
• -Xloggc:gc.log
• -XX:+PrintGCDetails
• -XX:+PrintGCDateStamps
• -XX:-PrintGCTimeStamps
11
PrintGCDateStamps
• Почему надо -XX:+PrintGCDateStamps:
2014-01-26T20:00:53.282+0400: [GC [ParNew: 307377K->479K(345024...
12
PrintGCTimeStamps
• Почему надо -XX:-PrintGCTimeStamps:
2014-07-20T09:54:31.478-0700: 208.268: [GC (Allocation Failure)
13
concurrent mode failure
• 2014-01-28T08:30:09.350+0400: [GC [ParNew (promotion failed): 471872K-
>471872K(471872K), 1.8...
14
Что такое concurrent mode failure
• CMS (в основном) работает параллельно с тредами приложения
• Из-за этого может быть...
15
Почему бы не использовать другие
сборщики?
• Serial Collector
• Parallel Collector
• Garbage-First (G1) Garbage Collect...
16
Serial collector
• STW collector… и слишком долго работает…
17
G1 GC
• Неплохая замена CMS, но полная поддержка
появилась только в Oracle HotSpot 7 update 4
18
Parallel collector
• STW collector
• Есть редкие ошибки в его работе:
4071460.500: [Full GC#
# A fatal error has been d...
19
Как работает CMS
- Initial mark – STW
- Mark
- Preclean
- Remark – STW
- Sweep
- Reset
20
CMS: Initial Mark
• CMS останавливает все треды и размечает объекты из
тредов приложения, объекты в статических полях и...
21
CMS: Mark
• CMS начинает искать достижимые объекты из
«корневых» объектов, найденных на предыдущей
фазе.
• Работает одн...
22
CMS: Preclean
• Начинает поиск живых объектов из всех dirty cards
• Работает одновременно с приложением (остановки
тред...
23
CMS: Remark (rescan)
• STW-стадия после сэмплинга нового поколения
Remark (rescan)13003.752: [GC[YG occupancy: 677602 K...
24
CMS: Sweep
• CMS добавляет свободные области во freelists
• Работает одновременно с приложением (остановки
тредов нет)
...
25
CMS: Reset
• CMS освобождает все свои внутренние объекты и
структуры для дальнейшего использования
13014.364: [CMS-conc...
26
Ещё одна опция GC
• Добавим ещё одну опцию логирования GC:
-XX:+PrintTenuringDistribution
27
PrintTenuringDistribution
• Нормальная строка лога GC после сборки в новом
поколении выглядит примерно так:
146715.578:...
28
PrintTenuringDistribution
• А вот строка лога GC после сборки в новом поколении
и флаге -XX+PrintTenuringDistribution:
...
29
2 последовательных вызова GC
• 2014-01-28T14:42:44.102+0400: [GC [ParNew
Desired survivor size 26836992 bytes, new thre...
30
Как уменьшить объёмы миграций из одного
поколения в другое
• Увеличить размер нового поколения
• Увеличить возраст объе...
31
Параметры JVM для нового поколения
• -XX:NewSize=3g
• -XX:InitialTenuringThreshold=15
• -XX:MaxTenuringThreshold=15
• -...
32
Результат
• Объёмы миграций стали меньше
• Concurrent mode failure стал реже (а именно – раз в
час-полтора)
33
Что-то делаем с CMS ?
• CMS долго не мог понять, когда ему стартовать…
12993.795: [GC [1 CMS-initial-mark: 8498366K(111...
34
Параметры JVM для старого поколения
• -XX:+UseCMSInitiatingOccupancyOnly
• -XX:CMSInitiatingOccupancyFraction=50 – как ...
35
Окончательный список параметров
JVM для GC
• -Xms14G
• -Xmx14G
• -XX:+UseParNewGC
• -XX:+UseConcMarkSweepGC
• -XX:+UseC...
36
Результат
• Concurrent mode failure стал редкостью (раз в день)
• Для нашего приложения это нормально
37
Один день – это часто
• Возможно, для кого-то один день – это часто. Какие
ещё workarounds возможны?
38
Переход на JDK 8
• Переходим на JDK 8 и используем G1 GC
• Хороший повод выучить lambda expressions,
java.util.stream p...
39
Extra-tuning CMS
• Тюнинг, связанный с уменьшением фрагментации
tenured generation
• Смотреть YoungPLABSize, OldPLABSiz...
40
Управляем Full GC
• А почему бы не сделать так, чтобы полная сборка (Full GC)
вызывалась в определённое время, когда на...
41
Управляем Full GC
• Проблема: все сервера будут недоступны в одно и то же время
• Сразу вопрос: System.gc() ведь всегда...
42
Немного в защиту System.gc()
• “If you observe an explicit full garbage collection in the garbage
collection logs, dete...
43
Ещё немного в защиту System.gc()
• “Be careful when disabling explicit garbage collection. Doing so may
have a nontrivi...
44
Управляем Full GC
• Можно сделать так, что джоб (с System.gc() внутри) исполнялся
бы с псевдослучайным интервалом
• Каж...
45
Формализация
• Пусть 𝑙 – общий интервал (например, 86400 сек.), 𝑠 –
длительность Full GC (в секундах), 𝑁 – количество м...
46
Решение
• Вероятность того, что запуск Full GC на первой машине произойдёт в
момент 𝑡 ∈ 𝑠, 𝑙 − 𝑠 , а у остальных – в мо...
47
Ещё бы что-то придумать…
• Разбиваете интервал (один день) на несколько интервалов, равное
количеству машин
• Для каждо...
48
Литература
• Java performance / Charlie Hunt, Binu John
“This book is the definitive masterclass in performance tuning ...
Upcoming SlideShare
Loading in …5
×

Об одной неприятной проблеме сборщика Concurrent Mark-Sweep, Кирилл Голоднов

1,413 views

Published on

В Java важно правильно настраивать сборку мусора (Garbage Collection), поскольку она может оказывать огромное влияние на производительность Java-приложения. Однако обилие алгоритмов и настроек GC может сбить с толку. В своём докладе я расскажу о проблеме долгих и частых остановок Java-приложения, которые сборщик Concurrent Mark-Sweep вызывает при неправильных настройках, а также о том, как эту проблему удалось устранить.

Published in: Technology
  • @Kirill Golodnov

    >Так и не понял, какое отношение имеет твой факт 'на дворе давным-давно'

    Перейти с 1.7u04 на 1.7u40 или 70 гораздо проще, чем на 1.8.
    Для меня странно, что на слайдах вообще не сказано про 'современный _на момент написания_ слайдов' мир.

    >В чём противоречие?

    Сначала ты говоришь 'найдём вероятность, что все сервера ушли в GC'. Потом ты говоришь, что для этого нужно чтобы моменты запуска были такими-то.
    Тут противоречие. При твоём выборе моментов начала GC будут попадаться и такие случаи, когда в GC ушли только 2 сервера из трёх.

    > вроде нет проблем
    Проведи следственный эксперимент.
    Я, например, провёл, и по-прежнему считаю, что для N>2 формула неверная.

    В случае N=3, l=86400, s=90 у тебя нижняя оценка это 4.3e-06, а в реальности вероятность оказывается 3.11e-06

    https://gist.github.com/vlsi/20d808ace8116d7b76da
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • @Vladimir Sitnikov
    Привет.
    Очень интересно увидеть здесь комментарий к моему докладу от бывшего соработника.

    >> На слайде 17 'только в 1.7u4'. На дворе давным давно 1.7u70+

    На дворе уже давным-давно 1.8u20, и совсем недавно - 1.8u25.
    Также на дворе уже давно EJB 3.2, Spring 4.x и ещё много чего интересного.
    Так и не понял, какое отношение имеет твой факт 'на дворе давным-давно' (ещё и про jdk 1.7) к тому, что я написал на слайде, и тем более - к тому, что я говорил, показывая этот слайд?

    >> Пример: пусть на втором узле FGC запустилось в момент t-0.9s, а на третьем в момент t+0.9s.
    Тогда в каждый момент времени есть хотя бы 1 узел, где _нет_ full gc: до момента t+0.9s это третий узел, а начиная с момента t+0.1s -- второй.

    Вероятность этого события (когда на 3-ей машине GC сработал через 1.8s и после GC на 2-ой машине) равна 0.5 * (L - 1.8s)^2 / L^2, то есть вероятность того, что на всех Full GC - должна быть меньше, чем 0.5 * (L^2 + 3.6*L*s - 3.24*s^2) / L^2.
    В чём противоречие? В том, что левая граница в неравенство меньше этой величины - вроде нет проблем при N >= 2..

    >> * 2s (количество вариантов случиться второму gc, чтобы время наслоилось)
    А при старте кластера там тоже наслоение 2s, особенно при первых s секундах?
    Или, может, мною заведомо решалась задача для временного промежутка длиной в L со старта кластера?

    Да, я действительно ошибся, подставив вместо s = 90 - случайно s = 180. При s = 90 действительно получается 0.002078.
    Все дальнейшие ответы и предложения - в скайп.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • На слайде 17 'только в 1.7u4'. На дворе давным давно 1.7u70+

    На слайде 46 'решение' неверное.
    Пример: пусть на втором узле FGC запустилось в момент t-0.9s, а на третьем в момент t+0.9s.
    Тогда в каждый момент времени есть хотя бы 1 узел, где _нет_ full gc: до момента t+0.9s это третий узел, а начиная с момента t+0.1s -- второй.

    Решим в частном виде для N=2, l=86400, s=90.
    Общее количество вариантов full gc (если оно только раз в сутки): L*L
    Количество ситуаций (рассматриваем дискретность до секунды для упрощения), когда full gc совпали: L (количество случаев когда full gc случается на 1-ом узле) * 2s (количество вариантов случиться второму gc, чтобы время наслоилось)
    p = (L*2s)/(L*L) == 2s/L == 0.002.

    Откуда цифра 0.004?
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Об одной неприятной проблеме сборщика Concurrent Mark-Sweep, Кирилл Голоднов

  1. 1. 1
  2. 2. 2 Об одной неприятной проблеме сборщика Concurrent Mark-Sweep
  3. 3. 3 Обо мне • Java-разработчик сервисов Яндекс.Метрика (большой метрики, метрики приложений, внутренних сервисов метрики) • Увлекаюсь математикой и прикладными науками, связанными с ней: машинное обучение, информационный поиск, криптография
  4. 4. 4 Содержание • Постановка задачи • Какие сборщики мусора мы бы могли выбрать • Почему мы выбрали Concurrent Mark-Sweep и как он работает • Как мы тюнили сборки в новом и старом поколениях • Что ещё можно было бы придумать
  5. 5. 5 Постановка задачи • Есть N машин, на которых развёрнуто Java-приложение • Приложение имеет внутренний сервис (джоб) – сотни тредов, high concurrency, большие объёмы обрабатываемых данных • В каждый момент времени должно работать не более одного джоба, поэтому N машин соединены координатором Apache Curator Framework (АПИ над Apache ZooKeeper) • Проблема: координатор очень часто переключает выполнение джоба с машины на машину, джоб едва ли успевает полностью отработать на какой-то из машин
  6. 6. 6 Куда и что смотреть? • Смотреть в логи машины, с которой произошло переключение • А там что-то типа этого: 2014.01.24 17:44:37 [DNS Resolver reader] WARN ..monitoringd.process.MonitoringSimple - addEntry tried to lock checkStateLock, but failed 2014.01.24 17:46:02 [spring-scheduler-3-SendThread(mtmon2.yandex.ru:2181)] INFO org.apache.zookeeper.ClientCnxn - Client session timed out, have not heard from server in 87741ms for sessionid 0x347200ebf2ac586, closing socket connection and attempting reconnect • И такое встречается в логе каждые 10 минут…
  7. 7. 7 Какой у нас Java Heap Space? • Находим наш процесс через что-то типа ps –ef | grep java jps -v и смотрим на параметры -Xms и -Xmx • Если этих параметров нет, то используем: jconsole, или kill -3 <pid> • У нас хип на 14 гб: -Xms1G -Xmx14G
  8. 8. 8 Java Heap Space
  9. 9. 9 Какие у нас алгоритмы GC? • Настройки GC: -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
  10. 10. 10 Расширенные настройки • -Xloggc:gc.log • -XX:+PrintGCDetails • -XX:+PrintGCDateStamps • -XX:-PrintGCTimeStamps
  11. 11. 11 PrintGCDateStamps • Почему надо -XX:+PrintGCDateStamps: 2014-01-26T20:00:53.282+0400: [GC [ParNew: 307377K->479K(345024K), 0.0956740 secs] 6653986K->6609342K(12544576K) icms_dc=72 , 0.0958890 secs] [Times: user=1.51 sys=0.01, real=0.09 secs]
  12. 12. 12 PrintGCTimeStamps • Почему надо -XX:-PrintGCTimeStamps: 2014-07-20T09:54:31.478-0700: 208.268: [GC (Allocation Failure)
  13. 13. 13 concurrent mode failure • 2014-01-28T08:30:09.350+0400: [GC [ParNew (promotion failed): 471872K- >471872K(471872K), 1.8524430 secs][CMS2014-01-28T08:30:17.973+0400: [CMS-concurrent-sweep: 11.525/15.409 secs] [Times: user=40.39 sys=0.29, real=15.41 secs] • (concurrent mode failure): 10825312K->6356437K(12058624K), 47.1781340 secs] 11092737K->6356437K(12530496K), [CMS Perm : 44494K- >44405K(74252K)], 49.0308390 secs] [Times: user=62.06 sys=0.09, real=49.03 secs]
  14. 14. 14 Что такое concurrent mode failure • CMS (в основном) работает параллельно с тредами приложения • Из-за этого может быть 2 ситуации: - Tenured generation наполняется быстрее, чем CMS освобождает там память. В итоге, рано или поздно, из young generation надо перенести больший объём (в байтах), чем свободно в tenured generation - В tenured generation есть необходимое свободное место, но оно сильно фрагментировано • Обе ситуации генерируют особое исключение CMS – concurrent mode failure. По сути, это однопоточный(!!!) Full GC.
  15. 15. 15 Почему бы не использовать другие сборщики? • Serial Collector • Parallel Collector • Garbage-First (G1) Garbage Collector
  16. 16. 16 Serial collector • STW collector… и слишком долго работает…
  17. 17. 17 G1 GC • Неплохая замена CMS, но полная поддержка появилась только в Oracle HotSpot 7 update 4
  18. 18. 18 Parallel collector • STW collector • Есть редкие ошибки в его работе: 4071460.500: [Full GC# # A fatal error has been detected by the Java Runtime Environment: # # SIGSEGV (0xb) at pc=0x00007fdbfec78177, pid=20405, tid=140581109962496 # # JRE version: 6.0_45-b06 # Java VM: Java HotSpot(TM) 64-Bit Server VM (20.45-b01 mixed mode linux-amd64 compressed oops) # Problematic frame: # V [libjvm.so+0x759177] PSMarkSweepDecorator::precompact()+0x307 # # An error report file with more information is saved as: # /tmp/hs_err_pid20405.log
  19. 19. 19 Как работает CMS - Initial mark – STW - Mark - Preclean - Remark – STW - Sweep - Reset
  20. 20. 20 CMS: Initial Mark • CMS останавливает все треды и размечает объекты из тредов приложения, объекты в статических полях и т.д. • Остановка короткая 12993.795: [GC [1 CMS-initial-mark: 8498366K(11184832K)] 8647061K(12443136K), 0.1610800 secs] [Times: user=0.16 sys=0.00, real=0.16 secs]
  21. 21. 21 CMS: Mark • CMS начинает искать достижимые объекты из «корневых» объектов, найденных на предыдущей фазе. • Работает одновременно с приложением (остановки тредов нет) 12993.957: [CMS-concurrent-mark-start] 12997.311: [CMS-concurrent-mark: 3.352/3.354 secs] [Times: user=19.88 sys=0.20, real=3.36 secs]
  22. 22. 22 CMS: Preclean • Начинает поиск живых объектов из всех dirty cards • Работает одновременно с приложением (остановки тредов нет) 12997.311: [CMS-concurrent-preclean-start] 12997.617: [CMS-concurrent-preclean: 0.298/0.306 secs] [Times: user=0.64 sys=0.01, real=0.30 secs] 12997.617: [CMS-concurrent-abortable-preclean-start] 12997.747: [GC 12997.747: [ParNew: 1252211K->139776K(1258304K), 0.5655490 secs] 9750577K->9040052K(12443136K), 0.5657350 secs] [Times: user=7.90 sys=0.02, real=0.56 secs] CMS: abort preclean due to time 13003.750: [CMS-concurrent-abortable- preclean: 5.549/6.133 secs] [Times: user=17.06 sys=0.17, real=6.14 secs]
  23. 23. 23 CMS: Remark (rescan) • STW-стадия после сэмплинга нового поколения Remark (rescan)13003.752: [GC[YG occupancy: 677602 K (1258304 K)]13003.752: [Rescan (parallel) , 0.3685750 secs]13004.649: [weak refs processing, 0.0001350 secs] [1 CMS-remark: 8900276K(11184832K)] 9577878K(12443136K), 0.8974050 secs] [Times: user=5.93 sys=0.04, real=0.90 secs]
  24. 24. 24 CMS: Sweep • CMS добавляет свободные области во freelists • Работает одновременно с приложением (остановки тредов нет) 13004.650: [CMS-concurrent-sweep-start] 13014.364: [CMS-concurrent-sweep: 9.714/9.714 secs] [Times: user=10.08 sys=0.30, real=9.71 secs]
  25. 25. 25 CMS: Reset • CMS освобождает все свои внутренние объекты и структуры для дальнейшего использования 13014.364: [CMS-concurrent-reset-start] 13014.391: [CMS-concurrent-reset: 0.028/0.028 secs] [Times: user=0.04 sys=0.00, real=0.03 secs]
  26. 26. 26 Ещё одна опция GC • Добавим ещё одну опцию логирования GC: -XX:+PrintTenuringDistribution
  27. 27. 27 PrintTenuringDistribution • Нормальная строка лога GC после сборки в новом поколении выглядит примерно так: 146715.578: [GC 146715.578: [ParNew: 1258304K->138199K(1258304K), 0.1885550 secs] 9504038K->8525401K(12443136K), 0.1887600 secs] [Times: user=2.48 sys=0.01, real=0.19 secs]
  28. 28. 28 PrintTenuringDistribution • А вот строка лога GC после сборки в новом поколении и флаге -XX+PrintTenuringDistribution: 2014-01-30T10:47:43.809+0400: [GC [ParNew Desired survivor size 724775728 bytes, new threshold 15 (max 15) - age 1: 25185248 bytes, 25185248 total - age 2: 268810584 bytes, 293995832 total - age 3: 3159800 bytes, 297155632 total - age 4: 229752592 bytes, 526908224 total - age 5: 28050384 bytes, 554958608 total - age 6: 7744840 bytes, 562703448 total : 2209979K->656455K(2359296K), 0.4699730 secs] 7615361K- >6061837K(13893632K), 0.4702490 secs] [Times: user=3.46 sys=0.06, real=0.47 secs]
  29. 29. 29 2 последовательных вызова GC • 2014-01-28T14:42:44.102+0400: [GC [ParNew Desired survivor size 26836992 bytes, new threshold 1 (max 4) - age 1: 29692568 bytes, 29692568 total - age 2: 12514000 bytes, 42206568 total : 461639K->52416K(471872K), 0.0553180 secs] 6447587K- >6050819K(12530496K), 0.0554520 secs] [Times: user=0.81 sys=0.01, real=0.06 secs] • 2014-01-28T14:42:45.794+0400: [GC [ParNew Desired survivor size 26836992 bytes, new threshold 1 (max 4) - age 1: 32051328 bytes, 32051328 total : 471872K->42579K(471872K), 0.0656970 secs] 6470275K- >6067036K(12530496K), 0.0658310 secs] [Times: user=0.84 sys=0.01, real=0.07 secs]
  30. 30. 30 Как уменьшить объёмы миграций из одного поколения в другое • Увеличить размер нового поколения • Увеличить возраст объектов, при котором они могут мигрировать в tenured generation • Увеличить survivor space и его threshold • Тюнить само приложение
  31. 31. 31 Параметры JVM для нового поколения • -XX:NewSize=3g • -XX:InitialTenuringThreshold=15 • -XX:MaxTenuringThreshold=15 • -XX:SurvivorRatio=2 • -XX:TargetSurvivorRatio=90 • -XX:-UseAdaptiveSizePolicy
  32. 32. 32 Результат • Объёмы миграций стали меньше • Concurrent mode failure стал реже (а именно – раз в час-полтора)
  33. 33. 33 Что-то делаем с CMS ? • CMS долго не мог понять, когда ему стартовать… 12993.795: [GC [1 CMS-initial-mark: 8498366K(11184832K)] 8647061K(12443136K), 0.1610800 secs] [Times: user=0.16 sys=0.00, real=0.16 secs] • Реально живых объектов – примерно 5.0 гб. И concurrent mode failure очищает весь хип примерно до такого объёма (чаще всего) • Посоветуем CMS запускаться тогда и только тогда, когда старое поколение будет заполнено на 5.5 гб
  34. 34. 34 Параметры JVM для старого поколения • -XX:+UseCMSInitiatingOccupancyOnly • -XX:CMSInitiatingOccupancyFraction=50 – как раз соответсвует запуску CMS при заполнении tenured generation на 5.5 гб • -XX:+CMSScavengeBeforeRemark
  35. 35. 35 Окончательный список параметров JVM для GC • -Xms14G • -Xmx14G • -XX:+UseParNewGC • -XX:+UseConcMarkSweepGC • -XX:+UseCMSInitiatingOccupancyOnly • -XX:CMSInitiatingOccupancyFraction=50 • -XX:-UseAdaptiveSizePolicy • -XX:+CMSScavengeBeforeRemark • -XX:NewSize=3g • -XX:+PrintTenuringDistribution • -XX:InitialTenuringThreshold=15 • -XX:MaxTenuringThreshold=15 • -XX:SurvivorRatio=2 • -XX:TargetSurvivorRatio=90
  36. 36. 36 Результат • Concurrent mode failure стал редкостью (раз в день) • Для нашего приложения это нормально
  37. 37. 37 Один день – это часто • Возможно, для кого-то один день – это часто. Какие ещё workarounds возможны?
  38. 38. 38 Переход на JDK 8 • Переходим на JDK 8 и используем G1 GC • Хороший повод выучить lambda expressions, java.util.stream package, type annotations и т. д. • Раз уж заговорили про лямбды, то вспомнить аксиомы лямбда-исчисления, а также – почему эта формальная система полна по Тюрингу
  39. 39. 39 Extra-tuning CMS • Тюнинг, связанный с уменьшением фрагментации tenured generation • Смотреть YoungPLABSize, OldPLABSize, ParallelGCRetainPLAB, TargetPLABWastePct, PLABWeight, ResizePLAB, PrintPLAB, CMSParPromoteBlocksToClaim, CMSPLABRecordAlways • В общем говоря, смотреть опции, которые выводит: java -server -XX:+PrintFlagsFinal -version 2>/dev/null | grep -i PLAB
  40. 40. 40 Управляем Full GC • А почему бы не сделать так, чтобы полная сборка (Full GC) вызывалась в определённое время, когда нагрузка на сервера минимальная? • Можно: джоб с одним-единственным вызовом System.gc() 2014-07-21T01:16:04.820-0700: 52.160: [Full GC (System.gc()) 52.160: [CMS: • Для этого ещё надо: -XX:-DisableExplicitGC -XX:-ExplicitGCInvokesConcurrent (собственно, такие флаги по умолчанию) • Проблема: все сервера будут недоступны в одно и то же время • Сразу вопрос: System.gc() ведь всегда плохо?
  41. 41. 41 Управляем Full GC • Проблема: все сервера будут недоступны в одно и то же время • Сразу вопрос: System.gc() ведь всегда плохо?
  42. 42. 42 Немного в защиту System.gc() • “If you observe an explicit full garbage collection in the garbage collection logs, determine why it is happening and then decide whether it should be disabled, whether the call should be removed from the source code, or whether it makes sense to specify an invocation of a CMS concurrent garbage collection cycle.” - Charlie Hunt, Binu John, Java performance
  43. 43. 43 Ещё немного в защиту System.gc() • “Be careful when disabling explicit garbage collection. Doing so may have a nontrivial performance impact on the Java application. There may also be situations where timely object reference processing is required and garbage collections are not happening frequently enough for that to occur. Applications using Java RMI may be subject to this. It is advisable when explicitly disabling explicit garbage collection to have a reason for doing so. Likewise, it is advisable to have a reason for using System.gc() in an application.” - Charlie Hunt, Binu John, Java performance
  44. 44. 44 Управляем Full GC • Можно сделать так, что джоб (с System.gc() внутри) исполнялся бы с псевдослучайным интервалом • Кажется, проблема того, что все сервера будут недоступны в одно и то же время, решена? • Какова вероятность того, что при таком подходе будет существовать момент времени, при котором все сервера ушли в Full GC?
  45. 45. 45 Формализация • Пусть 𝑙 – общий интервал (например, 86400 сек.), 𝑠 – длительность Full GC (в секундах), 𝑁 – количество машин • На каждой из N машин в момент времени 𝑡 ∈ 𝑈 0, 𝑙 запускается джоб с System.gc(), и полная сборка занимает 𝑠 секунд • Какова вероятность того, что существует момент времени, в который на всех машинах Full GC?
  46. 46. 46 Решение • Вероятность того, что запуск Full GC на первой машине произойдёт в момент 𝑡 ∈ 𝑠, 𝑙 − 𝑠 , а у остальных – в моменты на промежутках 𝑡 − 𝑠, 𝑡 + 𝑠 , равна 𝑙−2𝑠 𝑙 2𝑠 𝑙 𝑁−1 • Остаётся ещё вариант, когда Full GC на первой машине произойдёт в момент 𝑡 ∈ 0, 𝑠 или 𝑡 ∈ 𝑙 − 𝑠, 𝑙 . В общем говоря, это событие влечёт за собой событие, когда на всех машинах Full GC сработал на 0,2𝑠 или 𝑙 − 2𝑠, 𝑙 , вероятность каждого из которых 2𝑠 𝑙 𝑁 • Поэтому: 𝑙 − 2𝑠 𝑙 2𝑠 𝑙 𝑁−1 < 𝑝 𝑁, 𝑙, 𝑠 < 𝑙 − 2𝑠 𝑙 2𝑠 𝑙 𝑁−1 + 2 2𝑠 𝑙 𝑁 𝑙 − 2𝑠 𝑙 2𝑠 𝑙 𝑁−1 < 𝑝 𝑁, 𝑙, 𝑠 < 𝑙 + 2𝑠 𝑙 2𝑠 𝑙 𝑁−1 • Для 𝑁 = 2, 𝑙 = 86400, 𝑠 = 90 эта вероятность между 0.00415 и 0.00418
  47. 47. 47 Ещё бы что-то придумать… • Разбиваете интервал (один день) на несколько интервалов, равное количеству машин • Для каждой из машин забираете с DistributedAtomicLong (Curator Framework, Apache ZooKeeper) уникальные последовательные значения, и эссайните каждой машине свой интервал • Для каждой машины System.gc() запускается только на своём интервале • Ну и всё… по идее, в каждый момент времени недоступной будет не более одной машины…
  48. 48. 48 Литература • Java performance / Charlie Hunt, Binu John “This book is the definitive masterclass in performance tuning Java applications…” - James Gosling “This book you are now reading is the most ambitious book on the topic of Java performance that has ever been written.” - Steve Wilson, VP Engineering, Oracle Corporation • Java technical notes: http://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html • The Java HotSpot Performance Engine Architecture: http://www.oracle.com/technetwork/java/whitepaper-135217.html • Java GC, HotSpot's CMS and heap fragmentation: http://blog.ragozin.info/2011/10/java-cg- hotspots-cms-and-heap.html • Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning: http://www.oracle.com/technetwork/java/javase/gc-tuning-6-140523.html • Jon Masamitsu's Weblog: https://blogs.oracle.com/jonthecollector/entry/did_you_know • “abort preclean due to time” in Concurrent Mark & Sweep: http://mail.openjdk.java.net/pipermail/hotspot-gc-use/2011-May/000862.html • Concurrent mode failure in practice, part 1: http://kyryloholodnov.wordpress.com/2014/02/19/concurrent-mode-failure-in-practice-part-1 • Concurrent mode failure in practice, part 2: http://kyryloholodnov.wordpress.com/2014/05/27/concurrent-mode-failure-in-practice-part-2

×