深入理解JVM——实战:outofmemoryerror异常

目的:

      1.通过代码验证Java虚拟机规范中描述的各个运行时区域存储的内容。

      2.遇到实际的内存溢出异常时,能根据异常的信息快速判断是哪个区域的内存溢出,知道什么样的代码可能会导致这些区域内存溢出,以及出现这些异常后该如何处理。

Java堆溢出

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

      当出现Java堆内存溢出时,异常堆栈信息“java.lang.OutOfMemoryError”会跟着进一步提示“Java heap space”。要解决这个区域的异常的思路是:通过内存映像分析工具(如Eclipse Memory Analyzer)对Dump出来的堆转储快照进行分析,重点是确认内存中的对象是否是必要的,即先分清楚到底是内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)。如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链,找到泄漏对象是通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收它们的。如果不是内存泄漏,换句话说,就是内存中的对象确实都还必须存活着,那就应当检查虚拟机的堆参数(-Xmx与-Xms),与机器物理内存对比看是否还可以调大,从代码上检查是否存在某些对象生命周期过长、持有状态时间过长的情况,尝试减少程序运行期的内存消耗。

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

两种异常:

  • 如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常
  • 如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。

      为每个线程的栈分配的内存越大,反而越容易产生内存溢出异常。

      如果是建立过多线程导致的内存溢出,在不能减少线程数或者更换64位虚拟机的情况下,就只能通过减少最大堆和栈容量来换取更多的线程。

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

        String.intern()是一个Native方法,它的作用是:如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象。否则,将此String对象包含的字符串添加到常量池中,并返回此String对象的引用。

       运行时常量池溢出,在OutOfMemoryError后面跟随的提示信息是“PermGen space”

本机直接内存溢出

       真正申请分配内存的方法是unsafe.allocateMemory()

posted on 2018-04-07 15:41  Joyce&wang  阅读(149)  评论(0)    收藏  举报

导航