如何从应用程序内部检测 JVM 垃圾收集周期?

Jay*_*667 2 java garbage-collection

我只是想知道是否有一种方法可以从正在 gc 的代码/JVM 中检测垃圾收集周期。

时机不起作用。因此,事件发生在实际周期之前还是之后并不重要。(在循环期间发生事件似乎极不可能,而且也可能很危险,具体取决于所使用的 GC 实现)。

我能找到的只是可以与正在运行的 JVM 并行使用的应用程序,例如 jstat:https: //dzone.com/articles/how-monitor-java-garbage以及此处的源代码,例如:https:// /github.com/eagle518/jdk-source-code/blob/master/jdk5.0_src/j2se/src/share/classes/sun/tools/jstat/Jstat.java

我所看到的是他们使用

    MonitoredHost monitoredHost = MonitoredHost.getMonitoredHost(vmId);
    MonitoredVm monitoredVm = monitoredHost.getMonitoredVm(vmId, interval);
Run Code Online (Sandbox Code Playgroud)

所以我的问题基于两个问题。如果第二个问题有解,我们就不需要回答第一个问题:

  1. 我对MonitoredVm的了解还不够好,不知道如何访问本地JVM。通常需要 VM ID。我怎样才能可靠地解决这个问题,或者是否有其他方法可以获取“本地” MonitoredVm
  2. 甚至有必要使用 MonitoredVm,还是有更简单的方法,比如可以“安装”关闭挂钩并检测该事件 ( Runtime.getRuntime().addShutdownHook(hookThread);)?Java Reflection 也是一个受欢迎的解决方案!

Hol*_*ger 6

GarbageCollectorMXBean正指向正确的方向。xe2x80x99 不明显的是,这个 bean 实现了NotificationEmitter允许有关垃圾收集的通知:

\n
static volatile Object PREVENT_ESCAPE_ANALYSIS;\n\npublic static void main(String[] args) {\n    List<GarbageCollectorMXBean> gc = ManagementFactory.getGarbageCollectorMXBeans();\n    for(GarbageCollectorMXBean b: gc) {\n        ((NotificationEmitter)b).addNotificationListener((n, handback) -> {\n            GarbageCollectionNotificationInfo gcni\n                = GarbageCollectionNotificationInfo.from((CompositeData)n.getUserData());\n            GcInfo info = gcni.getGcInfo();\n            System.out.printf("%tT.%1$tL %dms %s %s %s%n", info.getStartTime(),\n               info.getDuration(),gcni.getGcName(),gcni.getGcAction(),gcni.getGcCause());\n            Map<String, MemoryUsage> before = info.getMemoryUsageBeforeGc();\n            info.getMemoryUsageAfterGc().forEach((s, u) -> {\n                long bu = before.get(s).getUsed(), au = u.getUsed();\n                if(bu != au) System.out.println("\\t" + s + " " + bu + " -> " + au);\n            });\n        }, null, b);\n    }\n\n    for(;;) {\n        PREVENT_ESCAPE_ANALYSIS = new Object();\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

Ideone 上的演示

\n
00:00:00.203 2ms Copy end of minor GC Allocation Failure\n    Eden Space 70516736 -> 0\n    Survivor Space 0 -> 795176\n00:00:00.271 1ms Copy end of minor GC Allocation Failure\n    Eden Space 70516736 -> 0\n    Survivor Space 795176 -> 862960\n00:00:00.355 2ms Copy end of minor GC Allocation Failure\n    Eden Space 70516736 -> 0\n    Survivor Space 862960 -> 931952\n00:00:00.412 2ms Copy end of minor GC Allocation Failure\n    Eden Space 70516736 -> 0\n    Survivor Space 931952 -> 1004784\n00:00:00.497 2ms Copy end of minor GC Allocation Failure\n    Eden Space 70516736 -> 0\n    Survivor Space 1004784 -> 1379400\n00:00:00.574 3ms Copy end of minor GC Allocation Failure\n    Eden Space 70516736 -> 0\n    Survivor Space 1379400 -> 1551944\n00:00:00.632 2ms Copy end of minor GC Allocation Failure\n    Eden Space 70516736 -> 0\n    Survivor Space 1551944 -> 1541144\n\xe2\x80\xa6\n
Run Code Online (Sandbox Code Playgroud)\n

  • 事实上,我机器上的日志看起来也比这个好。生成这些字符串的是 Ideone 机器(分别是他们配置的 GC 算法)。当我看到这一点时,我考虑在字符串之间放置更强的分隔符,但后来放弃了这个想法,因为无论如何它只是简单的示例代码......请注意,“GC 分配失败”是触发 GC 的事件的名称,对于非并发 GC,因为它们仅在分配失败时才开始工作。 (2认同)