首先gc回收的对象是堆区和方法区的垃圾对象,栈内的垃圾不在gc回收的范围内
如何判断是否是垃圾
- 引用计数法
为每个对象创建一个计数器,每被引用一次,计数器就+1;没被释放一次,就-1。直到计数器等于0时,才标志为垃圾。
缺点是无法避免循环引用的问题。即A引用B,B引用A,此时A、B永远无法被回收。
- 可达性分析
不能通过引用链连接到gc root的对象,都可以标记为垃圾对象。gc root有四种 虚拟机栈,本地方法栈引用的对象,方法区中静态属性引用的对象,常量引用的对象。
四大回收算法
- 标记清楚法
将所有认为是非垃圾对象的标记出来,然后清楚剩下的对象。
缺点
- 回收后的内存空间碎片化严重,可能无法存放大体积的对象。
- 复制算法
为了解决碎片化的问题,首先将内存区域划分为两块。每次只使用一块,然后将这一块上所有标记可达对象复制到另一块区域连续内存上,并清除原来一块,从而解决碎片化的问题。
缺点
- 只能利用50%的内存区域
- 如果可达对象多,复制的次数就过多,影响效率
- 标记整理法
基于标记清除法的算法,为了解决碎片化严重的问题,和内存最多只能利用50%的问题。再完成标记可达对象后,将这些对象都会压缩到内存的一边,然后清除剩下的垃圾对象。
缺点
比标记清除法的效率更低。
- 分代收集算法
现代商业垃圾回收器的解决方案,针对不同区域,分别使用以上的算法。
根据对象的生命周期,可以划分为新生代、老年代、永生代。
- 新生代,通常新建的对象都在新生代。新生代内部按照8:1:1的比例划分为eden区和两个survivor区(from区和to区)。触发gc时,首先会将eden区存活对象复制到to区,from区的对象有两个去处,如果年龄到了阈值以后,就会转移到老生代,如果没有就会年龄+1然后移到to区。然后清空eden区和from区。此时to区变成了from区,from区变成了空的to区。
新生代的gc叫做mirror gc,频率高,耗时短。
- 老年代,在新生代经历了N次gc后依然存活的对象,可以认为是不太会被回收的,会转到老年代。大小是新生代的2倍左右。老年代的回收叫做full gc ,耗时长,频率低。
- 永生代,用于存放一些静态文件,如java类和方法。
基于每个区域特性,时刻有大量垃圾对象被回收的新生代采用复制算法。大量对象存活,而只有小部分被回收的适合标记清除/整理法。