【Java虚拟机】常见故障及故障原因

13 篇文章 0 订阅

虚拟机中常见的故障及原因

1、StackOverFlowError (栈溢出)

当申请的栈的深度超过了虚拟机所允许的最大的栈的深度,就会报 StackOverFlowError。

最常见的情况就是没有出口的递归:

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

运行结果:
在这里插入图片描述

2、OutOfMemoryError: Java heap space (Java 堆空间内存泄漏)

Java堆空间不足以为新创建的对象分配内存时,就会报出 OutOfMemoryError: Java heap space

这也是很常见的一种故障情况,当创建一个超过JVM堆内存的大对象时或者一直创建对象直至堆空间饱满时,就会抛出此故障。

故障代码:

//设置虚拟机的最大栈空间和最小栈空间均为10m
// -Xms10m -Xmx10m
public static void main(String[] args) {
   //创建一个80M的大对象
   byte[] bytes = new byte[80 * 1024 * 1024];
   
}

运行结果:
在这里插入图片描述

3、OutOfMemoryError: GC overhead limit exceeded (超出GC开销限制)

GC回收时间过长会抛出 OutOfMemoryError: GC overhead limit exceeded。这里过长的定义是:超过98%的时间用来做GC,但却回收了不到%2的堆内存空间,连续多次GC都只回收了不到%2的极端情况才会抛出。如果不抛出 overhead limit exceeded ,那么GC回收出的内存马上又被填满,不得不再次触发GC,造成恶性循环。

错误代码:

//JVM启动配置参数如下:
//堆空间大小为10m		打印GC详情			直接内存大小为5m
//-Xmx10m -Xms10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
public static void main(String[] args) {
        int i = 0;
        List<String> list = new ArrayList<>();
        while (true) {
            list.add(String.valueOf(++i).intern());
        }
    }

运行结果:
在这里插入图片描述
我们来看看GC打印的部分详情:
在这里插入图片描述
首先可以直观的看出,此错误代码会频繁的触发GC。
在这里插入图片描述
上面这条日志的意思是新生代回收之前为2048K,回收之后为2047K;老年代回收之前为7102K,回收之后为7102K;总栈区回收之前为9150K,回收之后为9149K。
可以看出,此时GC的效率及其低下

4、OutOfMemoryError: Direct buffer memory (直接内存泄露)

写NIO程序时经常使用ByteBuffer来读取或写入数据,这是一种基于通道(Channel)与缓冲区(Buffer)的 I/O 方式。
它可以使用Native函数库直接分配堆外内存,也就是在本地内存分配。
ByteBuffer.allocate(capability) 第一种方式是分配JVM堆内存,属于GC的管辖范围,由于需要拷贝所以速度相对较慢。
ByteBuffer.allcateDirect(capability) 第二种方式是分配OS本地内存,不属于GC的管辖范围,由于不需要内存拷贝所以速度相对较快。
如果不断分配本地内存,堆内存很少使用,那么JVM就不需要执行GC,DirectByteBuffer对象们就不会被回收,这时候内存充足,但是本地内存可能已经使用光了,再次尝试分配本地内存就会出现 OutOfMemoryError: Direct buffer memory ,那么程序就直接崩溃了。

查看本地内存空间大小:

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

运行结果:
在这里插入图片描述
一般默认情况下,本地内存大约为电脑运行内存的四分之一(我的电脑是8G)

为了更直观的看到此故障,我们要把这个空间设置的小一点,参数和故障代码如下:

//-Xmx10m -Xms10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m(同上个故障)
public static void main(String[] args) {
   System.out.println("配置的直接内存大小:"+sun.misc.VM.maxDirectMemory() / (1024*1024)+"MB");
   // 直接内存大小为5m,在本地内存上分配一个6m的大对象
   ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6 * 1024 * 1024);
}

运行结果:
在这里插入图片描述
在这里插入图片描述

5、OutOfMemoryError: Unable to create new native thread (无法创建新的本地线程)
  1. 你的引用创建了太多的线程,超过了系统承载的极限。
  2. 你的服务器不允许你的应用创建这么多的线程,Linux系统默认允许单个进程可以创建的线程数是1024个,你的应用超过了这个数量,就会报OutOfMemoryError: Unable to create new native thread。

故障代码如下:

public static void main(String[] args) {
   //死循环创建线程
   for (int i = 0; ; i++) {
     new Thread(() -> {
        System.out.println("cccc");
     }).start();
   }
}

将此代码拷贝到 Linux 上运行,运行结果如下:
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值