深入理解JVM(二)-OutOfMemoryError异常

对于C/C++开发者来说,他们在内存管理方面具有至高的权利,但是也承担着巨大的维护责任。而对于Java程序员来说,有了JVM(Java虚拟机)管理机制的帮助,再也不用担心内存泄漏和内存溢出问题了。因此,这篇文章我将深入探讨一下JVM,它的内部结构以及运行原理。

OutOfMemoryError异常

1.Java堆溢出

Java堆用于存储对象,只要不断地去创建对象,并且保证GC Roots到对象之间有可达的路径来避免垃圾回收机制清除这些对象,那么在对象数量到达最大的堆容量限制后就会产生内存溢出异常。

通过设置参数-XX:+HeapDumpOnOutOfMemoryErro可以让虚拟机在出现内存溢出异常时抛出当前的内存堆转储快照以便事后进行分析。

要解决这个异常,一般的手段是先通过内存映像分析工具(如Eclipse Memory Analyzer)对堆转储出来的快照进行分析,重点是确认内存中的对象是否是必要的,也就是要分清楚到底是出现了内存泄漏还是内存溢出。

如果是内存泄漏,可以进一步使用工具查看GC Roots的引用链,通过引用链的路径就可以准确的找到发生内存泄漏的位置。

如果是内存溢出,说明内存中的对象都是必要的,这时候就要检查虚拟机的堆参数(-Xms初始化时内存大小与-Xmx可以使用的最大内存),与物理内存对比,看是否可以增大,另外从代码上检查哪些对象生命周期过长、持有的状态时间过长的情况,尝试着减少这部分内存的消耗。

2.虚拟机栈和本地方法栈溢出

在Java虚拟机规范中描述了两种异常:

  • 如果线程请求的栈的深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。

  • 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

3.方法区和运行时常量池溢出

方法区溢出是一种常见的内存溢出异常,一个类要被垃圾回收器回收,其判定条件是比较苛刻的。在经常动态的生成大量Class的应用中,需要特别注意类的回收。这种常见的场景有:

  • 大量JSP或动态产生JSP文件的应用(JSP第一次运行的时候需要编译为Java类)

  • 基于OSGi的应用(即使是同一个类文件,被不同的类加载器加载就会视为不同的类)

  • 在程序中使用CGLib字节码增强和动态语言

4.本机直接内存溢出

由DirectMemory导致的内存溢出,一个明显的特征是在Heap Dump文件中不会看见明显的异常。如果大家发现OOM之后Dump文件很小,而程序中又直接或间接使用了NIO,那就需要考虑是不是这个问题了。

感谢

如果大家想了解更多,欢迎继续阅读接下来的章节,推荐大家熟读《深入理解Java虚拟机》这本书。

你可能感兴趣的:(java,jvm,异常)