java虚拟机二之溢出实践

1.java堆溢出

看代码:

注意,java_opts要设置为:如果不懂java_opts,自己百度去。

-server -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/test/ 

-Xms  堆的最小内存,我们这儿设置为20M

-Xmx  堆的最大内存,我们这儿也设置为20M

-XX:+HeapDumpOnOutOfMemoryError  设置这个是为了当出现内存溢出时系统自动生成堆的快照,这个快照的主要作用就是为了分析内存溢出异常的原因

-XX:HeapDumpPath=D:/test/  生成快照的地址,我的是在windows系统下的

/**
 * Created by 
 * Date : 2018/7/24 9:53
 * 堆内存溢出
 */
public class HeapMain {
    static class HeapObject{

    }
    public static void main(String[] args){
        List<HeapObject> list=new ArrayList<>();
        while(true){
            list.add(new HeapObject());
        }
    }
}

执行结果:

Exception in thread "main" 
java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:2245)
    at java.util.Arrays.copyOf(Arrays.java:2219)
    at java.util.ArrayList.grow(ArrayList.java:242)
    at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:216)
    at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:208)
    at java.util.ArrayList.add(ArrayList.java:440)
    at outofmemory.HeapMain.main(HeapMain.java:17)

这段代码很简单,就是不停的创建对象,当创建的对象超出所设置的内存时,则报堆溢出异常。我们可以看到有“Java heap space”这么一句说明,这个就是说明堆空间溢出。

当系统报这个异常时,我们如何分析代码,找出内存溢出的原因?“深入理解java虚拟机”这本书推荐的是java自带的工具VisualVm或者它自带的命令,但是我用了,感觉好难用,用的有点卡。命令就更不用说了,打起来很烦琐。我个人推荐使用MemoryAnalyzer这个工具,仅仅是个人推荐。这个工具你可以集成在Eclipse上使用,也可以独立使用。我用的是idea开发工具,所以我独立使用这个工具。

我用的下载地址:http://www.eclipse.org/downloads/download.php?file=/mat/1.7/rcp/MemoryAnalyzer-1.7.0.20170613-win32.win32.x86_64.zip&mirror_id=1275

这个地址等各位小伙伴看到的时候也不知道能不能用,不能用就到celipse官网上下载去。不过这个工具对java有版本之分,我这个是用在windows上的64位的java7。

上述代码运行报异常后,在D:/test/ (这个是我自己设置的生成快照的地址)下找到快照文件,后缀名是hprof的文件。

然后使用下载的工具打开这个文件。

图中Total:15.9M,墨绿色最大的占了15.5M,我们点击墨绿色,然后点击左上角的排第三个图标,

我们发现排列在最上面的一行数据内存占比为97.37%,然后展开,发现全是HeapMain下的HeapObject,

回到工具的主页面,再次点击最大的墨绿色,在弹出的工具栏中,我们选择“Java Basics==>thread details”,

我们找到HeapMain.java的第17行,

while(true){

    list.add(new HeapObject());

}

现在问题已经显而易见了,就是这儿创建了很多个HeapObject造成的内存溢出。要想改掉这个问题,要么增大设置的内存,要么优化代码,不要循环添加这么多对象。

2.虚拟机栈与本地方法栈溢出

这两个内存空间都有两个异常,一个是栈溢出异常,一个是内存溢出异常,内存溢出异常不知道怎么实现,“深入理解java虚拟机”这本书关于内存溢出异常的是用线程举的例子,但我个人感觉,这样产生的内存溢出和栈空间大小没有关系,反而是和创建线程所需要的内存有关。所以如果有知道的与栈空间大小有关系的造成内存溢出异常的例子,可以在评论区留下代码给我哈。

下面是栈溢出的例子:

/**
 * Created by 
 * Date : 2018/7/24 13:54
 * 虚拟栈与本地方法栈,栈溢出异常
 */
public class StackMain {
    private int i=0;
    public void test(){
        i++;
        test();
    }
    public static void main(String[] args){
        StackMain sm=new StackMain();
        try{
            sm.test();
        }catch (Throwable e){
            System.out.println("栈深度:"+sm.i);
            throw e;
        }
    }
}

执行结果:

栈深度:995
Exception in thread "main" java.lang.StackOverflowError
    at outofmemory.StackMain.test(StackMain.java:11)
    at outofmemory.StackMain.test(StackMain.java:12)
    at outofmemory.StackMain.test(StackMain.java:12)
    at outofmemory.StackMain.test(StackMain.java:12)
    at outofmemory.StackMain.test(StackMain.java:12)

这儿就报了栈溢出异常,实现的方法也很简单,就是在方法中调用自己的方法。

栈溢出异常不需要工具的解析,看到程序抛出的异常就能知道错在哪了。

3.方法区溢出

通过前一章节,我们知道方法区主要存储的是关于类的信息。因为要想方法区溢出,我们就无限的创建类。我们通过cglib来创建,不过关于cglib的jar包,我试了好多版本,基本上都有冲突,我试的没有冲突的版本是cglig-3.2.5.jar与asm-5.2.jar,当然直接用maven就没有版本冲突的问题了,不过我本人比较懒,所以直接找了jar包来测试。

测试代码如下:

注意,java_opts要设置为:

-server -XX:PermSize=10M -XX:MaxPermSize=10M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/test/ 

-XX:PermSize  方法区内存,我们这儿设置为10M

-XX:MaxPermSize  方法区最大内存,我们这儿也设置为10M

/**
 * Created by 
 * Date : 2018/7/24 14:34
 * 方法区内存溢出
 */
public class MethodMain {
    public static void main(String[] args){
        while(true){
            Enhancer enhancer=new Enhancer();
            enhancer.setSuperclass(MethodObject.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                    return methodProxy.invokeSuper(o,objects);
                }
            });
            enhancer.create();
        }
    }
    static class MethodObject{

    }
}

执行结果:

java.lang.OutOfMemoryError: PermGen space
Dumping heap to D:/test/\java_pid3088.hprof ...
Heap dump file created [3871825 bytes in 0.019 secs]
Exception in thread "main" 

Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "main"

其中“PermGen space”表示方法区溢出。我们这儿也可以使用工具来分析方法区溢出的原因,和前面的使用方法一样,在此就不重复罗嗦了。

4.本机直接内存溢出

注意,java_opts要设置为:

-server -Xmx20M -XX:MaxDirectMemorySize=10M -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=D:/test/ 

-Xmx  堆的最大内存

-XX:MaxDirectMemorySize  本机最大直接内存,如果不指定此属性,则会以堆的最大内存为本机最大直接内存

/**
 * Created by 
 * Date : 2018/7/25 15:03
 * 本机直接内存溢出
 */
public class DirectMain {

    public static void main(String[] args)throws Exception{
        Field unsafeField=Unsafe.class.getDeclaredFields()[0];
        unsafeField.setAccessible(true);
        Unsafe unsafe=(Unsafe) unsafeField.get(null);
        while(true){
            unsafe.allocateMemory(1024*1024);
        }
    }
}

执行结果:

Exception in thread "main" java.lang.OutOfMemoryError
    at sun.misc.Unsafe.allocateMemory(Native Method)
    at outofmemory.DirectMain.main(DirectMain.java:18)

执行完溢出异常,我并没有看到生成堆溢出的快照。在错误信息中,我们也没看到“PermGen space”,“Heap space”等相似的信息。如果我们在程序中发现有此异常,并且在程序中直接或间接的使用了NIO,那我们就要检查一下是不是本机直接内存溢出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值