gpt4 book ai didi

Java G1 : Monitoring for memory leaks in production

转载 作者:太空狗 更新时间:2023-10-29 22:58:30 25 4
gpt4 key购买 nike

多年来,我们一直使用 +UseParallelOldGC 以适度的堆大小运行 Java 服务。现在,我们开始使用更大的堆和 G1 收集器推出一项新服务。进展顺利。

对于我们使用 +UseParallelOldGC 的服务,我们通过在收集后查看老年代大小并根据阈值发出警报来监控内存泄漏。这非常有效,实际上两周前就拯救了我们的培根。

具体来说,对于 +UseParallelOldGC,我们执行以下操作:

  • ManagementFactory.getMemoryPoolMXBeans()
  • 搜索名称以 "Old Gen" 结尾的 MemoryPoolMXBean 结果
  • 比较 getCollectionUsage().getUsed()(如果可用)与 getMax()

不幸的是,G1 似乎不再有 getCollectionUsage() 的概念。

不过,从根本上说,我们希望在它选择在混合循环或类似的东西中执行的最后一次混合收集之后监视 G1 堆大小。

例如,在 VM 之外,我会对 awk 脚本感到满意,该脚本仅发现最后一个 '(mixed)' 是紧随其后的 '(young)' 并查看最终堆大小是多少(例如,'1540.0M' 'Heap: 3694.5M(9216.0M)->1540.0M(9216.0M)')

有没有办法在 Java VM 中执行此操作?

最佳答案

是的,JVM 为您提供了足够的工具来检索 G1 的此类信息。例如,您可以使用类似此类的类来打印有关垃圾回收的所有详细信息(只需调用 MemoryUtil.startGCMonitor()):

public class MemoryUtil {

private static final Set<String> heapRegions;

static {
heapRegions = ManagementFactory.getMemoryPoolMXBeans().stream()
.filter(b -> b.getType() == MemoryType.HEAP)
.map(MemoryPoolMXBean::getName)
.collect(Collectors.toSet());
}

private static NotificationListener gcHandler = (notification, handback) -> {
if (notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) {
GarbageCollectionNotificationInfo gcInfo = GarbageCollectionNotificationInfo.from((CompositeData) notification.getUserData());
Map<String, MemoryUsage> memBefore = gcInfo.getGcInfo().getMemoryUsageBeforeGc();
Map<String, MemoryUsage> memAfter = gcInfo.getGcInfo().getMemoryUsageAfterGc();
StringBuilder sb = new StringBuilder(250);
sb.append("[").append(gcInfo.getGcAction()).append(" / ").append(gcInfo.getGcCause())
.append(" / ").append(gcInfo.getGcName()).append(" / (");
appendMemUsage(sb, memBefore);
sb.append(") -> (");
appendMemUsage(sb, memAfter);
sb.append("), ").append(gcInfo.getGcInfo().getDuration()).append(" ms]");
System.out.println(sb.toString());
}
};

public static void startGCMonitor() {
for(GarbageCollectorMXBean mBean: ManagementFactory.getGarbageCollectorMXBeans()) {
((NotificationEmitter) mBean).addNotificationListener(gcHandler, null, null);
}
}

public static void stopGCMonitor() {
for(GarbageCollectorMXBean mBean: ManagementFactory.getGarbageCollectorMXBeans()) {
try {
((NotificationEmitter) mBean).removeNotificationListener(gcHandler);
} catch(ListenerNotFoundException e) {
// Do nothing
}
}
}

private static void appendMemUsage(StringBuilder sb, Map<String, MemoryUsage> memUsage) {
memUsage.entrySet().forEach((entry) -> {
if (heapRegions.contains(entry.getKey())) {
sb.append(entry.getKey()).append(" used=").append(entry.getValue().getUsed() >> 10).append("K; ");
}
});
}
}

在此代码中,gcInfo.getGcAction() 为您提供了足够的信息来将次要集合与主要/混合集合分开。

但是使用您的方法(带有阈值)达到 G1 有一个重要的警告。 G1 中的单个混合回收通常只影响几个老年代区域——足够多以释放足够数量的内存,但不会太多以保持 GC 暂停时间较低。因此,在 G1 中进行混合回收后,您无法确定所有垃圾都已消失。因此,您需要找到更复杂的策略来检测内存泄漏(可能基于收集频率、从多个收集中收集统计信息等)

关于Java G1 : Monitoring for memory leaks in production,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35927926/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com