我如何知道谁在spark流程序中调用了System.gc()?

Fra*_*ank 2 garbage-collection jvm spark-streaming

我的 Spark Streaming 程序中 GC 时间太长。在GC日志中,我发现有人System.gc()在程序中调用了。我不调用System.gc()我的代码。所以调用者应该是我使用的api。

我添加-XX:-DisableExplicitGC到 JVM 并修复了这个问题。不过,我想知道是谁打电话的System.gc()

我尝试了一些方法。

  1. 使用jstack。但GC不是那么频繁,很难转储调用该方法的线程。
  2. 我添加了在 JProfiler 中调用方法时添加线程转储的触发器java.lang.System.gc()。但这似乎不起作用。

我如何知道谁在spark流程序中调用了System.gc()?

apa*_*gin 7

您不会发现System.gcjstack因为在 stop-the-world 暂停期间,JVM 不接受来自 Dynamic Attach 工具的连接,包括jstackjmapjcmd类似工具。

可以使用async-profilerSystem.gc跟踪调用者:

  1. 预先开始分析:

    $ profiler.sh start -e java.lang.System.gc <pid>
    
    Run Code Online (Sandbox Code Playgroud)
  2. 发生一种或多种System.gc情况后,停止分析并打印堆栈跟踪:

    $ profiler.sh stop -o traces <pid>
    
    Run Code Online (Sandbox Code Playgroud)

    输出示例:

    --- Execution profile ---
    Total samples       : 6
    
    Frame buffer usage  : 0.0007%
    
    --- 4 calls (66.67%), 4 samples
      [ 0] java.lang.System.gc
      [ 1] java.nio.Bits.reserveMemory
      [ 2] java.nio.DirectByteBuffer.<init>
      [ 3] java.nio.ByteBuffer.allocateDirect
      [ 4] Allocate.main
    
    --- 2 calls (33.33%), 2 samples
      [ 0] java.lang.System.gc
      [ 1] sun.misc.GC$Daemon.run
    
    Run Code Online (Sandbox Code Playgroud)

    在上面的示例中,System.gc从两个地方调用了 6 次。这两种情况都是 JDK 内部强制进行垃圾回收的典型情况。

    第一个来自java.nio.Bits.reserveMemory. 当没有足够的可用内存来分配新的直接 ByteBuffer 时(由于-XX:MaxDirectMemorySize限制),JDK 会强制进行 Full GC 回收无法访问的直接 ByteBuffer。

    第二个来自 GC Daemon 线程。这由 Java RMI 运行时定期调用。例如,如果您使用 JMX 远程,则每小时自动启用一次定期 GC。这可以通过-Dsun.rmi.dgc.client.gcInterval系统属性进行调整。