垃圾回收机制

 

java虚拟机内存结构中程序计数器,虚拟机栈和本地方法栈这三个区域随着线程的创建而生,销毁而死。因此这三个区域的内存分配和回收是确定的生命周期,而java虚拟机的堆内存和方法去时通过分代算法来完成的。

带着四个问题开始寻找?

1,  永久代什么时候发生垃圾回收机制?

2, MinorGC,mj  和 full 触发时机有什么异同?

3,三代各自的垃圾回收算法?

4,如何优化你的堆内存和方法区?

 

    1,对象优先在新生代的Eden分配

新生代的划分是Eden :from Survivor :to   Survivor =8:1:1。(可以通过-XX:ServivorRatio 改变比例,例如,若 -XX:ServivorRatio=6,那么,Eden:From:To = 6:2:2)

在分配内存是优先在新生代的Eden,当Eden区没有足够空间进行分配时,虚拟机将进行一次MinorGC。如果对象经过第一次Minor GC后,如果仍然存活,将会被移到from Survivor区,from区中的对象会根据年龄决定去向,年龄达到阈值的对象会移动到年老代,没有达到要求的则会被复制到to区域上,from和eden会被清空。然后to和from互换角色,保证to是空的,直到from区被占满,会将所有对象移动到年老代。

因为年轻代中的对象基本都是朝生夕死的,所以在年轻代的垃圾回收算法使用的是复制算法。

     2,大对象直接进入老年代

大对象是指需要大量连续内存空间的JAVA对象,最典型的大对象就是那种很长的字符串以及数组。大对象的分配对于java虚拟机的内存分配时一个坏消息,比这个更坏的是的是有大批量朝生夕灭的大对象。经常出现大对象容易导致内存还有不少空间时就提前触发垃圾收集以或得足够的连续空间来“安置”它们。(可以通过-XX:PretenureSizeThreshold,默认值是0,意味着任何对象都会出现在新生代分配内存。大于这个值设置的对象直接在老年代分配,这样做的目的是避免在Eden区及两个Survivor区之间发生大量的内存复制。注意:PretenureSizeThreshold参数只对Serial和ParNew两款收集器有效,Parallel Scavenge收集器不认识这个参数,Parallel Scavenge收集器一般并不需要设置。如果遇到必须使用此参数的场合,可以考虑ParNew加CMS的收集器组合)

     3,长期存活的对象直接进入老年代

   虚拟机给每个对象定义了一个对象年龄(Age)计数器,如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设为1,。对象在Survivor区中每熬过一次Minor GC,年龄就增加1,当他的年龄增加到一定程度(默认是15岁), 就将会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold设置。

   4,动态对象年龄判定

为了能更好地适应不同程度的内存状况,虚拟机并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升到老年代,如果在Survivor空间中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。

 

     5,空间分配担保

条件:在发生MinorGC之前,老年代最大可用连续空间     >      新生代所有对象的总空间

条件成立:进行一次安全的MinorGC

条件不成立:查看HandlePromotionFailure设置值是否允许担保失败(默认是true,允许失败)

                    允许:

                             检查:老年代最大可用连续空间     >    历次晋升到老年代对象的平均大小

                                        检查成立:进行一次有风险的MinorGC

                                        检查失败:进行一次FullGC

                    不允许:进行一次FullGC

 

担保和冒险:MinorGC后有大量存活的对象,Survivor无法容纳会直接进入老年代。但一共有多少对象在内存回收后存活下来是不可预知的,因此只好取之前每次垃圾回收后晋升到老年代的对象大小的平均值作为参考。取平均值仍然是一种概率性的事件,如果某次Minor GC后存活对象陡增,远高于平均值的话,必然导致担保失败,如果出现了分配担保失败,就只能在失败后重新发起一次Full GC。虽然存在发生这种情况的概率,但大部分时候都是能够成功分配担保的,这样就避免了过于频繁执行Full GC。

 

6,问题解答

      1,  永久代什么时候发生垃圾回收机制?

垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full GC)。如果你仔细查看垃圾收集器的输出信息,就会发现永久代也是被回收的。这就是为什么正确的永久代大小对避免Full GC是非常重要的原因。请参考下Java8:从永久代到元数据区 (译者注:Java8中已经移除了永久代,新加了一个叫做元数据区的native内存区)。

 

2, MinorGC,mj  和 full 触发时机有什么异同?

