JAVA-OOM

                                                              JAVA-OOM
1.Java运行时数据区域

这里写图片描述
虚拟机栈,本地方法栈,程序计数器是线程隔离的数据区,属于各个线程私有;方法区,推(Heap)由所有线程共享。
弄清楚运行时数据予以,是翻越虚拟机内存管理这堵墙的第一步,也是弄清楚OOM的第一步。
程序计数器它是一块较小的内存空间,它的作用可以看做是当先线程所执行的字节码的信号指示器,此内存区域是唯一一个在Java虚拟机规范中没有规定任何OutOfMemoryError情况的区域。
虚拟机栈:在Java虚拟机规范中,对这个区域规定了两种异常情况:(1)如果线程请求的栈深度大于虚拟机所允许的深度将抛出StackOverflowError(2)如果JVM Stack可以动态扩展,但是在尝试扩展时无法申请到足够的内存时抛出OutOfMemoryError。
本地方法栈:虚拟机规范对于本地方法栈中方法使用的语言,使用方式和数据结构没有强制规定,甚至有的虚拟机(比如HotSpot)直接把二者合二为一。这玩意儿抛出的异常跟上面的虚拟机栈一样。
堆(heap):它在虚拟机启动时创建,这货存在的意义就是存放对象实例,几乎所有的对象实例以及数组都要在这里分配内存,它是垃圾收集器管理的主要区域,因此很多时候也被称为“GC堆”, 它可以细分为:新生代,老年代。在细致一点可以分为:Eden空间,From Survivor空间,To Survivor空间等。Java堆的容量可以是固定大小,也可以随着需求动态扩展(-Xms和-Xmx),并在不需要过多空间时自动收缩。Java堆所使用的内存不需要保证是物理连续的,只要逻辑上是连续的即可。如果堆中没有内存完成实例分配并且堆也无法扩展,就会抛OutOfMemoryError。
方法区:用于存储以被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,对于习惯在HotSpot虚拟机上开发和部署程序的开发者来说,很多人愿意称它为“永久代”,方法区的容量可以是固定大小的,也可以随着程序执行的需求动态扩展,并在不需要过多空间时自动收缩。方法区在实际内存空间中可以是不连续的。当方法区无法满足内存分配需求时就会抛OutOfMemoryError。
运行时常量池:它是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。既然运行时常量池是方法区的一部分,自然会受到方法区内存的限制,当常量池无法再申请到内存时会抛出OutOfMemoryError异常。
直接内存(Direct Memory):直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现。服务器管理员配置虚拟机参数时,一般会根据实际内存设置-Xmx等参数信息,但经常会忽略掉直接内存,使得各个内存区域的总和大于物理内存限制(包括物理上的和操作系统级的限制),从而导致动态扩展时出现OutOfMemoryError异常。


2.常见的OOM异常及解决思路
(1) java.lang.OutOfMemoryError: unable to create new native thread
当调用new Thread时,如已创建不了线程了,则会抛出此错误,如果是JDK内部必须创建成功的线程,那么会造成Java进程退出,如果是用户线程,则仅抛出OOM,创建不了的原因通常是创建了太多线程,耗尽了内存,通常可通过减少创建的线程数,或通过-Xss调小线程所占用的栈大小来减少对Java 对外内存的消耗。
(2)java.lang.OutOfMemoryError: request bytes for . Out of swap space?
当JNI模块或JVM内部进行malloc操作(例如GC时做mark)时,需要消耗堆外的内存,如此时Java进程所占用的地址空间超过限制(例如windows: 2G,linux: 3G),或物理内存、swap区均使用完毕,那么则会出现此错误,当出现此错误时,Java进程将会退出。
(3)java.lang.OutOfMemoryError: Java heap space(堆溢出) ,这是最常见的OOM错误
【解决思路】
a.增加Java虚拟机中Xms(初始堆大小)和Xmx(最大堆大小)参数的大小
b.检查是否发生内存泄漏
c.看是否有死循环或不必要地重复创建大量对象
(4) java.lang.OutOfMemoryError: GC overhead limit execeeded
当通过new创建对象或数组时,如Java Heap空间不足,且GC所使用的时间占了程序总时间的98%,且Heap剩余空间小于2%,则抛出此错误,以避免Full GC一直执行,可通过UseGCOverheadLimit来决定是否开启这种策略,可通过GCTimeLimit和GCHeapFreeLimit来控制百分比。
(5) java.lang.OutOfMemoryError: PermGen space(方法区或者运行时常量池溢出)
【解决思路】
a.增加java虚拟机中的XX:PermSize和XX:MaxPermSize参数的大小
b.频繁使用CGLib,动态代理,反射GeneratedConstructorAccessor需要强大的方法区来支撑
(6)java.lang.StackOverflowError 虚拟机栈和本地方法栈溢出


