学习垃圾回收算法之前,我们先来了解一下Java中方法区的回收:
1.Java中方法区的回收主要收集两部分内容:废弃常量和无用的类
1)废弃常量:一般来讲,在垃圾回收时,没有被任何对象引用的常量将会被回收。
2)无用的类:只有同时满足一下三个条件才算是无用的类。
- 该类的所有实例已经被回收
- 加载该类的类加载器已经被回收
- 该类的Class对象没有在任何地方被引用,无法在任何地方通过反射来获取该类的所有内容
接下来我们来看垃圾回收算法:
1.标记—清除算法:
- 算法思想:根据名字其实就可以看出来,这个算法其实包含两部分,“标记"和"清除”。先标记出所有需要被回收的对象,然后再统一回收这些对象即可。
- 缺点:这样的算法一方面效率不够高,另一方面会产生大量的不连续空间。
后续的这些垃圾回收算法其实都是对"标记—清除算法"的优化:
2.复制算法:又叫新生代回收算法
- 算法思想:按照1:1的比例来划分内存空间,每次只使用一块这样的空间。当这块内存需要进行垃圾回收时,将这块内存上的所有存活的对象复制到另一块区域,然后将这块区域清理掉。
- 优点:不会产生大量的不连续空间碎片,而且还能提高效率。
- 缺点:由于新生代中大部分对象都是"朝生夕死"的,所以1:1来分配内存空间的方式,利用率并不高。
-
优化:将内存空间按8:1的比例来进行划分。
Eden:Survivor From:Survivor To = 8:1:1;
具体过程: -
首先,将对象放置Eden区,当Eden区满时,会触发第一次gc,将还存活的对象复制到Survivor From区。
-
第二次触发gc的时候,扫描Eden和Survivor From区,将存活的对象复制到Survivor To区,然后清空扫描区。
-
第三次触发gc的时候,扫描Eden和Survivor To区,将存活的对象复制到Survivor From区,然后清空扫描区。
-
后续触发gc的时候,重复上述步骤。当一个对象在Survivor区中的From与To中来回复制交换15次后还是存活,则将此对象存入老年代。
总结:
从上我们不难分析出,除了第一次,后续每次扫描的都是不为空的两块区域,然后将存活的复制到另一块为空的区域;当Survivor空间不够用时,需要依赖老年代进行分配大担保。
3.标记—整理算法:又叫老年代复制算法
- 算法思想:与标记—清除类似,不同的是,在标记完所有要回收对象后,并不进行清除,而是让所有存活的对象向一端移动,最后清除存活区外的内存。
- 优点:既不会产生大量的不连续空间碎片,也能提高效率。
了解了以上几种垃圾回收算法之后,我们就可以来看一下JVM所采用的"分代收集算法",其实这个算法就是根据对象存活周期的不同来采用不同的算法。
4.分代收集算法:
将Java堆分为新生代和老年代。
- Minor GC:新生代GC,新生代对象大部分是朝生夕死的,所以采用复制算法。
- Full GC:又叫Major GC或老年代GC,老年代中存活率较高,没有多余空间进行担保,所以采用"标记—整理"算法或"标记—清除"来进行垃圾回收。
垃圾回收算法其实是垃圾回收的方法论,而垃圾收集器就是内存回收的具体实现。以下是针对我们的垃圾回收算法所举例的垃圾收集器,博主只是了解:
- 基于"标记—清除"的垃圾收集器:CMS老年代收集器,并发GC(用户线程与垃圾回收线程同时执行)
- 基于"复制算法"的垃圾收集器:Parallel Scavenge新生代收集器,多线程收集器(多条垃圾回收线程并行处理)
- 基于"标记—整理"算法的垃圾收集器:Parallel Old老年代收集器,多线程收集器
要注意的是,G1是唯一一款全区域的垃圾回收器,既能回收新生代,又能回收老年代。