OutOfMemory相关问题(内存溢出异常OOM)
Posted yzhengy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OutOfMemory相关问题(内存溢出异常OOM)相关的知识,希望对你有一定的参考价值。
OutOfMemory(内存溢出异常OOM)
java.lang.OutOfMemoryError :Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector. (当JVM因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会抛出这个error。)
在《Java虚拟机规范》的规定里,除了程序计数器外,虚拟机内存的其他几个运行时区域都有发OutOfMemoryError异常的可能。
内存溢出和内存泄漏
内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。
内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。
常见的OOM
1.Java.lang.OutOfMemoryError:Java heap space(堆溢出)
? --如何产生:Java堆用于储存对象实例,我们只要不断地创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么随着对象数量的增加,总容量触及最大堆的容量限制后就会产生内存溢出异常。
? --如何解决:首先通过内存映像分析工具(如Eclipse Memory Analyzer和JProfile和Jhat)对Dump出来的堆转储快照进行分析。
? 第一步:分清楚到底是出现了内存泄漏还是内存溢出。
? 第二步:如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链,找到泄漏对象是通过怎样的引用路径、与哪些GC Roots相关联,才导致垃圾收集器无法回收它们,根据泄漏对象的类型信息以及它到GC Roots引用链的信息,一般可以比较准确地定位到这些对象创建的位置,进而找出产生内存泄漏的代码的具体位置。
? 第三步:如果不是内存泄漏,换句话说就是内存中的对象确实都是必须存活的,那就应当检查Java虚拟机的堆参数(-Xmx与-Xms)设置,与机器的内存对比,看看是否还有向上调整的空间。再从代码上检查是否存在某些对象生命周期过长、持有状态时间过长、存储结构设计不合理等情况,尽量减少程序运行期的内存消耗。
? --导致堆内存泄漏的常见原因:
静态集合类引起的内存泄漏:像HashMap、Vector等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,他们所引用的所有的对象Object也不能被释放,因为他们也将一直被Vector等引用着。
当集合Set里面的对象属性被修改后,再调用remove()方法时不起作用。之所以不起作用,是因为对象属性修改后,对象的hashcode就变了,remove的时候就找不到了。
监听器。Listener未删除。
各种连接。比如数据库连接、Socket连接、IO连接等,没有显式close掉。
非静态内部类。非静态内部类会自动生成构造函数,并把外部类作为构造函数的参数,这样才能在内部类里使用外部类的属性和方法。但是这样内部类会保留外部类的引用,如果内部类与外部类的生命周期不一致,就可能回收不了。
单例模式。在单例对象中引用了其它对象,被引用对象永远不会回收。
2.Java.lang.OutOfMemoryError:GC overhead limit exceeded(超出GC)开销限制
? --如何产生:JDK6新添错误类型,超过98%的时间系统资源在做垃圾回收,却回收了不到2%的堆内存,通常这样的异常伴随着CPU的冲高。通常是由于堆太小或者内存中存在的对象都是不可回收对象。
? --如何解决: a、查看系统是否有使用大内存的代码或死循环,定位方法与上一个相同。
b、通过添加JVM配置,来限制使用内存: < jvm-arg>-XX:-UseGCOverheadLimit< /jvm-arg>
3.Java.lang.OutOfMemoryError:Direct buffer memory(直接内存溢出)
? --如何产生:
? --如何解决:Heap Dump文件不会看到有什么明显的异常情况,如果发现内存溢出后产生的的Dump文件很小,程序又直接或间接使用DirectMemory(典型的间接使用就是NIO),就可以重点检查内存方面原因。
调整-XX:MaxDirectMemorySize= 参数
4.Java.lang.OutOfMemoryError:unable to create new native thread(不能创建新的本地线程)
? --如何产生:与平台有关,一个应用进程创建多个线程,超过系统承载极限。高并发请求服务器时,经常出现这个错误。
服务器不允许应用进程创建这么多线程,Linux系统默认允许单个进程可以创建的线程数是1024个。
? --如何解决:降低应用程序创建线程的数量。
? 修改linux服务器配置,扩大限制。
5.Java.lang.StackOverflowError(栈溢出SOF)
? --如何产生: 出现无限递归或死循环,局部变量不停地创建,导出栈溢出。
? --如何解决: 处理的方法是使用jstack输出.out文件,然后分析每个线程栈的运行情况。 可以通过虚拟机参数-Xss来设置栈的大小。
? 如果是建立过多线程导致的内存溢出,在不能减少线程数量或者更换64位虚拟机的情况下,就只能通过减少最大堆和减少栈容量来换取更多的线程。通过“减少内存”的手段来解决内存溢出。
6.Java.lang.OutOfMemoryError: PermGen space/Metaspace(方法区溢出) (JDK1.8引入元空间)
--如何产生: 一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区。另外,过多的常量尤其是字符串也会导致方法区溢出。
? IDEA集成tomcat环境下反复热发布应用也会出现此现象,JVM默认分配的4M运行空间过小,J2EE项目加载的类过多,造成内存溢出。
--如何解决: 可以通过更改方法区的大小来解决,使用类似-XX:PermSize=64m -XX:MaxPermSize=256m的形式修改。 定位方法与上文的1,2相同。
OOM和SOF的区别
在《Java虚拟机规范》中描述了两种异常:
1)如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。
2)如果虚拟机的栈内存允许动态扩展,当扩展栈容量无法申请到足够的内存时,将抛出OutOfMemoryError异常。
《Java虚拟机规范》明确允许Java虚拟机实现自行选择是否支持栈的动态扩展,而HotSpot虚拟机的选择是不支持扩展,所以除非在创建线程申请内存时就因无法获得足够内存而出现OutOfMemoryError异常,否则在线程运行时是不会因为扩展而导致内存溢出的,只会因为栈容量无法容纳新的栈帧而导致StackOverflowError异常。
内存OOM快速定位
1.确认是不是内存本身就分配过小:方法:jmap -heap 10765(进程ID)
2.找到最耗内存的对象:方法:jmap -histo:live 10765 | more
3.确认是否是资源耗尽:工具:pstree/netstat查看进程创建的线程数,以及网络连接数,如果资源耗尽,也可能出现OOM。
通过/proc/${PID}/fd,/proc/${PID}/task,可以分别查看句柄详情和线程数。
CPU100%快速定位
1.找到最耗CPU的进程;
执行top -c ,显示进程运行信息列表
键入P (大写p),进程按照CPU使用率排序
2.找到最耗CPU的线程;
top -Hp 10765 ,显示一个进程的线程运行信息列表
键入P (大写p),线程按照CPU使用率排序
3.查看堆栈,定位线程在干嘛,定位对应代码;
将线程PID转化为16进制。
jstack 10765 | grep ‘0x2a34‘ -C5 --color,打印进程堆栈通过线程id,过滤得到线程堆栈
面经中有关OOM的问题
内存溢出有哪些异常,有哪些oom,什么样的oom
JVM内存模型、OOM怎么解决
什么时候会抛栈的OOM
知道什么OOM
内存区域哪些会产生OOM,OOM场景
JVM,内存布局,垃圾回收,OOM怎么排查
oom(out of memory)可能原因
OOM排查思路
JVM,哪里会有OOM问题,OOM和SOF的区别
JVM有哪些垃圾回收器,讲一下细节;JVM有哪些优化配置参数;发生OOM如何定位到具体代码;
tomcat的oom怎么解决?
在什么地方会发生OOM?
如何分析OOM发生的原因?
让你自己实现OOM,你会怎么做?
以上是关于OutOfMemory相关问题(内存溢出异常OOM)的主要内容,如果未能解决你的问题,请参考以下文章