欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【JVM】浅谈对OOM的认识

程序员文章站 2022-06-07 09:08:55
...

一张图区分Exception和Error

【JVM】浅谈对OOM的认识

1、java.lang.*Error

这是栈溢出错误,如果一个线程所需用到栈的大小>配置允许最大的栈大小,那么jvm就会抛出*。
一般出现这个问题是因为程序里有死循环或递归调用所产生的。由于深度递归,抛出此错误以指示应用程序的堆栈已耗尽。

【举例】
【JVM】浅谈对OOM的认识

2、java.lang.OutOfMemoryError:Java heap space

使用Java程序从数据库中查询大量的数据时出现该异常。

【举例】
【JVM】浅谈对OOM的认识

3、java.lang.OutOfMemoryError:GC overhead limit exceeded

GC回收时间 过长 时会抛出OutOfMemoryError。“过长” 的定义是,超过98%的时间用来做GC并且回收了不到2%的堆内存,连续多次GC 都只回收了不到2%的极端情况下才会抛出。加入不抛出 GC overhead limit 错误,会发生什么情况嗯?
那就是GC清理的这么点内存很快会再次填满,迫使GC再次执行,这样就形成恶性循环。CPU使用率一直是100%,而GC却没有任何成果。

【举例】
【JVM】浅谈对OOM的认识

4、lava.lang.OutOfMemoryError:Direct buffer memory(直接内存溢出)

配置参数-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
故障现象Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
导致原因:写NIO程序经常使用 ByteBuffer来读取或写入数据,这是一种基于通道(Channel)与缓冲区(Buffer)的 I/O 方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的 DirectByteBuffer对象作为这块内的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。
ByteBuffer.allocate(capability)第一种方式是分配JVM堆内存,属于GC管辖范围,由于需要拷贝所以速度相对较慢。
ByteBuffer.allocateDirect(capability)第二种方式是分配OS本地内存,不属于GC管辖范围,由于不需要内存拷贝所以速度相对较快。
但如果不断分配本地内存,堆内存很少使用,那么JVM就不需要执行GC,DirectByteBuffer对象们就不会被回收。
这时候堆内存充足,但本地内存可能已经使用完了,再次尝试分配本地内存就会出现 OutOfMemoryError,那程序就直接崩溃了。

【举例】
【JVM】浅谈对OOM的认识

5、java.lang.OutOfMemoryError:unable to create new native thread(不能再创建更多新的线程了)

高并发请求服务器时,经常出现如下异常:java.lang.OutOfMemoryError:unable to create new native thread。
准却讲,该native thread异常与对应的平台有关。

导致原因
①你的应用创建了太多线程了,一个应用进程创建多个线程,超过系统承载极限
②你的服务器并不允许你的应用程序船舰这么多线程,Linux系统默认允许单个进程可以创建的线程数是1024个,你的应用创建超过这个数量,就会报java.lang.OutOfMemoryError:unable to create new native thread

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

6、java.lang.OutOfMemoryError:Metaspace

JDK1.7中,存储在永久代的部分数据就已经转移到了Java Heap或者是 Native Heap,譬如符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了java heap;类的静态变量(class statics)转移到了java heap。但永久代仍存在于JDK1.7中,并没完全移除。
JDK 8.HotSpot JVM使用本地化的内存存放类的元数据,这个空间叫做元空间(Metaspace)。

官方定义:“In JDK 8, classes metadata is now stored in the native heap and this space is called Metaspace”。元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,但可以通过以下参数来指定元空间的大小:
-XX:MetaspaceSize、-XX:MaxMetaspaceSize

public class MetaspaceOutOfMemory {

  public static void main(String[] args) throws Exception {
    ClassPool cp = ClassPool.getDefault();
    for (int i = 0;; i++) {
        cp.makeClass("com.demo.MetaspaceClass" + i).toClass();
        Thread.sleep(1);
    }
  }
}

该错误的主要原因, 是加载到内存中的 class 数量太多或者体积太大。
【JVM】浅谈对OOM的认识