StackoverFlowError

StackOverflowError 是一个java中常出现的错误:在jvm运行时的数据区域中有一个java虚拟机栈,当执行java方法时会进行压栈弹栈的操作。在栈中会保存局部变量,操作数栈,方法出口等等。jvm规定了栈的最大深度,当执行时栈的深度大于了规定的深度,就会抛出StackOverflowError错误。

代码示例:

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

    // 递归调用
    public static void test() {
        test();
    }
}

报错信息:

异常堆栈信息获取 java 报错堆栈信息_java


最容易引起错的是,递归没有加入退出条件,容易引起StackOverflowError

OutOfMemoryError:Java heap space

java的堆内存不够用了才会报OutOfMemoryError:Java heap space这个错误。

引发 java.lang.OutOfMemoryError: Java heap space 错误的主要原因就是在创建新的对象时, 堆内存中的空间不足以存放新创建的对象时发生!

报错演示:

1.先设置堆内存大小

异常堆栈信息获取 java 报错堆栈信息_java_02


代码示例:

public class JavaHeapSpaceErrorTest {
    public static void main(String[] args) {
        byte[] bytes = new byte[11*1024*1024];
    }
}

运行结果:

异常堆栈信息获取 java 报错堆栈信息_JVM_03

OutOfMemoryError:GC overhead limit exceeded

  1. GC回收事时间过长会抛出此异常。过长的定义是,超过98%的时间用来左GC并且回收了不到2%的堆内存。
  2. OutOfMemoryError是java.lang.VirtualMachineError的子类,当JVM资源利用出现问题时抛出,更具体地说,这个错误是由于JVM花费太长时间执行GC且只能回收很少的堆内存时抛出的。根据Oracle官方文档,默认情况下,如果Java进程花费98%以上的时间执行GC,并且每次只有不到2%的堆被恢复,则JVM抛出此错误。换句话说,这意味着我们的应用程序几乎耗尽了所有可用内存,垃圾收集器花了太长时间试图清理它,并多次失败。
  3. 在这种情况下,用户会体验到应用程序响应非常缓慢,通常只需要几毫秒就能完成的某些操作,此时则需要更长的时间来完成,这是因为所有的CPU正在进行垃圾收集,因此无法执行其他任务。

错误重现:

public class GCOverHeadErrorTest {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        int i = 0;
        while (true) {
            // 放回常量池中的引用,在常量池中创建对象
            String value = String.valueOf(++i).intern();
            list.add(value);
        }
    }
}

结果:

异常堆栈信息获取 java 报错堆栈信息_JVM_04


上面这段代码很简单,就是不断的网集合里面添加元素。

解决方案
理想的解决方案是通过检查可能存在内存泄漏的代码来发现应用程序所存在的问题,这时需要考虑:

  • 应用程序中哪些对象占据了堆的大部分空间?(What are the objects in the application that occupy large portions of the heap?)
  • 这些对象在源码中的哪些部分被使用?(In which parts of the source code are these objects being allocated?)

我们还可以使用自动化图形工具,比如JVisualVM、JConsole,它可以帮助检测代码中的性能问题,包括java.lang.OutOfMemoryError。

最后一种方法是通过更改JVM启动配置来增加堆大小,或者在JVM启动配置里增加-XX:-UseGCOverheadLimit选项来关闭GC Overhead limit exceeded。但是这样治标不治本,不推荐。

OutOfMemoryError:Direct buffer memory

导致原因
导致这个的原因是:用 nio ,但是 direct buffer 不够。

  1. 写NIO程序经常使用ByteBuffer来读取或者写入 数据 ,这是一种基于通道(channel)和缓冲区(Buffer)的I/O方式。
  2. 它可以使用Native函数库直接分配堆外内存,然后通过一个存储在 java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。
  3. 这样能在一些场景中显著的提高性能,因为避免了在Java堆和Native堆中来回复制数据
  • ByteBuffer.allocate(capability) :这种方式是分配JVM堆内存,属于GC管辖范围,由于需要拷贝数据较慢。
  • ByteBuffer.allocateDirect(capability):这种方式是分配OS本地内存,不属于GC管辖范围,由于不需要内存拷贝,所以速度相对较快。

但是如果不断的分配本地内存,堆内存很少使用,那么JVM就不需要执行GC,DirectByteBuffer对象们 就不会被回收。这时候内存充足,但是本地内存可能被用光,再次分配的时候就会报:OutOfMemoryError:Direct buffer memory错误,程序直接崩溃

代码示例

先分配堆外内存-XX:MaxDirectMemorySize=5m

异常堆栈信息获取 java 报错堆栈信息_JVM_05


代码:

public class DirectBufferMemoryTest {
    public static void main(String[] args) {
        // 查看JVM分配的最大的本地内存
        long memory = VM.maxDirectMemory()/1024/1024;
        System.out.println("JVM分配的最大DirectMemory内存是:"+ memory + "M");

        // allocateDirect分配堆外内存。不在堆内
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6 * 1024 * 1024);
    }
}

运行结果:

异常堆栈信息获取 java 报错堆栈信息_异常堆栈信息获取 java_06


从结果可以看出,当堆外内存不足的时候,就会报OutOfMemoryError:Direct buffer memory这个错误。

OutOfMemoryError:unable to create new native thread

高并发请求服务器时,有可能会出现这个异常
导致原因:
1. 应用创建了太多线程了,一个应用进程创建多个线程,超过系统承载极限
2. 服务器不允许应用程序创建这么多线程,linux系统默认允许单个进程创建的线程是1024个,如果超过这个数量,就可能报这个错误

如下代码在linux系统允许,由于不断的创建线程,就会报这个错误。

public class UnableToCreateThreadTest {
    public static void main(String[] args) {
        for (int i = 0; ;i++) {
            System.out.println("================:" + i);
            new Thread(new Runnable() {
                @Override
                public void run() {

                }
            }).start();
        }
    }
}

解决办法:

  1. 想办法降低应用程序创建的线程数量,分析应用是否真的需要创建这样多的线程,如果不是,改代码将线程数降到最低
  2. 对于有的应用,确实需要创建很多线程,超过了Linux系统的默认配置1024个,可以通过修改Linux服务器配置,扩大Linux默认限制

OutOfMemoryError:Metaspace

这个错误是JVM的元空间内存不够用了才会报这个错误,JDK8后,把永久代改为元空间,元空间使用的是本地内存,不在是堆内存

我们先查看元空间JVM默认的大小是多少

public class MetaspaceTest {
    public static void main(String[] args) throws InterruptedException {
        System.out.println("===========");
        Thread.sleep(Integer.MAX_VALUE);
    }
}

使用jps查看进程编号和jinfo命令查看信息:

C:\Users\18133\IdeaProjects\javastudy>jinfo -flag MetaspaceSize 12716
-XX:MetaspaceSize=21807104

从结果可以看出元空间的大小 是 21807104/1024/1024 = 20.8M,不到21M

java.lang.OutOfMemoryError: Metaspace 错误所表达的信息是: 元数据区(Metaspace) 已被用满