根据《深入理解java虚拟机》这本书总结
一、关于几个概念:(标记垃圾算法、垃圾收集算法、垃圾收集器)
前面说了如何寻找jvm垃圾,有两种方法:引用计数法/可达性算法。这篇准备讲,标记完垃圾之后,回收的算法,这里的算法只是垃圾回收的思想。后面会讲到多种垃圾收集器,这里的垃圾收集器就是运用了垃圾手机算法的思想,可以说是具体实现。
这里还是想多余的说一下这三个概念:
垃圾标记算法:标记垃圾的方法
垃圾收集算法:一种回收思想,供垃圾收集器使用。可能用在年轻代,也可能用在老年代(当然现在来说老年代和年轻代的算法基本是固定的)
垃圾收集器:垃圾回收算法的实现,真正回收垃圾者。一个垃圾收集器可以用在不同的区域,可以跟不用的垃圾收集器搭配使用,去回收标记了的垃圾。
二、三种垃圾收集算法
1、标记-清除:标记垃圾-->直接清除垃圾
这种算法有两个缺点:1、效率:标记和清除的过程都是很耗时间的。2、空间问题:因为内存分配是一块一块的,直接清除标记的垃圾,没有进行空间整理,会产生很多垃圾碎片,可能所有可用空间较大,但是没有连续的大空间,导致大对象不能存储。
2、复制算法:复制算法显然需要至少两块区域,垃圾收集的时候将可用对象按顺序复制到一块空区域,原区域直接清除。这样就解决了空间碎片的问题。具体思想如下:
(1)将内存分为两块相等的区域,分配内存时候只使用一块区域,当该区域满了,需要gc的时候,标记后把该区域可用对象复制到另外一个区域,并且清除。但是这样的做法导致内存利用率不高,只有一半的内存可以在一个时间内使用。
(2)将内存分为三个区域:较大的Eden区域、两个较小的survivor1和2区域,分配内存的时候,只分配给Eden,当gc的时候,将eden可用对象复制到空闲的survivor1中,清除eden。继续分配内存,分配给eden,当需要再次gc,将eden和survivor1中可用对象复制到survivor2中,如此循环。
一般来说,现在都用第二种方法,这种方法的空间利用率得到提高,三个空间的占比一半为:8-1-1,利用率在90%。但是这样,因为复制的时候,只能复制给百分之10的区域,所以需要内存中对象垃圾占比较多。所以这种方法,一般来说都用在新生代,这些对象大多数都是创建后就需要回收的,当然,如果真的发生了survivor不够放置的情况,这里有个空间分配担保机制,一般会分配给老年代区域。
3、标记-整理:标记垃圾--->可用对象整理
(1)标记-整理算法,比较标记清除算法而言,优化了空间碎片的有点,将可用对象都向前移动。
(2)相对于复制算法而言,复制算法比较适合对象存活率不高的区域,对于新生代来说比较适合。但是对于老年代,对象存活率很高。这时候用标记整理的算法,会更合适。
三、关于年轻代和老年代
一般的内存都会分为年轻代和老年代。什么样的对象会出现在年轻代?什么样的对象会出现在老年代?年轻代的话一般对象都是优先分配给年轻代的。老年代的情况有以下几种:
1、大对象:jvm可以设定值,如果对象过大,会直接放入老年代,入数组啊什么的。
2、年轻代转移过来:对象分配给年轻代后会有一个年龄属性,每次gc的时候,如果对象还存活,就会给年龄加1,如果大于默认的15/或者相同的年龄大于内存的一半的时候可以不达到设定年龄,就会转移到老年代。
可以看出,存活率比较高的对象,一半都会放置在老年代,所以对象上面的算法来说:
老年代:标记-清除、标记-整理
年轻代:复制算法