• 欢迎访问开心洋葱网站,在线教程,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站,欢迎加入开心洋葱 QQ群
  • 为方便开心洋葱网用户,开心洋葱官网已经开启复制功能!
  • 欢迎访问开心洋葱网站,手机也能访问哦~欢迎加入开心洋葱多维思维学习平台 QQ群
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏开心洋葱吧~~~~~~~~~~~~~!
  • 由于近期流量激增,小站的ECS没能经的起亲们的访问,本站依然没有盈利,如果各位看如果觉着文字不错,还请看官给小站打个赏~~~~~~~~~~~~~!

JVM 中的异常

JAVA相关 开心洋葱 2411次浏览 0个评论

内存溢出(OOM)和内存泄漏

内存溢出:无法为对象进行空间分配(垃圾回收也没用)就会导致内存溢出。

内存泄漏:对于程序不会再用到的对象,垃圾回收器无法将其回收。

两者联系:发生了内存泄漏后,可能会导致内存溢出。

 

StackOverflowError(栈溢出)

在 JVM 的栈中,栈的大小可以是固定的,也可以是动态变化的。如果采用固定大小的栈,那么如果线程要创建的栈帧大小大于栈容量的大小时,就会抛出 java.lang.StackOverflowError。比如下面的代码

public class StackErrorTest {

    public static void main(String[] args) {
        main(args);
    }
}

无限递归,那么就会不停的创建栈帧,最终撑爆栈空间,抛出栈移除异常。

 

栈上的OOM

如果栈是动态扩展的话,那么在拓展时无法申请到足够的内存,或者在创建新的线程时没有足够的内存去创建对应的虚拟机栈,那么栈就会抛出 OutOfMemoryError(OOM) 异常。

 

OOM:Java heap space

堆内存溢出,当堆空间不足以存放创建的对象时就会发生堆异常。具体模拟方式可以参见下面代码:

public class OverHeadOOM {
    public static void main(String[] args){
        int i = 0;
        List<String> list = new ArrayList<>();
        try {
            while (true){
                list.add(String.valueOf(++i));
            }
        } catch (Exception e) {
            System.out.println(i);
            e.printStackTrace();
        }
    }

}

为了让结果更快地展示出来,可以把堆空间大小调小一些:-Xms8m -Xmx8m

 

OOM:GC overhead limit exceeded

这的发生的原因和上面 Java heap space 差不多,上面是堆空间不足,这个是还未达到堆空间不足,但是超过 98% 的时间用来做 GC 并且回收了不到 2% 的堆内存,这时就会立刻触发当前的异常。

如果以上面的例子来看,如果将堆空间参数设置为 -Xms10m -Xmx10m。就会发生当前异常。

 

OOM:Direct buffer memory

直接内存溢出。

直接内存是 JVM 向系统申请的内存,由于其是系统内存,所以在 io 时没有状态切换和不必要的数据拷贝,所以相比于非直接内存的 io 执行效率会更高。JDK8 中方法区的实现元空间也是属于直接内存。

在使用 nio 进行缓冲区的定义时,一般是 Buffer.allocate() 来定义的,这种方式是在 JVM 内存中定义空间作为缓冲区的,执行效率也较低;使用 Buffer.allocateDirect() 就是在本地内存中定义的。如果本地内存的可用空间不足以支撑需要分配的空间,就会排除 Direct buffer memory 的异常。具体演示案例可以执行下面代码:

public class DirectBufferOOM {

    public static void main(String[] args){
        System.out.println("最大直接内存大小" + (sun.misc.VM.maxDirectMemory()/1024/1024) + "MB");
        ByteBuffer.allocateDirect(20*1024*1024);
    }
}

执行前需要将直接内存的大小设置为 6m :-XX:MaxDirectMemorySize=6m。

 

OOM:unable to create new native thread

当前应用程序创建过多的线程,超过设置的限制,就会抛出异常。这个异常一般是在 linux 环境下产生的,windows 下默认是无限制的,linux 下非 root 用户默认为 1024 个,执行下面代码就会抛出此异常。

public class UnableCreateNewThreadDemo {
    public static void main(String[] args) {
        for(int i = 1; ;i++){
            System.out.println("i=" + i);
            new Thread(()->{
                try { Thread.sleep(Integer.MAX_VALUE); }catch(Exception e) {e.printStackTrace();}
            },""+i).start();
        }
    }
}

如果想要提高上限,除了切换 root 用户外,还可以编辑 /etc/security/limits.d/90-nproc.conf ,增加当前用户的名字,为其设置可以创建的线程数

JVM 中的异常

 

OOM:Metaspace

元空间空间不足。因为在 JDK8 开始方法区实现变成了元空间,所以当创建了过多的类时,就会抛出这个异常。

触发案例:

public class MetaspaceOOM {
    static class OOMTest{}
    public static void main(String[] args){
        int i = 0;
        try {
            while (true){
                i++;
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(OOMTest.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, args);
                    }
                });
                enhancer.create();
            }
        } catch (Throwable throwable) {
            System.out.println("执行了" + i + "次");
            throwable.printStackTrace();
        }
    }
}

Enhancer 是 Spring cglib中用于生成动态代理的类,可以为未实现接口的类创建代理。在上面代码中就是通过 Enhancer 不停地创建代理对象(创建代理对象的同时也会将代理类加载到方法区中)来模拟元空间不足的场景。为了现象更明显,可以将元空间大小设置得小一些:-XX:MetaspaceSize=15m -XX:MaxMetaspaceSize=15m。


开心洋葱 , 版权所有丨如未注明 , 均为原创丨未经授权请勿修改 , 转载请注明JVM 中的异常
喜欢 (0)

您必须 登录 才能发表评论!

加载中……