1

我正在努力确定是什么导致了一个中等大小的 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 开销是正常的
  • 服务器上的 CPU 使用率看起来还不错
  • 外部数据库和网络服务调用都正常

关于 CPU 使用率的一个注意事项是,该应用程序的行为就像它需要 CPU 一样,但它只使用了 4 CPU 机器的总 CPU 的大约 1/4。它的行为就像一个线程应该 100% 与 CPU 挂钩,但我根本没有看到这一点。然而,我在网上找到的一些信息表明setCallSiTeTargetNormal方法调用完全阻塞了 JVM 上的所有线程。

4

1 回答 1

0

让我提出该方法的另一种替代方案setCallSiTeTargetNormal。在这样的问题中,我会检查 GC 日志以查看它是否过于频繁地进入以清理 Permgen(对于 Java 7 及之前版本)或 Metaspace(对于 Java 8 及更高版本)。如果您为其指定限制,元空间会自动清理,而对于 Permgen,您需要在命令行中进行某些开关。

当用于在正在运行的 JVM 中执行脚本时,Groovy 会大量使用这些空间来为每个脚本实例化类加载器。然后,当它达到极限时,GC 必须清理它。例如,这是我使用 Jenkins 时的场景。因此,使用 Metaspace 就好像它是某种堆,具有相同的行为。每次 GC 扫描都应该停止您的应用程序,导致明显的停止,同时不会影响 CPU 等。

您应该能够通过使用此命令行启动您的应用程序并查看 GC 多久进行一次完整扫描(以及停止多长时间)来测试并可能放弃这个想法。

-XX:+PrintGC -XX:+PrintGCTimeStamps -Xloggc:/my/log/file

于 2017-08-22T19:29:14.203 回答