JVM之 GC(垃圾回收)

当进程启动后会创建垃圾回收线程,来回收内存中无用的对象
1.垃圾回收的时机
(1)System.gc()
显示的调用此方法, 建议JVM进行fgc(full gc),虽然只是建议而非一定,但多半都会进行fgc, 增加fgc的频率,所以一般不用此方法,而是让JVM自己管理它的内存

(2)jvm 垃圾回收机制决定
创建对象需要分配内存, 当内存不足时 出发GC;
finalize(),java.lang.Object中的方法,当JVM发现不在有引用指向某对象时,垃圾收集器会在对象上调用该方法
2.垃圾回收策略—如何判断对象可回收
2.1引用计数算法
给对象添加一个引用计数器,当有引用指向该对象时,计数器加1 ,当引用失效时,计数器值减一, 当值为0时,说明没有引用指向该对象. 该算法实现简单,效率高,但它很难解决对象之间循环引用的问题

2.2可达性算法
把一系列称为GC Roots的对象作为起始点,从这些起始点向下搜索,搜索走过的路径称为GC Roots引用链,当对象到GC Roots 没有任何引用链相连(从GC Roots到这个对象不可达)时,说明该对象是不可用的。
如下图,object5 和object6 到GC Roots是不可达的,因此被判定为可回收对象
JVM之 GC(垃圾回收)
3.需要垃圾回收的内存划分
3.1方法区(JDK1.7)或元空间(JDK1.8)
JDK1.7的方法区在GC中被称为永久代
JDK1.8的元空间在本地内存中,GC也是对元空间的垃圾回收
主要回收废弃的常量和无用的类, 此区域GC的"性价比"很低
3.2堆
java堆是垃圾收集器主要管理区域,也被称为"GC堆";
由于垃圾收集器 分代 采用不同的收集算法, 所以 java堆可细分为:
JVM之 GC(垃圾回收)
(1)新生代(Young Generation) 又可分为Eden ,from surviver ,to surviver
发生在新生代的GC 称为young GC(YGC) 或Minor GC ,因为java对象大多具备朝生夕灭的特性,所以manor GC发生的很频繁,而且回收速度很快.
(2)老年代(old Generation) 发生在老年代的GC被称为Major GC.出现了Major GC,经常会伴随至少一次的Minor GC(但非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程)。Major GC的回收速度比Manor GC的速度要慢10倍左右.
(3)Full GC 在不同的语义中,定义也不同,可以是老年代GC,可以是全堆(老年代+新生代),也可以指用户线程暂停(stop-the-world)的垃圾回收.

4.垃圾回收算法
4.1标记–清除算法(Mark Sweep)
老年代收集算法.
分为标记和 清除 两个阶段,先把所有回收对象标记上,在统一清理,
不足:①效率不高,② 会产生大量不连续的内存碎片,导致后续为大对象分配内存时,没有足够的连续的内存,就会提前触发垃圾收集动作
4.2标记–整理算法(Mark Compact)
老年代收集算法
和"标记–清除算法"过程一致,但清除阶段不是直接清除,而是把存活对象移动到一端,然后清除掉边界以外的内存
4.3复制算法(Copying)
新生代收集算法
把内存按容量分为两块大小相等的区域, 每次只用一块儿,当这一块要用完了就把所有存活对象复制到另一块上去,然后把这一块儿全部清理掉.
实现简单,效率高
不足:内存利用率太低了

5.垃圾回收过程
当eden空间不足时,会触发Manor GC:(对象优先在Eden上分配),把Eden和用过得Surviver中存活的对象复制到另一块Surviver上,然后把Eden和之前那块Surviver清理掉
JVM之 GC(垃圾回收)
年老对象进入到老年代: JVM给对象定义一个对象年龄计数器,当对象经过一次Manor GC后仍然存活,那么年龄加一,当增加到一定值后(默认15),就进入到老年代.
当surviver内存不足时,通过分配担保机制,存活对象进入到老年代.

6.垃圾收集的影响

6.1用户线程暂停–stop-the-world (STW)
垃圾回收工作要在垃圾回收线程里进行,所以,多数时候,当进行垃圾回收时,或进行期中一步骤时,会暂停用户线程.
垃圾回收需要先标记,在标记对象时,用户线程并发执行时,可能把已标记的对象重新加入引用链,此时,再回收这个对象就会产生问题.

所以用户线程会在特定安全点停下来开始GC;
①抢先试中断:不需要线程的代码主动配合,而是在发生GC时把所有线程中断,若不在安全点,就恢复线程,让它跑到安全点.(此方法用的极少)
②主动式中断:在执行GC时,会设置一个标志,线程主动去轮询这个标志,当中断标志为真时就自己中断挂起, 轮询标志的地方和安全点是重合的.
6.2评价垃圾回收器的指标----吞吐量和停顿时间(用户体验)
吞吐量:CPU用于运行用户代码的时间 除以 CPU总的运行时间 , 即吞吐量=运行用户代码时间/
(运行用户代码时间+垃圾收集时间)
单次停顿时间与总的停顿时间一般是反比
用户体验优先:用户线程单次停顿时间短,即使总的停顿时间长一点也可以接受。
吞吐量优先:用户线程总的停顿时间短,即使单次停顿时间长一点也可以接受。

7.垃圾收集器
JVM之 GC(垃圾回收)
1.CMS收集器(老年代收集器,并发GC)
特性:
并发收集、低停顿
“标记-清除”算法
整个过程分为4个步骤:

  1. 初始标记(CMS initial mark) 初始标记仅仅只是标记一下GC Roots能直接关联到的对象,
    速度很快, 需要“Stop The World”。
  2. 并发标记(CMS concurrent mark) 并发标记阶段就是进行GC Roots Tracing的过程。
  3. 重新标记(CMS remark) 重新标记阶段是为了修正并发标记期间因用户程序继续运作而导
    致标记产生 变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍
    长一些,但远比并发标 记的时间短,仍然需要“Stop The World”。
  4. 并发清除(CMS concurrent sweep) 并发清除阶段会清除对象。
    缺陷:
    ①抢占CPU资源
    ②无法处理浮动垃圾:由于并发清理过程,用户线程也在执行,所以会产生对象和垃圾, 而产生的垃圾没有标记 ,需要累积到下一次CMS集中处理,所以,需要预留一部分空间给并发执行的程序. 如果在清除阶段,内存不足,就会临时调用单线程的Serial Old收集器来重新进行老年代的垃圾收集,这样的话,CMS原本降低停顿时间的目的不仅没完成,和直接使用Serial Old收集器相比,还增加了前面几个阶段的停顿时间。
    ③会产生不连续的空间碎片—由"标记-清除"的算法决定.