Groovy Проблемы с производительностью Invoke Dynamic?
Я изо всех сил пытаюсь определить, что заставляет умеренно большое приложение Groovy медленно работать в производственной среде. При получении дампов потоков запущенных приложений странная вещь, которую я вижу, это множество потоков с трассировкой стека:
at java.lang.invoke.MethodHandleNatives.setCallSiteTargetNormal(Native Method)
at java.lang.invoke.CallSite.setTargetNormal(CallSite.java:258)
at java.lang.invoke.MutableCallSite.setTarget(MutableCallSite.java:154)
at org.codehaus.groovy.vmplugin.v7.Selector$MethodSelector.doCallSiteTargetSet(Selector.java:909)
at org.codehaus.groovy.vmplugin.v7.Selector$MethodSelector.setCallSiteTarget(Selector.java:969)
at org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:228)
at java.lang.invoke.LambdaForm$DMH/1665404403.invokeStatic_L3IL5_L(LambdaForm$DMH)
at java.lang.invoke.LambdaForm$BMH/1705072168.reinvoke(LambdaForm$BMH)
Я видел некоторые упоминания в Интернете, что setCallSiteTargetNormal
Метод довольно тяжелый и блокирует все потоки JVM во время его вызова. Мы используем динамическую поддержку вызова Groovy, и мне интересно, не сталкиваемся ли мы с какой-то ошибкой в Groovy, из-за которой этот метод вызывается слишком широко.
Очевидные проблемы с производительностью, которые я уже проверил:
- Использование памяти в порядке
- GC накладные расходы в норме
- Загрузка процессора на сервере выглядит нормально
- Внешняя БД и вызовы веб-сервисов все нормально
Одно замечание об использовании ЦП заключается в том, что приложение ведет себя так, как если бы оно нуждалось в ЦП, но использует только около 1/4 от общего ЦП компьютера с 4 ЦП. Он действует так, как будто поток должен привязывать процессор на 100%, но я этого не вижу. Однако некоторая информация, которую я нашел в Интернете, указывает на setCallSiTeTargetNormal
вызов метода как нечто, полностью блокирующее все потоки в JVM.
1 ответ
Позвольте мне предложить другую альтернативу setCallSiTeTargetNormal
метод. В такой проблеме я бы проверил журналы GC, чтобы увидеть, не слишком ли часто они вводятся для очистки Permgen (для Java 7 и более ранних версий) или Metaspace (для Java 8 и более поздних версий). Metaspace автоматически очищается, если вы указываете для него ограничение, а для Permgen вам нужны определенные ключи в командной строке.
Когда Groovy используется для выполнения скрипта внутри работающей JVM, он интенсивно использует это пространство для создания загрузчиков классов для каждого скрипта. Затем, когда он достигает своего предела, GC должен очистить его. Это мой сценарий при использовании Jenkins, например. Таким образом, используя Metaspace, как будто это была какая-то куча, с таким же поведением. Каждое сканирование GC должно останавливать ваше приложение, вызывая заметные остановки, не затрагивая процессор и т. Д.
Вы должны быть в состоянии протестировать и, возможно, отказаться от этой идеи, используя эту командную строку, чтобы запустить свое приложение и посмотреть, как часто GC выполняет полный цикл (и как долго длится остановка).
-XX:+PrintGC -XX:+PrintGCTimeStamps -Xloggc:/my/log/file