OutOfMemoryError 汇总

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/chen8238065/article/details/80860444

内存溢出

内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。

memory leak会最终会导致out of memory

持久代溢出java.lang.OutOfMemoryError: PermGen space

持久代包含以下类信息:

类方法
类名称
常量池信息
对象数组和与类相关的类型数组
被Java虚拟机使用的内部对象
编译器用于优化的信息

一般出现 持久代溢出,首先调整jvm参数 ;
jvm配置正确的时候,注意观察是否重定义了ClassLoader.注意是否有异常使用的ClassLoader,比如方法内初始化classloder 并 load class.
第二种 就是输出堆信息 观察 是不是 字符串常量池太大.

常用 jvm 配置

-XX:MaxPermSize=128m -XX:+UseConcMarkSweepGC XX:+CMSClassUnloadingEnabled
- 具体MaxPermSize大小结合你的机器,和服务现状.
- CMSClassUnloadingEnabled 这个参数表示在使用CMS垃圾回收机制的时候是否启用类卸载功能。默认这个是设置为不启用的

元空间溢出java.lang.OutOfMemoryError: Metaspace

jdk8 后开始有元空间,弃用持久代,移出常量池到堆中,溢出解决方案同上,

常用参数

MetaspaceSize
初始化的Metaspace大小,控制元空间发生GC的阈值。GC后,动态增加或降低MetaspaceSize。在默认情况下,这个值大小根据不同的平台在12M到20M浮动。使用Java -XX:+PrintFlagsInitial命令查看本机的初始化参数
MaxMetaspaceSize
限制Metaspace增长的上限,防止因为某些情况导致Metaspace无限的使用本地内存,影响到其他程序。在本机上该参数的默认值为4294967295B(大约4096MB)。

注: jdk 最好要设置这个值 MetaspaceSize, 不设值会因为元空间是动态分配的,启动的时候会导致2次full gc

java.lang.OutOfMemoryError: Compressed class space

一般很少会遇到这个异常.
如果启用了UseCompressedClassesPointers的话(打开UseCompressedOops的话之后,会默认启用),那么原生内存上会有两个独立的区域用来存储类和它们的元数据。启用UseCompressedClassesPointers之后,64位的类指针会使用32位的值来表示,压缩的类指针会存储在压缩类空间(compressed class space)中。默认情况下,压缩类空间的大小是1GB并且可以通过CompressedClassSpaceSize进行配置。
MaxMetaspaceSize能够为这两个区域设置一个总的提交(committed)空间大小,即压缩类空间和类元数据的提交空间。
启用UseCompressedClassesPointers之后,在GC日志中会进行采样输出。在Metaspace所报告的提交和保留(reserved)空间中包含了压缩类空间的提交和预留空间。

java.lang.OutOfMemoryError: Java heap space

首先还是先观察 jvm 参数-Xmx5G -Xms5G -Xmn2G ,没问题再观察
1. Java堆上已经没有空闲的空间,JVM无法继续执行程序了。这种错误最常见的原因就是指定的最大Java堆空间已经不足以容纳所有的存活对象了。要检查Java堆空间是否足以容纳JVM中所有存活的对象,一种简单的方式就是检查GC日志.
2. -XX:+HeapDumpOnOutOfMemoryError 可以在对溢出的时候dump
3. 注意,并行垃圾收集器可能会连续地调用Full GC以便于释放堆上的空间,即便这种尝试的收益很小、堆空间几乎已被充满时,它可能也会这样做。为了避免这种情况的发生,我们可以调节-XX:GCTimeLimit 和-XX:GCHeapFreeLimit的值。
GCTimeLimit能够设置一个上限,指定GC时间所占总时间的百分比。它的默认值是98%。减少这个值会降低垃圾收集所允许花费的时间。GCHeapFreeLimit设置了一个下限,它指定了垃圾收集后应该有多大的空闲区域,这是一个相对于堆的总小大的百分比。它的默认值是2%。增加这个值意味着在GC后要回收更大的堆空间。如果五次连续的Full GC都不能保持GC的成本低于GCTimeLimit并且无法释放 GCHeapFreeLimit所要求的空间的话,将会抛出OutOfMemoryError。

终结器所导致的OutOfMemoryError

