A.可能抛出oom异常的内存:
1.jvm运行时所管理的内存:除了程序计数器之外的内存(堆,方法区,虚拟机栈,本地方法栈)都有可能抛出oom异常。
当然栈中有可能会抛出OOM异常也可能会抛出stack overflow Error。当请求的深度大于栈所允许的最大的深度,则抛出stack overflow error ,当栈的容量扩充,分配不到空间时抛出OOM.
2.不属于jvm运行时管理的内存:Directory Memory (直接内存),NIO中,java对中有一个DirectoryByteBuffer引用指向直接内存,用户NIO操作,主要用来减少NIO内容在内存之间的复制。
B.抛出异常如何检查:
1.内存泄漏(没用的对象,不能被回收)
2.内存溢出(对象都有用途,内存太小,在为对象申请空间时,分配(申请)不到足够内存。
C.调试方案:
配置参数 -XX:+HeapDumpOnOutOfMemoryError 可以让虚拟机在出现内存溢出时,dump出转储快照,以便分析。
D.常见的OOM区域。
1.java堆。Java堆是程序运行过程中常见的OOM区域,提示信息 java heap space
解决方案:通过内存映像分析工具(Ecplise的Eclipse Memory Analyzer)对dump出来的快照进行分析。重点确认内存中的对象是否是必要的。如果是非必要的,那么属于内存泄漏,可以用工具查看对象到GC ROOTS之间的引用链(看看为啥导致了GC无法自动回收),找到内存泄漏的相应代码。如果不存在内存泄漏,那么有如下几种解决方案:a) 看看物理内存,确认虚拟机管理的堆内存是否还可以调大(-Xms 与-Xmx)。b)从代码检查,看看某些对象的生命周期是否过长,尝试减少程序运行过程中的内存消耗。
2.虚拟机栈,本地方法栈。 (HotSpot虚拟机不区分这两个,直接直接设置栈容量即可-Xss)
a) 单线程情况下,(减小栈内存、增大方法栈中的本地变量表长度)都抛出*Error。
b) 多线程情况下容易产生OOM异常,假如 JVM内存 一共 2G ,堆占了1G, 程序计数器内存占用很小,忽略。 那么1G的内存,分配给多个线程,每个线程分配到的内存越大,自然线程数就收到了限制。 如果需要更多的线程,自然会产生OOM异常。
解决方案: qa)增大栈内存。qb)减小每个线程分配到的栈内存。
(个人见解)这两个解决方案看着有冲突(减小栈内存*,增加栈内存OOM),其实并不然。两者之间有一个平衡点。一般虚拟机默认的栈内存,不太容易抛出*Error,因此在此基础上,我们需要的线程数量是可以控制或者说可以估量的,我们可以根据这个数量,计算出一个最小的栈内存,合理增大机器的内存即可。(当然如果极限情况,超出了寻址范围等,那么还是需要从两方面考虑。1.是否确实需要这么多线程 2.是否适当减小栈内存以增加线程数)。
3.方法区和运行时常量池 (测试这个异常用String 的intern( ) 方法) 提示信息 PermGen space
*JDK1.6及以前版本,运行时常量池都被分配在永久代里面,相当容易出现OOM异常。
JDK1.7 及以后版本,方法区中主要出现OOM异常的原因不是运行时常量池,而是方法区中的Class相关信息(类名,访问修饰符,常亮,字段描述,方法描述)。因此在使用好多框架时,他们都会为我们生成代理对象(动态生成了Class),因此也是相当容易产生OOM异常。
解决方案:查看一个类要被回收的条件(相当苛刻,专门描述一篇文章)。
4.直接内存(DirectMemory) 直接内存默认和java堆内存大小一致。 可以使用-XX:MaxDirectMemorySize指定。
如果是DirectMemory导致的内存溢出,溢出时,Dump的内存文件很小,没有明显异常,这个时候就应该考虑是DirectMemory抛出了OOM.
解决方案:检查程序中是否使用到了NIO技术,因为NIO才会通过DirectByteBuffer对象直接在DirectMemory中分配内存。