理解一下,为何要对堆内存分代?固然,不分代也能完成它所作的事情,之因此分代,实际上是为了优化GC性能。算法
若是没有分代,那么全部的对象都会放在一块内存区域中,GC的时候寻找垃圾对象,就须要对整个内存区域进行扫描,这样会很大程度上影响GC效率,在Java中,不少对象都是 “朝生夕死” 的,若是把内存空间划分区域的话,将新建立的对象放到某个区域中,GC的时候优先回收这部分 “朝生夕死” 的对象,这样就会腾出很大的空间来。jvm
因此就对堆内存进行了分代,下面这个图就是堆内存的分布图性能
堆内存被划分为两块,分别是年轻代和老年代。优化
年轻代其实分为两部分,分别是1个Eden区和2个Survivor区(分别叫from和to),默认比例是8:1:1,通常状况下,新建立的对象基本都会放到Eden区,(除非一些特别大的对象会直接放到老年代),当Eden没有足够的空间的时候,就会触发jvm发起一次Minor GC,若是对象通过一次Minor GC还存活,而且又能被Survivor空间接受,那么将被移动到Survivor空间当中,对象在Survivor区中每熬过一次Minor GC,年龄就会增长一岁,当它的年龄增长到必定程度(默认15岁)时,就会被移到老年代中,固然晋升老年代的年龄是能够设置的。spa
由于年轻代中的对象基本都是朝生夕死的(80%以上),因此在年轻代的垃圾回收算法使用的是复制整理算法,复制整理算法的基本思想就是将内存分为两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另一块上面。对象
优势:不会产生内存碎片
缺点:会开辟新的空间,也就是To survivor,用来保存有用对象
复制对象会花费一些时间
在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区的“To”是空的。紧接着进行GC,通过一轮Minor GC后,Eden区中全部存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到必定值(年龄阈值,能够经过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。通过此次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。无论怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满以后,会将全部对象移动到年老代中。blog
当年轻带随着不断地Minor GC ,from survivor中的对象会不断成长,当from survivor中的对象成长大15岁的时候,就会进入老年代,随着Minor GC的持续进行,老年代中对象也会持续增加,最终老年代的空间也会不够用,此时就会执行老年代的GC-->Major GC。Major GC使用的算法是标记清除算法或者标记-压缩算法。内存
步骤it
【1】首先会去根对象的集合中进行遍历,发现对象还存在引用,就是存活的对象并打上一个存活的标记
【2】再去根对象集合进行二次遍历,将没有被打上标记的对象清除掉。
优缺点io
优势:可以进入老年代的对象,通常都相对稳定,被回收的数量较少,减小对磁盘的清理,若是采用复制整理算法,被复制的对象会有不少。
缺点:虽然垃圾获得了回收,可是回收之后,堆内存中出现了不连续的现状---内存碎片,致使大对象没法建立
和标记清除算法基本相同,惟一不一样的就是,在清除完成以后,会把存活的对象向内存的一边进行压缩,这样就能够解决内存碎片问题。上篇文章已经详细介绍过了,这里就再也不赘述了。
【1】-XX:NewSize和-XX:MaxNewSize 用于设置年轻代的大小,建议设为整个堆大小的1/3或者1/4,两个值设为同样大。 【2】-XX:SurvivorRatio 用于设置Eden和其中一个Survivor的比值,这个值也比较重要。 【3】-XX:+PrintTenuringDistribution 这个参数用于显示每次Minor GC时Survivor区中各个年龄段的对象的大小。 【4】-XX:InitialTenuringThreshol和-XX:MaxTenuringThreshold 用于设置晋升到老年代的对象年龄的最小值和最大值,每一个对象在坚持过一次Minor GC以后,年龄就加1。