JAVA基础知识-OOM怎么办

前言:

     在介绍OOM之前,首先了解一下java运行时的数据区域。


JAVA运行时的数据区

这里写图片描述

1:虚拟机栈,本地方法栈,程序计数器是线程隔离的数据区,属于各个线程私有;

2:方法区,推(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异常。

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不能回收


总结

      到此为止,我们弄清楚虚拟机里面的内存是如何划分的,哪部分区域,什么样的代码、操作可能导致OOM异常。虽然Java有垃圾收集机制,但OOM仍然离我们并不遥远。遇到OOM怎么办?只有理解了OOM出现的原因,才能正确的解决问题。



猜你喜欢

转载自blog.csdn.net/champion2009/article/details/70992407