GC 一篇文章彻底了解Java垃圾收集机制( 二 )


2.复制算法(Copying)
将内存的总容量分为两块,每次只使用其中的一块,当这一块用完了,触发GC,此时将还存活的对象转移到另一块内存中,之前使用的那一块内存完全清理掉 。这样每次对一个半区进行回收,也不会存在内存碎片,实现简单,运行高效,但是一次只能使用半块内存可能会造成浪费 。
在新生代中,绝大部分的对象时“朝生夕死”的,因此,不需要按照1:1来划分空间 。而是将内存分为一块较大的Eden区以及两个Survivor区,HotSpot虚拟机中,Eden:Survivor=8:1 ,每次使用一个Eden区以及一个Survivor区,90%的空间,触发GC后,将剩余的对象转移到未使用的Survivor中,然后清理Eden区和用过的Survivor区,空间不够时,会担保分配到老年代 。这样一次可以使用90%的内存空间,极大的提高了内存的使用率 。因此,新生代一般采用这种算法来回收 。
3.标记整理算法(Mark-Compact)
如果回收时空间内的对象存活率较高,那么使用复制算法一次只能使用50%的空间(以应对所有对象都存活的情况),因此老年代采用标记整理算法 。先对需要清理的对象进行标记,然后将存活的对象都向一端移动,直接清理掉端边界以外的内存 。这种方式也不会留下内存碎片 。
标记整理算法没有复制算法快 。
三. Java垃圾收集器(了解即可,需要时可以网上细查)
新生代收集器:Serial收集器、ParNew收集器(Serial的多线程版本)、Parallel Scanvenge收集器(控制吞吐量,提高相应速度)
老年代收集器:Serial Old收集器、Parallel Old收集器、CMS收集器(最短停顿)、G1(新生代、老年代都可回收)
四. 内存的分配与回收新生代:即复制算法中提到的Eden区以及2个Survivor区 。
老年代:新生代存活足够长时间后进入老年代 。堆上的另一块区域 。
Minor GC:发生在新生代的垃圾收集动作 。因为Java对象存活时间一般较短,故Minor GC非常频繁,一般回收速度也较快 。
Full GC:发生在老年代的垃圾收集动作,伴随着最少一次的Minor GC,且速度较慢(比Minor GC慢10倍以上)
1.空间的分配
1)对象优先在新生代Eden区分配 。当Eden区没有足够空间时,将发动一次Minor GC.
2)较大对象需要连续的空间,如长字符串或数组,如果放在新生代会提前触发GC 。故大对象直接进入老年代区域,避免频繁的GC 。
3)长期存活的对象进入老年代,每个对象有一个年龄,在对象头Mark word中记录,刚被创建时年龄为0,当它活过一次Minor GC,并且转移到Survivor中,年龄变为1,此后,在Survivor区中每活过一个Minor GC,年龄就会+1,当年龄达到某个程度(默认为15),就会晋升到老年代 。
4)此外,为了适应内存的复杂情况,年龄不一定达到规定值才能进入老年代 。当Survivor区的相同年龄所有对象大小大于Survivor区大小的一半时,此年龄就会被作为判定标准,大于等于该年龄的都会进入老年代 。
2.空间的回收--GC
这里我用一张图来彻底解释清除:

GC 一篇文章彻底了解Java垃圾收集机制

文章插图
 
需要解释的地方有:担保失败,这个的作用在图上已经解释的很清楚了,可以在JVM参数设置 。
另外一个地方就是平均大小来作比较,因为有多少对象晋升到老年代是无法知道的,所以只好取之前每一次晋升到老年代的对象的容量的平均值大小来作为经验值,来决定是否进行Full GC来让老年代腾出更多空间 。如果仍然失败,那么只能进行一次Full GC 。在我个人开来,之所以使用担保,经验值来尽可能的只进行MinorGC,所有的一切,都是为了尽可能不执行Full GC的情况下将需要申请的内存空间搞定 。




推荐阅读