JAVA虚拟机学习实践2-实践

来源:互联网 发布:黑客帝国动画版 知乎 编辑:程序博客网 时间:2024/05/21 02:20

OOM异常

Java堆溢出

Java堆用于存储对象实例,我们只有不断地创建对象,在对象数量达到最大堆的容量限制后就会产生举出异常。

代码:

public class HeapOOM {
    static class OOMObiect {
    }

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

将上述代码编码后,用C:\Users\ai-wanxy3\Desktop\java>java -Xmx20m -Xms20m -XX:+HeapDumpOnOutOfMemoryError  HeapOOM执行代码,其中-Xmx设置Java堆最大20m,-Xms设置Java堆最小20m,-XX:+HeapDumpOnOutOfMemoryrror将内存溢出时的内存堆转存为快照以便进行事后分析,执行结果如图2-1


                                                                                          图2-1

Java堆异常时,首先出现"java.lang.OutOfMemoryError",然后后面紧跟"Java heap space",并生成dump文件。

要解决这个区域的问题,首先要通过内存分析工具,如ibm Heapanalyzer重点是确认内存中和对象是否都是必要的,也就是清楚认识到到底是内存溢出还是内存泄露。如果不存在泄露,也就是内存中的对象确实都还需要活着,就应该考虑增加Java堆的大小了,-Xmx设置最大值,-Xms设置最小值。


虚拟机栈和本地方法区溢出

关于虚拟机栈和本地方法区栈,在Java虚拟机规范中描述了两种异常:

1、如果线程请求的栈深度大于虚拟机允许的最大深度,将抛出StackOverflowError异常。

2、如果虚拟机在扩展栈深度时无法申请到足够多的内存,将抛出OutOfMemoryError异常。

在测试该内存区的异常时,使用-Xss参数减少栈内存的容量,将抛出StackOverflowError异常,代码如下:

public class JavaVMStackOF {

    private int stackLength = 1;

    public void stackLeak() {
        stackLength++;
        int i = 0;
        while (i < 10000) {
            Object o = new Object();
            i++;
        }
        stackLeak();
    }

    /**
     * @param args
     */
    public static void main(String[] args) throws Throwable{
        // TODO Auto-generated method stub
        JavaVMStackOF oom = new JavaVMStackOF();
        try {
            oom.stackLeak();
        } catch (Throwable t) {
            System.out.println("stack length:" + oom.stackLength);
            throw t;
        }
    }
}

使用命令:java -Xss1k   JavaVMStackOF,执行时将报错:

stack length:21042

Exception in thread "main" java.lang.StackOverflowError

at JavaVMStackOF.stackLeak(JavaVMStackOF.java:20)

出现StackOverflowError异常时有错误堆栈信息可以阅读,比较容易找到问题所在。而且使用虚拟机默认配置,栈深度在大多数情况下达到1000-2000是没有问题的,对正常的方法调用,完全没有问题。


运行时常量池溢出


常量池位位于方法区内,可以通过 -XX:PermSize和-XX:MaxPermSize限制方法区的大小,间接限制常量区的大小。

如果要向常量池中添加内容,最简单的方法是使用String.intern()方法:如果池中包含了一个等于此String对象的字符串,则返回这个对象,否则将此对象添加到常量池中,并且返回此对象的引用,代码如下:

import java.util.ArrayList;
import java.util.List;

/**
 * @author ai-wanxy3
 *
 */
public class ConstantPoolOOM {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        List<String> list = new ArrayList<String>();
        int i = 0;
        while (true) {
            list.add(String.valueOf(i++).intern());
        }
}

使用命令:java -XX:MaxPermSize=1m ConstantPoolOOM执行代码,得到如下错误信息:

Exception in thread "main"  java.lang.OutofMenoryError: PerMGen space


方法区溢

方法区用于存放Class的相关信息,如类名,访问修饰符、常量池、属性描述、方法描述等,对这个区域的描述,思路是生成大量的类去填满这个区域,直接溢出。与测试常量池溢出时类似。

方法区溢出是一种常见的内存溢出,一个类要被垃圾收集器回收,判断条件是相当苛刻的,特别是在经常动态生成大量Class文件的应用中,特别需要注意类的回收状况。



本机直接内存区溢

DirectMemory容量可通过-XX:MaxDirectMemorySize=128m指定,如果不指定,则默认与-Xmx(Java堆大小),通过减少直接内存的值,在测试时报OutofMemoryError错误。





0 0
原创粉丝点击