引言
java对象的实例都存放在堆里,有的对象会频繁的创建和消亡,生命周期短,而另外一些对象则相当长寿。jvm按照寿命的长短将对象存放于不同的堆区里并采用不同的垃圾回收策略,最大限度的保证内存的需求和降低gc的消耗。
堆的分区
堆分为三个代,分别是青年代(Young)、老年代(Old)和永久代(Perm)。
青年代(Young)
顾名思义,青年代用来存放新创建和年轻的对象,何为年轻?年轻到什么程度?这得由垃圾回收器说了算,如果经过几次(默认是15次)垃圾回收后对象依然存活,垃圾回收器就认为该对象不再年轻,于是就把该对象转移到老年代。另外,如果青年代得空间不足也会将部分青年代对象转移到老年代。
青年代又分为三个区:
创建空间(Eden)、生存空间0(Survivor)、生存空间1(Survivor1),分别简记为E、S0、S1
对象的创建都在E区完成,若E区空间不足,jvm执行minorGC,具体做法如下:
a、将E区中的存活对象复制到一个空的S区,另一个S区中的存活对象也复制到这个空的S区,始终保证一个S区是空的,清空E区;
b、a步骤中若发现S区满,将其中的对象复制到老年代(Old);若未满,其中一些足够老的对象也会复制到老年代(Old)。
c、b步骤中若老年代(Old)也满了,则执行全收集(fullGC).
相关jvm参数:
-XX:NewRation=n设置青年代所占堆区的空间比例
-XX:NewSize=n设置青年代所占内存大小
-XX:SurvivorRation=n设置S区与E区的空间比例
-XX:MaxTenuringThreshold=n 当青年代中对象的回收次数超过该值时,gc认为该对象够老,将该对象复制到老年代。事实上如果该值设为0,则jvm根本就不会用到S区,在E区内存不足时直接将存活对象复制到老年代。
垃圾回收算法:
青年代采用“复制”垃圾回收算法,复制算法比较适合于垃圾回收频繁且垃圾多的场合,能够避免内存碎片。该算法的垃圾回收会暂停当前的工作线程直到回收完毕,故在实际使用过程中应该根据系统自身的特点(实时性优先/吞吐量优先)选择合适的垃圾回收器。
老年代(Old)
老年代里存放的是一些长寿的对象,只有在青年代内存不足向老年代复制对象且老年代内存也不足的时候才会对老年代做垃圾回收。
垃圾回收算:
老年代采用“标记-清楚-压缩”垃圾回收算法,与复制算法不同,该算法适用的场合是回收频率低、垃圾少,该算法如果不执行压缩的话还会产生内存碎片。所谓压缩,就是指将内存区域内的对象往内存的一边对齐,腾出另外一边连续的空闲空间。显然,该算法要比青年代上的复制算法重量级得多。不过,该算法的垃圾回收可以并发式进行,即在不停止当前工作线程的情况下进行垃圾回收。
永久代(Perm)
该区域也叫“方法区”,主要用来存放类的字节码和元信息,以及常量的定义。java虚拟机规范不要求实现该区域的垃圾回收,原因有二:
1、该区域上的垃圾少,回收的性价比非常低;
2、判断一个类是否为垃圾比判断一个对象是否为垃圾昂贵得多,复杂度高。
鉴于此,我们对该区域的垃圾回收别报希望,只要记住一点就行了:永久代上不会主动进行垃圾回收。因此,在日常开发的过程中,尤其是在struts、hibernate等框架的使用上一定要留意,不要让动态生成的类占满了永久代区域。