java GC 学习(二)

"GC监控"

Posted by Jht on February 21, 2017

GC监控

垃圾回收收集监控指的是搞清楚JVM如何执行GC的过程,例如,我们可以查明:

    1. 何时一个新生代中的对象被移动到老年代时,所花费的时间。
    1. 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

start

分析界面

分析界面有六个标签

  • 概述: Displays overview information about the Java VM and monitored values.
  • 内存: 显示内存使用信息
  • 线程: 显示线程使用信息
  • 类: 显示类装载信息
  • VM摘要: 显示java VM信息
  • MBeans: 显示 MBeans.

start

对着图点击右键可以保存数据到CSV文件,以后可以使用其他工具来分析这些数据。

内存

查看堆内存,非堆内存,内存池的状况总体内存的分配和使用情况以及不同的GC进行垃圾回收的次数和时间。可以手动进行GC查看内存变化。

start

线程

start

start

VM摘要

start

MBean

start

jvisualvm

jvisualvmjconsole的升级版

启动

#运行
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);
    }

}