Да, начальник попросил меня написать БАГ!

Java JVM
Да, начальник попросил меня написать БАГ!

предисловие

Название не ошибся, очень хочется написатьbug!

Когда я впервые получил эту просьбу, я не почувствовал ни малейшего волнения в своем сердце и даже был немного взволнован. Это моя специальность, наконец-то я могу писать открытоbug🙄.

Давайте сначала посмотрим, что он собирается делать.На самом деле, основная цель состоит в том, чтобы заставить некоторые серверы с очень низкой нагрузкой потреблять дополнительные ресурсы, такие как память и процессор (насчет предыстории, я не буду говорить больше), чтобы можно было увеличить нагрузку.

Обзор распределения памяти JVM

Поэтому я взял шаттл и написал код, который примерно выглядит следующим образом:

После того, как я написал это, я думал о проблеме, коде вmemБудет ли объект переработан сразу после выполнения метода? Я думаю, что должны быть некоторые люди, которые думают, что он перерабатывается после выполнения метода.

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

А факты? Я сделал тест.

Я только что запустил приложение со следующими параметрами запуска.

java -Djava.rmi.server.hostname=10.xx.xx.xx 
-Djava.security.policy=jstatd.all.policy 
-Dcom.sun.management.jmxremote.authenticate=false 
-Dcom.sun.management.jmxremote.ssl=false 
-Dcom.sun.management.jmxremote.port=8888  
-Xms4g -Xmx4g  -jar bug-0.0.1-SNAPSHOT.jar

Таким образом, я могу удаленно подключиться к этому приложению через порт JMX, чтобы наблюдать за ситуацией с памятью и GC.


Если метод завершен, он будет переработанmemобъект, когда я назначаю250Mпамяти, память будет иметь четкую кривую, и GC тоже будет работать.


Затем наблюдайте за кривой памяти.

Выяснится, что действительно имеется значительное увеличение, но оно не восстанавливается сразу после этого, а поддерживается на этом уровне воды. При этом ГК слева не отвечает.

использоватьjstatТо же самое касается просмотра схемы памяти.

не важно какYGC,FGCНет, это всего лишь увеличение использования площади Эдема, ведь выделено 250М памяти.

Так как же он будет перерабатываться?

Я наблюдал кривую памяти после повторного выделения двух 250M.

Когда будут найдены третьи 250МEdenдостигнутая площадь98.83%Поэтому его необходимо переработать, когда он снова будет выделен.Edenрайон производстваYGC.

В то же время кривая памяти также была уменьшена.

Весь процесс преобразования показан на рисунке:

Поскольку инициализированная куча памяти4G, поэтому рассчитаноEdenПлощадь составляет примерно1092MОЗУ.

плюс запуск приложенияSpringпотребляет ок.20%память, поэтому выделение 250M памяти 3 раза приведет кYGC.

Вернемся к предыдущему вопросу:

memПоскольку объект не будет переработан после выполнения метода, когда он будет переработан?

На самом деле, помните только одно:Для объектов требуется сборщик мусораGCмогут быть переработаны, независимо от того, является ли объект локальной переменной или глобальной переменной.

В ходе предыдущих экспериментов также было установлено, что приEdenНедостаточно места в районеYGCбудут переработаны, когда мы создадимmemобъект.

Но на самом деле здесь есть скрытое условие: то есть объектлокальная переменная. Если объект является глобальной переменной, он все равно не может быть переработан.

Это то, что мы часто говоримОбъект недоступен, так что недостижимый объект находится вGCКогда это происходит, считается, что это объект, который необходимо переработать, и перерабатывается.

После долгих размышлений, почему некоторые люди думают, что локальные переменные будут переработаны после выполнения метода?

Я думаю это надо путать, на самом деле после выполнения метода рекавери идет栈帧.

Его самый непосредственный результат должен привести кmemЭтот объект больше не упоминается. Но отсутствие справки не означает, что она будет переработана сразу, то есть необходимость генерирования упомянутого вышеGCбудут переработаны.

Таким образом, используется вышеупомянутый недостижимый объектАлгоритмы анализа достижимостичтобы указать, какие объекты должны быть переработаны.

Когда на объект не ссылаются, он считается недостижимым.

Вот движущаяся картинка, которая более четкая:

После выполнения методаmemОбъект эквивалентен изображениюObject 5, так что вGCбудет переработано вовремя.

Приоритет отдается размещению объектов в районе Эдема.

На самом деле из вышеприведенного примера видно, что в новом поколении объект предпочтительно размещается в районе Эдема, но есть посылка, что объект не может быть слишком большим.

Я уже писал об этом:

Крупные предметы уходят прямо в старость

Крупный объект напрямую отнесен к старому возрасту (насколько он велик, это можно настроить параметрами).


Когда я напрямую выделил 1000 МБ памяти, поскольку область Eden не могла быть загружена напрямую, вместо этого она была выделена в старом поколении.

можно увидетьEdenПлощадь почти не изменилась, а вот старость увеличилась на 37%, согласно рассчитанной ранее памяти старости.2730MПочти1000Mпамяти.

проверка памяти линукс

Вернемся к тому, что мне нужно сделать на этот раз: увеличить нагрузку на сервер и ЦП.

С ЦП все в порядке, он сам использует определенное количество ресурсов, и каждый раз, когда создается объект, он потребляет некоторое количество ЦП.

В основном память, давайте сначала посмотрим на ситуацию с памятью перед запуском приложения.

Вероятно, используется только память 3G.

После запуска приложения оно, вероятно, потребляет всего около 600 м памяти.

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

Невозможно выделять память все время, что приведет к слишком высокой загрузке ЦП, а память будет занята не очень сильно из-за рециркуляции сборщика мусора.

Поэтому мне нужно выделить небольшую сумму, чтобы большинство объектов в молодом поколении нужно было сохранить на 80% или 90%, чтобы их не собирали.

В то же время также необходимо выделить некоторые крупные объекты для старости и сохранить использование старости на уровне 80-90%.

Таким образом, память кучи 4G может использоваться в максимальной степени.

Поэтому я сделал следующее:

  • Сначала выделите несколько небольших объектов в новом поколении (800M), чтобы поддерживать новое поколение на уровне 90%.
  • а затем выделил老年代内 *(100%-已使用的28%);也就是 2730*60%=1638MПусть старость тоже будет в районе 90%.

Эффект как выше.

Главное один разGCЭтого не произошло, и я этого хотел.

Конечное потребление памяти составляет около 3,5G.

Суммировать

Хотя спрос в этот раз довольно странный, но хочется точного контроляJVMРаспределение памяти по-прежнему не так просто.

Вам нужно иметь определенное представление о его структуре памяти и утилизации.Процесс написания этого бага действительно усилил впечатление.Если вам это поможет, пожалуйста, не скупитесь на лайки и репост.

Ваши лайки и репост - лучшая поддержка для меня