GC监控
垃圾回收收集监控指的是搞清楚JVM如何执行GC的过程,例如,我们可以查明:
-
- 何时一个新生代中的对象被移动到老年代时,所花费的时间。
-
- Stop-the-world 何时发生的,持续了多长时间。
GC监控是为了鉴别JVM是否在高效地执行GC,以及是否有必要进行额外的性能调优。 基于以上信息,我们可以修改应用程序或者调整GC算法(GC优化)。
如何设置JAVA程序运行时可以被连接分析
本地访问
-Dcom.sun.management.jmxremote #是否支持远程JMX访问,默认true
-Dcom.sun.management.jmxremote.port=2990 #监听端口号,方便远程访问
-Dcom.sun.management.jmxremote.ssl=false #是否对连接开启SSL加密,默认开启
-Dcom.sun.management.jmxremote.authenticate=false #是否需要开启用户认证,默认开启
上述仅限于你在本地访问
远程访问
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=2990
#rmi端口,可以将这个端口和jmx.port的端口设置成一个端口
-Dcom.sun.management.jmxremote.rmi.port=2990
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
#支持远程访问协议,也就是前面描述的JMX Connector
-Djava.rmi.server.hostname=服务器的IP地址或者域名
jconsole
启动
#运行
jconsole
分析界面
分析界面有六个标签
- 概述: Displays overview information about the Java VM and monitored values.
- 内存: 显示内存使用信息
- 线程: 显示线程使用信息
- 类: 显示类装载信息
- VM摘要: 显示java VM信息
- MBeans: 显示 MBeans.
对着图点击右键可以保存数据到CSV文件,以后可以使用其他工具来分析这些数据。
内存
查看堆内存,非堆内存,内存池的状况总体内存的分配和使用情况以及不同的GC进行垃圾回收的次数和时间。可以手动进行GC查看内存变化。
线程
类
VM摘要
MBean
jvisualvm
jvisualvm
是jconsole
的升级版
启动
#运行
jvisualvm
插件安装
tools->plugin->Available Plugin 会有值得安装的插件,如:VisualGC JConsole
VisualGC JConsole
是监控GC的插件
注意
要使用VisualGC
必须在远程机上启动jstatd
代理程序,否则会显示 “not supported for this jvm” 错误
而启动 jstatd 时会有一个权限问题,需要做如下修改:
vi JAVA_HOME/jre/lib/security/java.policy
#输入内容
grant codebase "file:${java.home}/../lib/tools.jar" {
permission java.security.AllPermission;
};
然后启动 jstatd并且不要关闭。
JMX
利用JMXConnector
获取信息。对照jconsole
的MBean.
package com.seasungames.stress.jconsole;
import com.seasungames.stress.jconsole.model.GCInfo;
import com.seasungames.stress.jconsole.model.JVMInfo;
import com.seasungames.stress.jconsole.model.MermoryPoolInfo;
import javax.management.MBeanServerConnection;
import javax.management.ObjectName;
import javax.management.openmbean.CompositeDataSupport;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
public class JConsole {
private MBeanServerConnection mbsc;
private int ncpu = 1;
private long sample;
private String host;
private String port;
private String dir;
private boolean stop;
private long jvmStartTime;
private Map<String, String> memoryPoolMap;
private Map<String, String> gcMap;
private List<JVMInfo> jvmInfos;
private JMXConnector jmxc;
private long connectTime;
public JConsole(String host, String port, int sample) {
this.host = host;
this.port = port;
this.sample = sample;
String urlStr = "service:jmx:rmi:///jndi/rmi://" + host + ":" + port + "/jmxrmi";
JMXServiceURL url = null;
try {
url = new JMXServiceURL(urlStr);
jmxc = JMXConnectorFactory.connect(url, null);
mbsc = jmxc.getMBeanServerConnection();
connectTime = System.currentTimeMillis();
ncpu = getAvailableProcessors();
memoryPoolMap = new HashMap<>();
gcMap = new HashMap<>();
init();
} catch (Exception e) {
e.printStackTrace();
}
}
private void init() throws Exception {
getObjectNames();
jvmStartTime = getJVMSrartTime();
jvmInfos = new ArrayList<>();
}
public void listen() {
LogThread li = new LogThread();
new Thread(li).start();
}
private void jvmLog() throws Exception {
for (; ; ) {
if (stop) {
break;
}
try {
JVMInfo info = getJVMInfo();
jvmInfos.add(info);
} catch (IOException e) {
if (!stop) {
System.err.println("Exception : " + e.getMessage());
}
} catch (Exception e) {
System.err.println("Exception : " + e.getMessage());
} finally {
}
}
}
/**
*获得所有的ObjectName
*保存GC的name和memoryPool的name
**/
private void getObjectNames() throws Exception {
Set<ObjectName> objectNames = new TreeSet<ObjectName>(mbsc.queryNames(null, null));
for (ObjectName name : objectNames) {
String mpName = name.toString();
if (mpName.startsWith(Constant.JAVA_LANG_TYPE_MEMORY_POOL)) {
String[] names = mpName.split(",");
String attrName = names[1].replace("name=", "");
if (attrName.endsWith(Constant.MEMORY_POOL_CODE_CACHE)) {
memoryPoolMap.put(Constant.MEMORY_POOL_CODE_CACHE, attrName);
} else if (attrName.endsWith(Constant.MEMORY_POOL_COMPRESSED_CLASS_SPACE)) {
memoryPoolMap.put(Constant.MEMORY_POOL_COMPRESSED_CLASS_SPACE, attrName);
} else if (attrName.endsWith(Constant.MEMORY_POOL_EDEN_SPACE_END)) {
memoryPoolMap.put(Constant.MEMORY_POOL_EDEN_SPACE_END, attrName);
} else if (attrName.endsWith(Constant.MEMORY_POOL_METASPACE)) {
memoryPoolMap.put(Constant.MEMORY_POOL_METASPACE, attrName);
} else if (attrName.endsWith(Constant.MEMORY_POOL_OLD_GEN_END)) {
memoryPoolMap.put(Constant.MEMORY_POOL_OLD_GEN_END, attrName);
} else if (attrName.endsWith(Constant.MEMORY_POOL_SURVIVOR_SPACE_END)) {
memoryPoolMap.put(Constant.MEMORY_POOL_SURVIVOR_SPACE_END, attrName);
}
}
if (mpName.startsWith(Constant.JAVA_LANG_TYPE_GARBAGE_COLLECTOR)) {
String[] names = mpName.split(",");
String attrName = names[1].replace("name=", "");
for (String key : Constant.YOUNG_GC) {
if (attrName.endsWith(key)) {
gcMap.put(Constant.GC_MAP_KEY_YOUNG, attrName);
break;
}
}
for (String key : Constant.OLD_GC) {
if (attrName.endsWith(key)) {
gcMap.put(Constant.GC_MAP_KEY_OLD, attrName);
break;
}
}
}
}
}
/**
*获取jvm信息
**/
private JVMInfo getJVMInfo() throws Exception {
JVMInfo info = new JVMInfo();
BigDecimal cpu = new BigDecimal(getCpuUsage());
info.setCpu(cpu.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue());
info.setHeapUsage(getHeapUsage());
info.setNonHeapUsage(getNonHeapUsage());
info.setLoadedClassCount(getLoadedClassCount());
info.setThreadCount(getThreadCount());
info.setTotalCompilationTime(getTotalCompilationTime());
info.setMermoryPool(getMermoryPoolInfo());
info.setGcInfo(getGCInfo());
info.setTime(System.currentTimeMillis());
return info;
}
private long getJVMSrartTime() throws Exception {
ObjectName mbeanName;
mbeanName = new ObjectName(Constant.JAVA_LANG_TYPE_RUNTIME);
return (Long) mbsc.getAttribute(mbeanName, "StartTime");
}
public double getCpuUsage() throws Exception {
long c, u;
double ec, eu;
c = getOSProcessCpuTime();
u = getUpTime();
Thread.sleep(sample);
ec = (getOSProcessCpuTime() - c) / 1000000;
eu = (getUpTime() - u);
if (eu == 0) {
return 0;
}
return (ec / (eu * ncpu)) * 100;
}
private int getAvailableProcessors() throws Exception {
ObjectName mbeanName;
mbeanName = new ObjectName(Constant.JAVA_LANG_TYPE_OPERATINGSYSTEM);
return (Integer) mbsc.getAttribute(mbeanName, "AvailableProcessors");
}
private long getUpTime() throws Exception {
ObjectName mbeanName;
mbeanName = new ObjectName(Constant.JAVA_LANG_TYPE_RUNTIME);
return (Long) mbsc.getAttribute(mbeanName, "Uptime");
}
private long getOSProcessCpuTime() throws Exception {
ObjectName mbeanName;
mbeanName = new ObjectName(Constant.JAVA_LANG_TYPE_OPERATINGSYSTEM);
return (Long) mbsc.getAttribute(mbeanName, "ProcessCpuTime");
}
private double getHeapUsage() throws Exception {
ObjectName mbeanName;
mbeanName = new ObjectName(Constant.JAVA_LANG_TYPE_MEMORY);
CompositeDataSupport o;
o = (CompositeDataSupport) mbsc.getAttribute(mbeanName, "HeapMemoryUsage");
double ret = (double) ((Long) o.get("used")) / Constant.BYTE_2_M;
BigDecimal used = new BigDecimal(ret);
return used.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
}
private long getTotalCompilationTime() throws Exception {
ObjectName mbeanName;
mbeanName = new ObjectName(Constant.JAVA_LANG_TYPE_COMPILATION);
return (Long) mbsc.getAttribute(mbeanName, "TotalCompilationTime");
}
private double getNonHeapUsage() throws Exception {
ObjectName mbeanName;
mbeanName = new ObjectName(Constant.JAVA_LANG_TYPE_MEMORY);
CompositeDataSupport o;
o = (CompositeDataSupport) mbsc.getAttribute(mbeanName, "NonHeapMemoryUsage");
double ret = (double) ((Long) o.get("used")) / Constant.BYTE_2_M;
BigDecimal used = new BigDecimal(ret);
return used.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
}
private int getThreadCount() throws Exception {
ObjectName mbeanName;
mbeanName = new ObjectName(Constant.JAVA_LANG_TYPE_THREADING);
return (Integer) mbsc.getAttribute(mbeanName, "ThreadCount");
}
private int getLoadedClassCount() throws Exception {
ObjectName mbeanName;
mbeanName = new ObjectName(Constant.JAVA_LANG_TYPE_CLASSLOADING);
return (Integer) mbsc.getAttribute(mbeanName, "LoadedClassCount");
}
private String getBean(String objectName, String attribute0, String attribute1) throws Exception {
ObjectName mbeanName;
mbeanName = new ObjectName(objectName);
CompositeDataSupport o;
if (attribute1 == null) {
Object obj = mbsc.getAttribute(mbeanName, attribute0);
return obj.toString();
}
o = (CompositeDataSupport) mbsc.getAttribute(mbeanName, attribute0);
return o.get(attribute1).toString();
}
private final void getDeadLockedThreads() throws Exception {
ObjectName mbeanName;
mbeanName = new ObjectName(Constant.JAVA_LANG_TYPE_THREADING);
long[] dl = (long[]) mbsc.invoke(mbeanName, "findDeadlockedThreads", null, null);
StringBuilder sb;
if (dl != null) {
sb = new StringBuilder();
sb.append("Dead Lock Detected - Host:");
sb.append(host);
sb.append("\n");
for (int i = 0; i < dl.length; i++) {
sb.append("Thread " + dl[i] + "\n");
}
}
}
private MermoryPoolInfo getMermoryPoolInfo() throws Exception {
MermoryPoolInfo info = new MermoryPoolInfo();
for (String key : memoryPoolMap.keySet()) {
MermoryPoolInfo.MermoryPoolItemInfo itemInfo = new MermoryPoolInfo.MermoryPoolItemInfo();
itemInfo.setName(key);
itemInfo.setUsage(getMemoryPoolItem(memoryPoolMap.get(key)));
if (key.equals(Constant.MEMORY_POOL_CODE_CACHE)) {
info.setCodeCache(itemInfo);
} else if (key.equals(Constant.MEMORY_POOL_COMPRESSED_CLASS_SPACE)) {
info.setCompressedClassSpace(itemInfo);
} else if (key.equals(Constant.MEMORY_POOL_EDEN_SPACE_END)) {
info.setEdenSpace(itemInfo);
} else if (key.equals(Constant.MEMORY_POOL_METASPACE)) {
info.setMetaspace(itemInfo);
} else if (key.equals(Constant.MEMORY_POOL_OLD_GEN_END)) {
info.setOldGen(itemInfo);
} else if (key.equals(Constant.MEMORY_POOL_SURVIVOR_SPACE_END)) {
info.setSurvivorSpace(itemInfo);
}
}
return info;
}
private double getMemoryPoolItem(String attrName) throws Exception {
ObjectName mbeanName;
mbeanName = new ObjectName(Constant.JAVA_LANG_TYPE_MEMORY_POOL + ",name=" + attrName);
CompositeDataSupport o;
o = (CompositeDataSupport) mbsc.getAttribute(mbeanName, "Usage");
double ret = (double) ((Long) o.get("used")) / Constant.BYTE_2_M;
BigDecimal used = new BigDecimal(ret);
return used.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
}
private GCInfo getGCInfo() throws Exception {
GCInfo info = new GCInfo();
for (String key : gcMap.keySet()) {
GCInfo.GCItemInfo itemInfo = new GCInfo.GCItemInfo();
String attrName = gcMap.get(key);
ObjectName mbeanName = new ObjectName(Constant.JAVA_LANG_TYPE_GARBAGE_COLLECTOR + ",name=" + attrName);
itemInfo.setName(attrName);
long collectionCount = (Long) mbsc.getAttribute(mbeanName, "CollectionCount");
itemInfo.setCollectionCount(collectionCount);
long collectionTime = (Long) mbsc.getAttribute(mbeanName, "CollectionTime");
itemInfo.setCollectionTime(collectionTime);
GCInfo.LastGcInfo lastGcInfo = new GCInfo.LastGcInfo();
CompositeDataSupport lastGc = (CompositeDataSupport) mbsc.getAttribute(mbeanName, "LastGcInfo");
int gcThreadCount = (Integer) lastGc.get("GcThreadCount");
lastGcInfo.setGcThreadCount(gcThreadCount);
long duration = (Long) lastGc.get("duration");
lastGcInfo.setDuration(duration);
long id = (Long) lastGc.get("id");
lastGcInfo.setId(id);
long startTime = (Long) lastGc.get("startTime");
lastGcInfo.setStartTime(jvmStartTime + startTime);
long endTime = (Long) lastGc.get("endTime");
lastGcInfo.setEndTime(jvmStartTime + endTime);
itemInfo.setLastGcInfo(lastGcInfo);
if (key.equals(Constant.GC_MAP_KEY_YOUNG)) {
info.setYoungGC(itemInfo);
} else {
info.setOldGC(itemInfo);
}
}
return info;
}
public long getSample() {
return sample;
}
public void setSample(long sample) {
this.sample = sample;
}
public String getDir() {
return dir;
}
public void setDir(String dir) {
this.dir = dir;
}
class LogThread implements Runnable {
@Override
public void run() {
try {
jvmLog();
} catch (Exception ex) {
System.err.println("Thread Exception " + ex.getMessage());
}
}
}
public boolean isStop() {
return stop;
}
public boolean shutdown() {
this.stop = true;
try {
jmxc.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
return true;
}
public List<JVMInfo> getJvmInfos() {
return jvmInfos;
}
public void setJvmInfos(List<JVMInfo> jvmInfos) {
this.jvmInfos = jvmInfos;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getPort() {
return port;
}
public void setPort(String port) {
this.port = port;
}
public Map<String, String> getGcMap() {
return gcMap;
}
}
//================================================================================
package com.seasungames.stress.jconsole;
import java.util.HashSet;
import java.util.Set;
public class Constant {
public static String JAVA_LANG_TYPE_OPERATINGSYSTEM = "java.lang:type=OperatingSystem";
public static String JAVA_LANG_TYPE_MEMORY = "java.lang:type=Memory";
public static String JAVA_LANG_TYPE_MEMORY_POOL = "java.lang:type=MemoryPool";
public static String JAVA_LANG_TYPE_RUNTIME = "java.lang:type=Runtime";
public static String JAVA_LANG_TYPE_THREADING = "java.lang:type=Threading";
public static String JAVA_LANG_TYPE_GARBAGE_COLLECTOR = "java.lang:type=GarbageCollector";
public static String JAVA_LANG_TYPE_CLASSLOADING = "java.lang:type=ClassLoading";
public static String JAVA_LANG_TYPE_COMPILATION = "java.lang:type=Compilation";
public static String MEMORY_POOL_CODE_CACHE = "Code Cache";
public static String MEMORY_POOL_METASPACE = "Metaspace";
public static String MEMORY_POOL_EDEN_SPACE_END = "Eden Space";
public static String MEMORY_POOL_OLD_GEN_END = "Old Gen";
public static String MEMORY_POOL_COMPRESSED_CLASS_SPACE = "Compressed Class Space";
public static String MEMORY_POOL_SURVIVOR_SPACE_END = "Survivor Space";
public static String GC_PARNEW = "ParNew";
public static String GC_COPY = "Copy";
public static String GC_SCAVENGE = "Scavenge";
public static String GC_CONCURRENTMARKSWEEP = "ConcurrentMarkSweep";
public static String GC_MARKSWEEPCOMPACT = "MarkSweepCompact";
public static String GC_PS_MARKSWEEP = "PS MarkSweep";
public static Set<String> YOUNG_GC;
public static Set<String> OLD_GC;
public static String GC_MAP_KEY_YOUNG = "young";
public static String GC_MAP_KEY_OLD = "old";
public static int BYTE_2_M = 1024 * 1024;
static {
YOUNG_GC = new HashSet<>();
YOUNG_GC.add(GC_PARNEW);
YOUNG_GC.add(GC_COPY);
YOUNG_GC.add(GC_SCAVENGE);
OLD_GC = new HashSet<>();
OLD_GC.add(GC_CONCURRENTMARKSWEEP);
OLD_GC.add(GC_MARKSWEEPCOMPACT);
OLD_GC.add(GC_PS_MARKSWEEP);
}
}