JVM---运行时区域异常OutOfMemoryError

这些异常最好不好出现,下面复现,只是为了让我们对这些异常有一个直观的认识

 

堆溢出

可以通过参数-XX:+PrintCommandLineFlags,查看堆初始值和最大值,若初始值过小,堆会进行自动扩容(扩容前会触发GC),频繁的GC或者扩容肯定是消耗时间的,因此看系统执行情况,合理配置初始堆大小

为复现堆OutOfMemoryError,可设置初始堆70M,即-Xms70m,最大堆大小70M,即-Xmx70m,程序代码

public class OutOfMemoryTest {
    public static void main(String[] args) {
        List<OutOfMemoryTest> list = new ArrayList<>();
        for (;;){
            list.add(new OutOfMemoryTest());
        }
    }
}

运行结果,显示JAVA堆溢出

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:3204)
    at java.util.Arrays.copyOf(Arrays.java:3175)
    at java.util.ArrayList.grow(ArrayList.java:246)
    at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:220)
    at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:212)
    at java.util.ArrayList.add(ArrayList.java:443)
    at main.JVMTEST.OutOfMemoryTest.main(OutOfMemoryTest.java:10)

引申,若将代码做如下修改,则不会出现OutOfMemoryError,这个是怎样造成的呢

public class OutOfMemoryTest {
    public static void main(String[] args) {
        List<OutOfMemoryTest> list = new ArrayList<>();
        for (;;){
            list.add(new OutOfMemoryTest());
            System.gc();
        }
    }
}

使用jvisualvm观察,发现堆空间好像都没怎么变动过,程序也没有出现OutOfMemoryError。实际上就是不断gc,还耗时。实际上长时间的话,迟早OutOfMemoryError。

对堆空间进行分析,可以设置-XX:+HeapDumpOnOutOfMemoryError生成内存转储快照,以方便后续进行分析(一般情形下不用设,如果出现堆OutOfMemoryError,而又无法直接观察出来,可以进行设置)。

这个地方需要指出,若在堆上看到OutOfMemoryError,需要我们确认时内存溢出还是内存泄露产生的,其实就是看对象是不是必须还存活者。比如这个list实际上是一个实例对象,但是使用完了之后一直不进行回收而占用堆空间(这种就是对象不是必须或者),这样可能就造成内存泄漏。别人博客让也写了OutOfMemoryError,且做了一个思维导图https://blog.csdn.net/baidu_37107022/article/details/88862371,我觉得还是可以借鉴的,我就不直接复制出来了。

 

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

Hot Spot将两个区域合二为一。设置参数如下:

-Xoss(设置本地方法栈大小),实际上无效

-Xss(设计栈容量)

JAVA虚拟机规范中描述了两种异常

OutOfMemoryError  栈无法申请到足够空间抛出(实现时,没看到出现,没有写例子)

StackOverFlowError  线程请求的栈深度大于虚拟机所允许的最大深度

vm参数-Xss80k

public class StackOverflowTest {
int length;
public int getLength(){
return this.length;
}

public void test(){
this.length++;
test();
}

public static void main(String[] args) {
StackOverflowTest stackOverflowTest = new StackOverflowTest();
try{
stackOverflowTest.test();
}catch (Throwable t){
System.out.println(stackOverflowTest.getLength());
t.printStackTrace();
}
}
}

 

 

方法区和运行时常量池溢出

jdk1.8之前

Hot Spot虚拟机将方法区使用永久代实现,可以设置-XX:PermSize和-XX:MaxPermSize设置(我机器上安装的时JDK1.8,我就不进行复现了)

jdk1.8往后

Hot Spot虚拟机将方法区使用元空间实现,可设置-XX:MaxMetaspaceSiz

vm参数设置:-XX:MaxMetaspaceSize=10m

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.8</version>
        </dependency>
public class MethodAreaOutOfMemoryError {
    public static void main(String[] args) {
        for(;;){
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(MethodAreaOutOfMemoryError.class);
            enhancer.setUseCache(false);
            enhancer.setCallback((MethodInterceptor)(obj, method, args1, proxy)->
                    proxy.invokeSuper(obj,args1)
            );
            enhancer.create();
        }
    }
}
Exception in thread "main" java.lang.OutOfMemoryError: Metaspace
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:340)
    at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:467)
    at net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:336)
    at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)
    at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:114)
    at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291)
    at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)
    at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)
    at outofmemoryerror.MethodAreaOutOfMemoryError.main(MethodAreaOutOfMemoryError.java:15)

 

本机直接内存溢出(并不属于虚拟机运行时区域,不过也有OutOfMemory)

可以通过-XX:maxDirectMemorySize指定,如果不指定默认与Java堆最大值(-Xmx)相同

VM参数-XX:MaxDirectMemorySize=500k或者-Xmx2m

public class DirectorBufferTest {
    private static final int _1MB =1024*1024;

    public static void main(String[] args) {
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_1MB);
    }
}
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
    at java.nio.Bits.reserveMemory(Bits.java:658)
    at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
    at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
    at main.JVMTEST.DirectorBufferTest.main(DirectorBufferTest.java:9)

 

 

 

 

友情链接:CGLIB https://blog.csdn.net/gyshun/article/details/81000997

元空间 https://www.infoq.cn/article/Java-permgen-Removed

内存OutOfMemoryhttps://blog.csdn.net/baidu_37107022/article/details/88862371

 

posted on 2020-04-20 21:51  xingshouzhan  阅读(200)  评论(0编辑  收藏  举报

导航