背景
最近公司在做GC方面的监控,由于堆转储方式(heap dump)对于系统损耗过大,在生产中不能使用该方式,经查阅在JDK1.7版本后,JDK新增了notification的方式,用于在各种内存变化信息时主动发送通知,该通知含有内存的详细信息,以GC通知为例,GarbageCollectionNotificationInfo中含有GC发生的起始时间,结束时间,耗时,使用算法,发生原因,各JVM内存块的init, max, committed, used的信息等,相对于被动检测来说(周期性从GarbageCollectionMXBean对象中获取JVM内存信息),这种方式具有更好的实时性,性能损耗更小。
类图
主要包括通知以及各种Bean
监听器
package com.peipei.jvm.notify.garbage;
import com.google.gson.Gson;
import com.sun.management.GarbageCollectionNotificationInfo;
import com.sun.management.GcInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.openmbean.CompositeData;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;
import java.lang.management.RuntimeMXBean;
import java.util.Map;
/**
* 监听器.当收到通知后由该监听器处理通知
*/
public class GarbageNotificationListener implements NotificationListener {
private static final Logger logger = LoggerFactory.getLogger(GarbageNotificationListener.class);
private static final Gson gson = new Gson();
private static final RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
private static final Long JVM_START_TIME = runtimeMXBean.getStartTime();
/**
* @param notification
* @param handback
*/
public void handleNotification(Notification notification, Object handback) {
// 垃圾回收的通知信息
GarbageCollectionNotificationInfo info = GarbageCollectionNotificationInfo.from((CompositeData) notification.getUserData());
// GC的相关信息
GcInfo gcInfo = info.getGcInfo();
logger.info("垃圾回收信息: {}", gson.toJson(gcInfo));
// 垃圾回收后的内存信息
Map<String, MemoryUsage> memoryUsageBeforeGcMap = gcInfo.getMemoryUsageBeforeGc();
logger.info("垃圾回收前内存信息: {}", gson.toJson(gcInfo));
// 垃圾回收后的内存信息
Map<String, MemoryUsage> memoryUsageAfterGcMap = gcInfo.getMemoryUsageAfterGc();
logger.info("垃圾回收后内存信息: {}", gson.toJson(gcInfo));
// GC发生的开始时间,是相对于JVM启动时间的相对值
long gcStartTime = gcInfo.getStartTime() + JVM_START_TIME;
// GC发生的结束时间,是相对于JVM启动时间的相对值
long gcEndTime = gcInfo.getEndTime();
// GC耗时
long duration = gcInfo.getDuration();
logger.info("GC发生于{}, 结束于{}, 耗时 {}ms", gcStartTime, gcEndTime, duration);
}
}
过滤器
package com.peipei.jvm.notify.garbage;
import com.sun.management.GarbageCollectionNotificationInfo;
import javax.management.Notification;
import javax.management.NotificationFilter;
/**
* 过滤器,JVM会将所有类型的通知都发送事件过来
* 此处将过滤掉除垃圾回收通知之外的其他所有通知信息
*/
public class GarbageNotificationFilter implements NotificationFilter {
/**
* 除了类型为垃圾回收之外的通知都不会接收
*
* @param notification 通知
* @return
*/
public boolean isNotificationEnabled(Notification notification) {
return GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION.equals(notification.getType());
}
}
注册事件
package com.peipei.jvm.notify.garbage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.management.NotificationEmitter;
import javax.management.NotificationFilter;
import javax.management.NotificationListener;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.util.List;
public class GarbageNotificationRegister {
private static final Logger logger = LoggerFactory.getLogger(GarbageNotificationRegister.class);
// 获取运行JVM的垃圾回收器相关的管理的bean
private static List<GarbageCollectorMXBean> garbageCollectorMXBeanList = ManagementFactory.getGarbageCollectorMXBeans();
/**
* 初始化,注册垃圾回收的监听器
*/
public static void init (){
for (GarbageCollectorMXBean bean : garbageCollectorMXBeanList){
// 发射器,发生GC时,发射一个通知到监听器中
NotificationEmitter emitter = (NotificationEmitter)bean;
// 创建监听器对象
NotificationListener listener = new GarbageNotificationListener();
// 创建过滤器对象
NotificationFilter filter = new GarbageNotificationFilter();
emitter.addNotificationListener(listener, filter, bean);
}
}
}
启动程序
package com.peipei.jvm.notify.garbage.main;
import com.peipei.jvm.notify.garbage.GarbageNotificationRegister;
import java.util.ArrayList;
import java.util.List;
public class Main {
private static final List<byte[]> list = new ArrayList();
static {
GarbageNotificationRegister.init();
}
public static void main(String[] args) {
while (true){
list.add(new byte[1024 * 1024 * 5]);
Runtime.getRuntime().gc();
}
}
}
观擦控制台不断的打印GC