用于确定JVM何时进入内存/ GC故障的有用指标

Arn*_*sen 11 java garbage-collection jvm scala

我有一个scala数据处理应用程序,95%的时间可以处理内存中抛出的数据.如果不加以检查,剩余的5%通常不会触及OutOfMemoryError,但只是进入一个主要GC的循环,它会刺激CPU,防止后台线程执行,如果它甚至完成,则需要10x-50x,只要它有足够的记忆力.

我已经实现了可以将数据刷新到磁盘并将磁盘流视为内存中迭代器的系统.它通常比内存慢一个数量级,但足以满足这5%的情况.我目前正在通过一个集合上下文的最大大小的启发式触发,该集合上下文跟踪数据处理中涉及的各种集合的大小.这有效,但实际上只是一个临时的经验门槛.

我宁愿做出反应,让JVM接近上面的错误状态并在那时刷新到磁盘.我试过看过记忆,但是找不到正确的伊甸园,旧等组合来可靠地预测死亡螺旋.我也尝试过观看主要地方选区的频率,但这似乎也受到各种"过于保守"到"太迟"的影​​响.

任何用于判断JVM运行状况和检测故障状态的资源都将受到赞赏.

K E*_*son 4

一种可靠的方法是注册 GC 事件的通知侦听器,并在所有 Full GC 事件后检查内存运行状况。在完整 GC 事件之后,使用的内存就是您实际的实时数据集。如果此时您的可用内存不足,那么可能是时候开始刷新到磁盘了。

这样,您可以避免在不知道何时发生完整 GC 的情况下尝试检查内存时经常发生的误报,例如使用通知MEMORY_THRESHOLD_EXCEEDED类型时。

您可以使用类似以下代码注册通知侦听器并处理 Full GC 事件:

// ... standard imports ommitted
import com.sun.management.GarbageCollectionNotificationInfo;

public static void installGCMonitoring() {
    List<GarbageCollectorMXBean> gcBeans = ManagementFactory.getGarbageCollectorMXBeans();
    for (GarbageCollectorMXBean gcBean : gcBeans) {
        NotificationEmitter emitter = (NotificationEmitter) gcBean;
        NotificationListener listener = notificationListener();
        emitter.addNotificationListener(listener, null, null);
    }
}

private static NotificationListener notificationListener() {
    return new NotificationListener() {
        @Override
        public void handleNotification(Notification notification, Object handback) {
            if (notification.getType()
                    .equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) {
                GarbageCollectionNotificationInfo info = GarbageCollectionNotificationInfo
                        .from((CompositeData) notification.getUserData());
                String gctype = info.getGcAction();
                if (gctype.contains("major")) {
                    // We are only interested in full (major) GCs
                    Map<String, MemoryUsage> mem = info.getGcInfo().getMemoryUsageAfterGc();
                    for (Entry<String, MemoryUsage> entry : mem.entrySet()) {
                        String memoryPoolName = entry.getKey();
                        MemoryUsage memdetail = entry.getValue();
                        long memMax = memdetail.getMax();
                        long memUsed = memdetail.getUsed();
                        // Use the memMax/memUsed of the pool you are interested in (probably old gen)
                        // to determine memory health.
                    }
                }
            }
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

感谢这篇文章,我们首先从这里得到了这个想法。