在jvm内存模型中,除了程序计数器,其他区域都可能出现内存溢出问题。什么叫内存溢出呢?在程序运行过程中,当内存已扩展到最大,无法动态扩展,而又有新对象需要分配内存的时候,出现的内存空间不足的问题,叫做内存溢出。
关于内存溢出和内存泄漏,两个概念,它们有什么区别?
它们两个的表现都是内存不够用了。区别是造成内存不够的原因不同。
内存泄漏(Memory Leak)是指内存中有无用的对象堆积,GC无法清除,致使空间不足。在使用引用计数法标记垃圾对象的时候,容易出现循环引用的情况,致使这些垃圾对象无法被GC清除,会出现内存泄漏。
内存溢出(Memory Overflow)是指内存中对象都是必须要存在的,而有新对象申请内存,内存却不够分了,这种情况就是内存溢出。
哪块儿内存区域可能会发生内存溢出?报的异常分别是什么?什么样的代码会导致此区域内存溢出?如何解决或者避免这些内存溢出问题?下面分别来总结一下。
“java.lang.OutOfMemoryErro:Java heap space”,这个异常信息,大家在写代码的时候应该都遇到过。以冒号为分界,前半部分是异常种类,表示内存溢出。后半部分表示异常发生的区域。除了上边的堆区,还有下面几种: "unable to create new native thread","PermGen space","(Native Method)"。可以很容易的和前边讲的java内存模型对应起来,分别是栈、永久代。最后一个不是jvm内存中,它是本机直接内存DirectMemory。
//TODO:什么样的代码会导致此区域内存溢出?
堆溢出:
栈溢出:虚拟机栈/本地方法栈溢出。
方法区和运行时常量池溢出:
本机直接内存溢出:
如何解决或者避免这些内存溢出问题?第一步的,虚拟机做了处理,那就是垃圾回收机制。第二步的,我们的应用程序中可能会因为代码写的有问题造成内存溢出。这就需要排查出有问题的代码在哪里,从而修改它。如何排查问题,我们可以借助java提供的一些工具。对堆栈进行分析。步骤大致如下:
1、先通过内存映像分析工具(如Eclipse Memory Analyzer)对Dump出来的堆转储快照进行分析。看是内存泄漏还是内存溢出。
2、若是内存泄漏,就通过工具去查看泄漏对象到GC Roots的引用链,看它们是如何关联上的。这样定位泄漏代码的问题,再去解决具体的问题。(GC Roots是应用的可达性分析的方法来确定对象是否使用,若从GC Roots出发,通过引用链能到达的对象是可达对象,也就是对程序来说有用的对象,反之,不可达就是垃圾对象,需要被垃圾收集器回收的)
3、若是内存溢出,再去看参数,看相应的区域是否还可以调大。或者看代码,是否有占用生命周期过长的情况。以堆区为例,若发生溢出,去看-Xmx与-Xms,去和机器物理内存对比,是否可调大。
关于工具如何使用,请关注后续的文章。
通过常见的内存溢出情况,反推代码写就时应该注意的问题,从而避免溢出的发生:
1、避免创建周期过长的对象。??
2、避免递归调用的深度过长。??
总结:
讲了一下内存溢出发生的几种情况。异常信息分别是什么。GC的存在是为了对垃圾对象进行回收,也就避免了内存溢出问题。
理论和实践相结合,理论指导实践,而实践能更好的理解理论。