滥用终结器(finalizer)可能也会造成OutOfMemoryError。带有终结器的对象(也就是含有finalize()方法)会延迟它们所占有空间的回收。在回收这些实例并释放其堆空间之前,终结器线程(finalizer thread)需要调用它们的finalize()方法。如果终结者线程的处理速度比不上要终结对象的增加速度(添加到终结者队列中以便于调用其finalize()方法)的话,那么即便终结器队列中的对象都有资格进行回收,JVM可能也会出现OutOfMemoryError。因此,非常重要的一点就是确保不要因为大量对象等待(pending)终结而造成内存耗尽。 jmap – finalizerinfo 查看 pending信息.

java.lang.OutOfMemoryError: unable to create new native thread

程序创建的线程数量已达到上限值.

Java程序向JVM请求创建一个新的Java线程;

JVM本地代码(native code)代理该请求, 尝试创建一个操作系统级别的 native thread(原生线程);

操作系统尝试创建一个新的native thread, 需要同时分配一些内存给该线程;

如果操作系统的虚拟内存已耗尽, 或者是受到32位进程的地址空间限制(约2-4GB), OS就会拒绝本地内存分配;

JVM抛出 java.lang.OutOfMemoryError: Unable to create new native thread 错误。

可以修改系统限制来避开 Unable to create new native thread 问题 ,ulimit -n

java.lang.OutOfMemoryError: Direct buffer memory

堆外内存 分配失败,一般在nio ,netty 中都会直接使用对外内存DirectByteBuffer.

DirectByteBuffer是通过虚引用(Phantom Reference)来实现堆外内存的释放的。
PhantomReference 是所有“弱引用”中最弱的引用类型。不同于软引用和弱引用,虚引用无法通过 get() 方法来取得目标对象的强引用从而使用目标对象,观察源码可以发现 get() 被重写为永远返回 null。虚引用主要被用来 跟踪对象被垃圾回收的状态,通过查看引用队列中是否包含对象所对应的虚引用来判断它是否 即将被垃圾回收,从而采取行动。它并不被期待用来取得目标对象的引用,而目标对象被回收前,它的引用会被放入一个 ReferenceQueue 对象中,从而达到跟踪对象垃圾回收的作用。
通过-XX:MaxDirectMemorySize来指定最大的堆外内存大小,当使用达到了阈值的时候将调用System.gc()来做一次full gc,以此来回收掉没有被使用的堆外内存
会显示调用gc ,不能禁用 -XX:+DisableExplicitGC

堆外内存=总内存-堆内存-元空间/持久代内存-线程占用空间

java.lang.OutOfMemoryError: reason stack_trace_with_native_method

表明本机方法遇到了分配故障。 这与之前的消息之间的区别在于分配失败是在 Java 本地接口(JNI)或本机方法中检测到的,而不是在JVM代码中检测到的。如果抛出此类 OutOfMemoryError 异常,则可能需要使用操作系统的本机实用程序来进一步诊断问题。

java.lang.OutOfMemoryError: GC Overhead limit exceeded

这种情况发生的原因是, 程序基本上耗尽了所有的可用内存, GC也清理不了。
JVM抛出 java.lang.OutOfMemoryError: GC overhead limit exceeded 错误就是发出了这样的信号: 执行垃圾收集的时间比例太大, 有效的运算量太小. 默认情况下, 如果GC花费的时间超过 98%, 并且GC回收的内存少于 2%, JVM就会抛出这个错误。

java.lang.OutOfMemoryError: request size bytes for reason. Out of swap space?

JVM启动参数指定了最大内存限制。如 -Xmx 以及相关的其他启动参数. 假若JVM使用的内存总量超过可用的物理内存, 操作系统就会用到虚拟内存。异常表明, 交换空间(swap space,虚拟内存) 不足,是由于物理内存和交换空间都不足所以导致内存分配失败。
一般重要服务最好单独部署,避免机器上的其他程序导致内存不够用.

java.lang.OutOfMemoryError: Required array size too large

byte[] data = Files.readAllBytes(path);

Files.readAllBytes 方法最大支持 Integer.MAX_VALUE - 8 大小的文件,也即最大2GB的文件。

java.lang.OutOfMemoryError: Requested array size exceeds VM limit

Java平台限制了数组的最大长度。各个版本的具体限制可能稍有不同, 但范围都在 1 ~ 21亿 之间。异常说明想要创建的数组长度超过限制
一般很少看到这个错误, 因为Java使用 int 类型作为数组的下标(index, 索引)。在Java中, int类型的最大值为 2^31 – 1 = 2,147,483,647。大多数平台的限制都约等于这个值 —— 例如在 64位的 MB Pro 上, Java 1.7 平台可以分配长度为 2,147,483,645, 以及 Integer.MAX_VALUE-2) 的数组。

展开阅读全文

没有更多推荐了,返回首页