关于JVM案例分析(二)

内存分析篇

VisualVM 通过检测 JVM 中加载的类和对象信息等帮助我们分析内存使用情况,我们可以通过 VisualVM 的监视标签对应用程序进行内存分析。

一.内存堆Heap

首先我们来看内存堆Heap使用情况,我本机eclipse的进程在visualVM显示如下:

随便写个小程序占用内存大的,运行一下

程序如下:

复制代码
package jvisualVM;

public class JavaHeapTest {
    public final static int OUTOFMEMORY = 200000000;
    
    private String oom;

    private int length;
    
    StringBuffer tempOOM = new StringBuffer();

    public JavaHeapTest(int leng) {
        this.length = leng;
       
        int i = 0;
        while (i < leng) {
            i++;
            try {
                tempOOM.append("a");
            } catch (OutOfMemoryError e) {
               e.printStackTrace();
               break;
            }
        }
        this.oom = tempOOM.toString();

    }

    public String getOom() {
        return oom;
    }

    public int getLength() {
        return length;
    }

    public static void main(String[] args) {
        JavaHeapTest javaHeapTest = new JavaHeapTest(OUTOFMEMORY);
        System.out.println(javaHeapTest.getOom().length());
    }

}
复制代码

查看VisualVM Monitor tab, 堆内存变大了

在程序运行结束之前, 点击Heap Dump 按钮, 等待一会儿,得到dump结果,可以看到一些Summary信息

点击Classes, 发现char[]所占用的内存是最大的

双击它,得到如下Instances结果

 Instances是按Size由大到小排列的

第一个就是最大的, 展开Field区域的 values

StringBuffer类型的 全局变量 tempOOM 占用内存特别大, 注意局部变量是无法通过 堆dump来得到分析结果的。

另外,对于“堆 dump”来说,在远程监控jvm的时候,VisualVM是没有这个功能的,只有本地监控的时候才有。

二.永久保留区域PermGen

其次来看下永久保留区域PermGen使用情况

运行一段类加载的程序,代码如下:

复制代码
package jvisualVM;

import java.io.File;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;

public class TestPermGen {
    
    private static List<Object> insList = new ArrayList<Object>();

    public static void main(String[] args) throws Exception {

        permLeak();
    }

    private static void permLeak() throws Exception {
        for (int i = 0; i < 1000; i++) {
            URL[] urls = getURLS();
            URLClassLoader urlClassloader = new URLClassLoader(urls, null);
            Class<?> logfClass = Class.forName("org.apache.commons.logging.LogFactory", true,urlClassloader);
            Method getLog = logfClass.getMethod("getLog", String.class);
            Object result = getLog.invoke(logfClass, "TestPermGen");
            insList.add(result);
            System.out.println(i + ": " + result);
        }
    }

    private static URL[] getURLS() throws MalformedURLException {
        File libDir = new File("C:/Users/wadexu/.m2/repository/commons-logging/commons-logging/1.1.1");
        File[] subFiles = libDir.listFiles();
        int count = subFiles.length;
        URL[] urls = new URL[count];
        for (int i = 0; i < count; i++) {
            urls[i] = subFiles[i].toURI().toURL();
        }
        return urls;
    }

    
}
复制代码

一个类型装载之后会创建一个对应的java.lang.Class实例,这个实例本身和普通对象实例一样存储于堆中,我觉得之所以说是这是一种特殊的实例,某种程度上是因为其充当了访问PermGen区域中类型信息的代理者。

运行一段时间后抛OutOfMemoryError了, VisualVM监控结果如下:

结论:PermGen区域分配的堆空间过小,我们可以通过设置-XX: PermSize参数和-XX:MaxPermSize参数来解决。

原文地址:https://www.cnblogs.com/ZJOE80/p/12298893.html