Java GC的理解
GC的步骤
1. 判断垃圾对象
在进行垃圾回收之前,先判断哪些对象应该被回收,将其标志出来。java提供两种策略:
1. 1 引用计数法
当一个对象有一次引用时,计数+1。计数为0时,代表该对象没有任何引用,将其标志。但无法解决循环引用问题。如下图,堆中两个对象相互引用,但栈中没有引用指向它们,此时他们的计数都为1,不能被回收,但实际上他们已经为垃圾数据。
1.2 GC Roots可达性分析
以GC Roots对象做为起点,如果该起点与某个对象没有有可达性的连接,则代表该对象可被回收。
可做为GC Roots的对象:
- 虚拟机栈中引用的对象
- 方法区中常量引用的对象
- 方法区中类静态属性引用的对象
- 本地方法栈中JNI(即一般说的Native方法)中引用的对象
2. 垃圾回收算法
将堆内存分为新生代和老年代。因为新生代只有少量对象存活,通常使用复制算法;而老年代对象存活率高,通常使用标志 - 清楚算法或者标志 - 整理算法
2.1 标志 - 清除算法
该方法效率高,直接把标记好的对象清除,把占用的空间置为可用。但是会产生碎片化的内存,导致没有足够大的空间存储大内存对象。
2.2 复制算法
将新生代分为三个区(Eden、Survivor1,Survivor 2,空间占比8:1:1 )。
- 第一次new出来的对象放在Eden区
- 第一次GC算法后,将没被回收的对象放到survivor1区,且年龄+1,同时清空Eden区所有对象
- 第二次new出来的对象放在Eden区
- 第二次GC算法后,将没被回收的对象放到survivor2区,且年龄+1,同时清空Eden区和survivor2区所有对象
- 依次循环
- 当年龄达到一定值(CMS规定6,PS、PO规定12),将存活对象放入老年代。
2.3 标志 - 整理算法
将已标志的对象一边清除,一遍整理,使得GC过后,内存还能连续,解决碎片化问题。
3. 常用的垃圾回收器
3.1 Serial、Serial Old
串行回收器:停止当前所有工作线程,启动一个GC线程进行垃圾回收。GC线程回收完成后,工作线程方可继续执行。Serial是新生代的回收器,使用的是复制回收算法,Serial Old是老年代的回收器,使用的是标记 - 清除算法或者标记 - 整理算法。(jdk1.1,jdk1.2默认回收器)
3.2 Parallel Scavenge、Parallel Old
并行回收器(PS、PO):串行回收器的多线程版本,停止所有工作线程后,启动多个GC线程去进行回收工作。Parallel Scavenge是新生代的回收器,Parallel Old是老年代的回收器 (jdk1.8默认回收器)
3.3 Parnew、CMS
ParNew: 针对新生代的回收器,停止所有工作线程后,启动多个GC线程进行回收工作(同Parallel Scavenge,只不过Parallel Scavenge是与Parallel Old搭配使用的,ParNew是与CMS搭配使用的)
CMS(Concurrent Mark Sweep): 针对老年代的回收器
- 初始标记(CMS initial mark):停止所有工作线程,标记 GC Roots 能直接关联到的对象(使工作线程的停顿时间降到最低)
- 并发标记(CMS concurrent mark):找出其他未标记的存活对象,此时与工作线程并发执行,所以会出现误标,漏标现象
- 重新标记(CMS remark):修正并发标记期间出现误标,漏标的现象
- 并发清除(CMS concurrent sweep):将未标志的对象清除,不整理压缩内存空间,因此会产生碎片化
3.4 G1
todo
本文地址:https://blog.csdn.net/qq_36390175/article/details/107352625