说明:对于以上几种OOM错误,其中容易造成严重后果的是Out of swap space这种,因为这种会造成Java进程退出,而其他几种只要不是在main线程抛出的,就不会造成Java进程退出。


3.什么情况下会出现OOM?

(1)内存泄漏(连接未关闭,单例类中不正确引用了对象)

(2)代码中存在死循环或循环产生过多重复的对象实体

(3)Space大小设置不正确

(4)内存中加载的数据量过于庞大,如一次从数据库取出过多数据

(5)集合类中有对对象的引用,使用完后未清空,使得JVM不能回收

### Java 中 OutOfMemoryError (OOM) 的原因 Java 中 `java.lang.OutOfMemoryError` 是一种运行时异常,表示 JVM 无法分配所需的内存。其主要原因可以分为以下几个方面: #### 1. **堆内存不足** 当应用程序尝试创建对象时,如果堆内存不足以容纳这些对象,则会触发 `java.lang.OutOfMemoryError: Java heap space` 错误[^1]。这种情况通常是由于以下原因之一引起的: - 应用程序中存在内存泄漏,导致不再使用的对象未能及时被垃圾回收器清理。 - 堆大小设置过小,无法满足应用的需求。 #### 2. **栈内存溢出** 线程的栈空间有限,如果单个方法调用层次太深或局部变量占用过多内存,可能会引发 `StackOverflowError` 或者类似的 OOM 错误。不过这种错误更常见于递归深度过大或其他极端情况。 #### 3. **元数据区/永久代溢出** 在 JDK 8 及之后版本中,永久代被移除并替换为元数据区(Metaspace)。如果加载了大量的类文件而没有足够的元数据区域存储它们,将会发生 `java.lang.OutOfMemoryError: Metaspace` 错误[^2]。 #### 4. **直接缓冲区溢出** 使用 NIO API 创建大量 Direct ByteBuffer 对象可能导致堆外内存耗尽,从而抛出 `java.lang.OutOfMemoryError: Direct buffer memory` 错误。这是因为 Direct ByteBuffer 使用的是堆外内存而非 JVM 堆内存[^3]。 #### 5. **数组大小超出限制** 试图初始化超过允许范围的大数组也会引起此问题。具体来说,在某些平台上,JVM 尝试验证所请求的数据结构是否能够寻址成功之前就已经失败了,这通常是因为接近最大整数值减去一个小常数的结果超出了可用地址空间[^4]。 #### 6. **频繁 GC 导致性能下降直至崩溃** 如果大部分时间都花在了垃圾收集上却只回收到很少一部分内存,最终也可能报告此类错误消息:“GC overhead limit exceeded”。这意味着系统花费太多比例的时间来做垃圾搜集工作但却几乎没有效果[^5]。 --- ### 解决方案 针对上述各种类型的 OOM 问题,有如下几种通用以及特定场景下的解决办法: #### 1. **调整 JVM 参数** 对于堆内存不足的情况,可以通过增加初始和最大堆大小来缓解压力。例如: ```bash -Xms512m -Xmx2g ``` 如果是元数据区满载的问题,则应适当增大它的容量上限: ```bash -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m ``` 而对于直接字节缓冲区造成的短缺现象,我们则需重新定义 `-XX:MaxDirectMemorySize` 参数值以匹配实际需求。 #### 2. **优化代码逻辑减少不必要的对象生成** 审查业务流程找出那些重复构建相同实例的地方加以改进;利用缓存机制保存临时计算结果而不是每次都重新运算一遍相同的表达式等等措施都能有效降低整体消耗水平。 #### 3. **检测与修复内存泄露** 借助专业的剖析工具如 VisualVM, JProfiler 来定位潜在的内存泄露源头,并采取相应行动消除这些问题根源所在之处。 #### 4. **监控与报警机制建立** 定期查看服务器上的日志记录寻找任何有关于 OOM 提示的信息片段,并据此设定合理的预警阈值以便尽早发现问题苗头并作出反应。 #### 5. **升级硬件设施提高承载能力** 当经过以上种种努力仍然无法彻底解决问题的时候,考虑更换更高规格的物理设备可能是必要的选择之一。 --- ### 结论 综上所述,理解每种形式下可能出现的具体状况及其背后原理有助于开发者快速识别现场遇到的实际难题,并运用恰当的技术手段予以应对。记住合理规划资源配置的重要性的同时也要注重日常维护工作的开展频率与时效性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值