У нас возникли серьезные проблемы с нашим приложением Java EE. В частности, приложение запускает до 99% кучи старого поколения в течение нескольких минут после запуска. Никаких OOM не бросают, но эффективно JVM не реагирует. Jstat показывает, что старое поколение вообще не уменьшает размер, сбор мусора продолжается, а kill -3 говорит:
Heap
PSYoungGen total 682688K, used 506415K [0xc1840000, 0xf3840000, 0xf3840000)
eden space 546176K, 92% used [0xc1840000,0xe06cd020,0xe2da0000)
from space 136512K, 0% used [0xe2da0000,0xe2da0000,0xeb2f0000)
to space 136512K, 0% used [0xeb2f0000,0xeb2f0000,0xf3840000)
PSOldGen total 1536000K, used 1535999K [0x63c40000, 0xc1840000, 0xc1840000)
object space 1536000K, 99% used [0x63c40000,0xc183fff8,0xc1840000)
Параметры VM:
-Xmx2300m -Xms2300m -XX:NewSize=800m -XX:MaxNewSize=800m -XX:SurvivorRatio=4 -XX:PermSize=256m -XX:MaxPermSize=256m -XX:+UseParallelGC -XX:ParallelGCThreads=4
(я изменил его с 2300m heap/1800m new gen, как попытку решить проблему)
Я взял кучу кучи JVM, когда он добрался до состояния "из памяти" (взял навсегда), и на нем запустил Eclipse Memory Analyzer.
Результаты довольно забавные. Около 200 Мб заняты объектами всех видов (некоторые из них принадлежат больше других), но все остальное - 1.9Gb - все недостижимы (может быть, следует отметить, что большинство занято объектами GSON, но я не думаю, что это указание на что-либо, только говорит, что мы отказываемся от множества объектов GSON во время работы сервера).
Любое объяснение того, почему виртуальная машина имеет столько недостижимых объектов и не может собирать их вообще?
JVM:
$ /0/bin/java -version
java version "1.6.0_37"
Java(TM) SE Runtime Environment (build 1.6.0_37-b06)
Java HotSpot(TM) Server VM (build 20.12-b01, mixed mode)
Когда система попадает в этот киоск, вот то, что подробный GC продолжает печатать:
922.485: [GC [1 CMS-initial-mark: 511999K(512000K)] 1952308K(2048000K), 3.9069700 secs] [Times: user=3.91 sys=0.00, real=3.91 secs]
926.392: [CMS-concurrent-mark-start]
927.401: [Full GC 927.401: [CMS927.779: [CMS-concurrent-mark: 1.215/1.386 secs] [Times: user=5.84 sys=0.13, real=1.38 secs] (concurrent mode failure): 511999K->511999K(512000K), 9.4827600 secs] 2047999K->1957315K(2048000K), [CMS Perm : 115315K->115301K(262144K)], 9.4829860 secs] [Times: user=9.78 sys=0.01, real=9.49 secs]
937.746: [Full GC 937.746: [CMS: 512000K->511999K(512000K), 8.8891390 secs] 2047999K->1962252K(2048000K), [CMS Perm : 115302K->115302K(262144K)], 8.8893810 secs] [Times: user=8.89 sys=0.01, real=8.89 secs]
решаемые
Как предложил Пол Беллора, это было вызвано слишком большим количеством объектов, созданных в JVM, за слишком короткий промежуток времени. На данный момент отладка становится довольно утомительной. То, что я закончил, - это инструменты для классов, использующих настраиваемый агент JVM. Инструментарий будет считать вызовы метода и конструктора. Затем подсчеты были проверены. Я обнаружил, что незаметная одиночная операция создаст около 2 миллионов объектов и вызовет некоторые отдельные методы примерно 1,5 миллиона раз (нет, не было циклов). Сама операция была идентифицирована, будучи медленной по сравнению с другими. Вы можете использовать любой профилировщик hotspot (что-то вроде visualVM), но у меня были всевозможные проблемы с ними, поэтому я написал свои собственные.
Я все еще думаю, что поведение JVM является загадкой. Похоже, сборщик мусора приходит в стойло и не очищает больше памяти, но распределитель памяти ожидает его (и, таким образом, не бросается OOM). Вместо этого я ожидал, что он очистит всю эту недостижимую память. Но поведение приложения было бы не намного лучше, так как большинство времени было бы потрачено на сбор мусора в любом случае.