分区:

新生代  MinorGC  :指在新生代的垃圾收集动作,因为大多数对象都具备朝生夕灭的特性,所以比较频繁回收速度也比较快。

老年代 MajorGC和Full GC :指发生在老年代的GC,出现MajorGC一般会伴随着一次MinorGC(但不绝对),MajorGC一般比MinorGC慢十倍以上。

MajorGC和Full GC的区分:

Major GC:清理永久代,但是由于很多MojorGC 是由MinorGC 触发的,所以有时候很难将MajorGC 和MinorGC区分开。

FullGC:是清理整个堆空间—包括年轻代和永久代。FullGC 一般消耗的时间比较长,远远大于MinorGC,因此,有时候我们必须降低FullGC 发生的频率。

触发时机:

MinorGC触发的条件:
当Eden区没有足够空间进行分配时,虚拟机将进行一次MinorGC


full GC触发的条件:
除直接调用System.gc外,触发Full GC执行的情况有如下四种:

1. 旧生代空间不足。
旧生代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行Full GC后空间仍然不足,则抛出如下错误: java.lang.OutOfMemoryError: Java heap space

2. Permanet Generation(持久代)空间满 。
PermanetGeneration中存放的为一些class的信息等,当系统中要加载的类、反射的类和调用的方法较多时,Permanet Generation可能会被占满,在未配置为采用CMS GC的情况下会执行Full GC。如果经过Full GC仍然回收不了,那么JVM会抛出如下错误信息: java.lang.OutOfMemoryError: PermGen space

3. CMS GC时出现promotion failed和concurrent mode failure 。
对于采用CMS进行旧生代GC的程序而言,尤其要注意GC日志中是否有promotion failed和concurrent mode failure两种状况,当这两种状况出现时可能会触发Full GC。
promotionfailed是在进行Minor GC时,survivor space放不下、对象只能放入旧生代,而此时旧生代也放不下造成的;concurrent mode failure是在执行CMS GC的过程中同时有对象要放入旧生代,而此时旧生代空间不足造成的。
应对措施为:增大survivorspace、旧生代空间或调低触发并发GC的比率,但在JDK5.0+、6.0+的版本中有可能会由于JDK的bug29导致CMS在remark完毕后很久才触发sweeping动作。对于这种状况,可通过设置-XX:CMSMaxAbortablePrecleanTime=5(单位为ms)来避免。

4. 统计得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间 。
这是一个较为复杂的触发情况,Hotspot为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象,在进行Minor GC时,做了一个判断,如果之前统计所得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间,那么就直接触发Full GC。
例如程序第一次触发Minor GC后,有6MB的对象晋升到旧生代,那么当下一次Minor GC发生时,首先检查旧生代的剩余空间是否大于6MB,如果小于6MB,则执行Full GC。
当新生代采用PS GC时,方式稍有不同,PS GC是在Minor GC后也会检查,例如上面的例子中第一次Minor GC后,PS GC会检查此时旧生代的剩余空间是否大于6MB,如小于,则触发对旧生代的回收。

除了以上4种状况外,对于使用RMI来进行RPC或管理的Sun JDK应用而言,默认情况下会一小时执行一次Full GC。可通过在启动时通过-java-Dsun.rmi.dgc.client.gcInterval=3600000来设置Full GC执行的间隔时间或通过-XX:+ DisableExplicitGC来禁止RMI调用System.gc。

HotSpot虚拟机中存在三种垃圾回收现象,minor GC、major GC和full GC。对新生代进行垃圾回收叫做minor GC,对老年代进行垃圾回收叫做major GC,同时对新生代、老年代和永久代进行垃圾回收叫做full GC。许多major GC是由minor GC触发的,所以很难将这两种垃圾回收区分开。major GC和full GC通常是等价的,收集整个GC堆。但因为HotSpot VM发展了这么多年,外界对各种名词的解读已经完全混乱了,当有人说“major GC”的时候一定要问清楚他想要指的是上面的full GC还是major GC。

另外HotSpot虚拟机在1.8之后已经取消了永久代,改为元空间

3,三代各自的垃圾回收算法?

年轻代:复制算法

老年代:标记-清除/标记-整理

永久代:废弃的常量和无用的类。废弃常量的回收比较简单,跟堆中对象的回收类似。

 

 

4,如何优化你的堆内存和方法区?

ing......

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值