前言
对新生代分区的思考有感,供参考,前提是了解一些垃圾回收算法,如标记-清除、标记-整理、标记复制等,文末有补充
为什么要分为Eden和Survivor?
只用一个Eden区不就好了,通过标记-整理算法也解决了内存碎片的问题啊
主要还是效率问题,效率不够高,标记整理算法需要遍历整个内存区域,效率太低
YoungGC是比较频繁的,加上大部分对象都是会被清理的,如果采用了标记整理,就会导致遍历太多无用的对象
此时标记复制就非常合适了,新生代空间小,复制的代价不会很大,也不需要遍历那么多无用的对象
通常我们说复制,都是从一个地方复制到另一个地方,不会说复制回自己本身,那我就想复制回本身不可以吗?可以
在分Eden和Survivor,我认为跟职责划分也有关系,对象是从Survivor晋升到老年代的,可以这样理解
Eden区是用来筛选可能长时间存活的对象的
Survivor是用来管理对象晋升到老年代的时机的
为什么要设置两个Survivor区
设置两个Survivor区最大的好处就是解决了碎片化,刚刚新建的对象在Eden中,经历一次Minor GC,Eden中的存活对象就会被移动到
第一块Survivor0,Eden被清空,等Eden区再满,就再触发一次Minor GC,Eden和Survivor0中的对象又会被复制送入Survivor1中
这个过程非常重要,因为这种复制算法保证了Survivor1中来自Survivor0和Eden两部分的存活对象占用连续的内存空间,避免内存碎片
- 那到底为什么能保证?如果只有一块Survivor和一块Eden作为新生代不可以吗?
是吧,好像看上去是没啥问题,但是就是忘记了Survivor也是会清理对象的,那这个是又要用什么回收算法?
- 标记清除:不还是会导致内存碎片
-
标记整理,效率低,没有标记复制效率来的快,具体看上一个部分,只有Eden部分中有考虑这个
-
标记复制,先在Eden复制到Survivor,Survivor再复制一遍再放到自己这个里面,你不觉得很怪吗
所以将Survivor区分为了Survivor0和Survivor1,让Eden和Survivor同时进行复制操作后移动到Survivor2
然后Survivor0和Survivor1互换,不断重复上述步骤,如果Survivor1存不下了,再次发生YoungGC,然后将达到一定年龄的对象
放入老年代
标记-清除
直接将该被回收的对象直接进行回收
缺点 :
-
会导致大量的内存碎片,若此时由一个对象要存入堆中,此时的内存剩余总量是足够的,
可是这些内存都是不连续的,还是会导致对象无法存入堆中。
标记-整理
将该被回收的对象回收后,会将内存进行整理,解决了内存碎片的问题
缺点 :
- 因为涉及到了内存地址的变更重组,导致效率确实是非常低,需要遍历整个内存空间
标记-复制
缺点 :需要占用双倍的内存,空间换时间了