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

How to detect JVM Garbage Collection cycle from INSIDE the app?

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

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

我只能找到可以与 运行 JVM 并行使用的应用程序,例如 jstat:https://dzone.com/articles/how-monitor-java-garbage with the source code here, for example: 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);

所以我的问题是基于 2 个问题。如果第二个问题有解,我们就不用回答问题1了:

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

至少 count(和累计时间)的垃圾收集是通过 JMX MBeans 公开的。

特别是 ManagementFactory.getGarbageCollectorMXBeans() will return a list of GarbageCollectorMXBean 提供 getCollectionCount() 方法的对象。您可以轮询这些值并对它们的变化做出反应。

GarbageCollectorMXBean is pointing into the right direction. What’s not obvious, is that this bean implements NotificationEmitter 允许有关垃圾回收的通知:

static volatile Object PREVENT_ESCAPE_ANALYSIS;

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

    for(;;) {
        PREVENT_ESCAPE_ANALYSIS = new Object();
    }
}

Demo on Ideone

00:00:00.203 2ms Copy end of minor GC Allocation Failure
    Eden Space 70516736 -> 0
    Survivor Space 0 -> 795176
00:00:00.271 1ms Copy end of minor GC Allocation Failure
    Eden Space 70516736 -> 0
    Survivor Space 795176 -> 862960
00:00:00.355 2ms Copy end of minor GC Allocation Failure
    Eden Space 70516736 -> 0
    Survivor Space 862960 -> 931952
00:00:00.412 2ms Copy end of minor GC Allocation Failure
    Eden Space 70516736 -> 0
    Survivor Space 931952 -> 1004784
00:00:00.497 2ms Copy end of minor GC Allocation Failure
    Eden Space 70516736 -> 0
    Survivor Space 1004784 -> 1379400
00:00:00.574 3ms Copy end of minor GC Allocation Failure
    Eden Space 70516736 -> 0
    Survivor Space 1379400 -> 1551944
00:00:00.632 2ms Copy end of minor GC Allocation Failure
    Eden Space 70516736 -> 0
    Survivor Space 1551944 -> 1541144
…