深入理解Java 虚拟机读书笔记--第三章 垃圾收集器与内存分配策略

时间:2022-12-27 15:23:35

1960 年诞生与MIT的LISP 是第一门真正使用内存动态分配和垃圾收集技术的语言;

 

GC需要完成的3件事情:

1.      哪些内存需要回收(what)

2.      什么时候回收(when)

3.      如何回收(how

 

程序技术器,虚拟机栈,本地方法栈随线程而生,随线程而灭, 不需要回收。

Java堆和方法区需要回收, GC主要的工作范围是Java堆

 

判断对象存活与死亡:

引用计数算法(ReferenceCounting) : 简单,高效, 但不能解决对象间循环依赖问题

 

Java,C#,Lisp 等主流语言都通过可达性分析(Reachability Analysis )来判定对象是否存活的;

 

可达性分析

通过一系列的GCRoots 对象为起点,往下搜索, 搜索走过的路径称为引用链。当一个对象到GCRoots 没有任何的引用链相连时, 则证明此对象是不可用的, 可以被回收。

 

GC Roots 对象包括以下几种:

1.      虚拟机栈(栈帧中的本地变量表)中引用的对象

2.      方法区中类静态属性引用的变量

3.      方法区中常量引用的对象

4.      本地方法栈中JNI(即一般说的Native 方法)引用的对象

 

 

判断一个对象死亡, 一般要经过两次标记过程:

1.      可达性分析中判断不可达,  为第一次标记

2.      判断对象是否要执行finalize() 方法。 如果不需要,  则对象直接标记为回收;

如果需要执行finalize() 方法,对象被移至F-Queue 中; GC 对F-Queue中对象进行第二次标记。

 

      Finalize() 方法是对象逃脱死亡命运的最后一次机会,  因为finalize() 方法只会被调用一次。

Finalize() 能做的事情, try-finally 都能做的更好, 不要使用finalize() 方法, 毕竟它是Java 刚诞生是为了c/C++程序员接受而模仿析构函数做的妥协。

 

 

垃圾收集算法:

标记-清除(Mark-Sweep):  基础的垃圾收集算法,   标记和清除两个过程的效率不高, 会产生大量不连续的内存碎片

 

复制算法(Coping):   内存分为两块,  每次只使用一块。 一块用完了, 将存活的对象复制到另一块上面去,  已使用的一块内存空间一次性清理掉。 

 实现简单, 运行高效, 没有碎片;  缺点是有一部分内存不能使用

 

新生代对象98% 是“朝生夕死”,采用复制算法回收:  新生代内存分为Eden, From Survivor,ToSurvivor(HotSpot 虚拟机默认Ecen,From,To 比例为8:1:1), 每次使用Eden和一块Survivor,  回收时将Eden 和Survivor 中还存活的对象复制到另一块Survivior 中,  最后清理掉Eden 和使用过的Survivor.

 

标记-整理(Mark_Compact):标记过程一样, 标记过后进行整理: 让所有存活的对象向一段移动,  然后清理掉端边界外的内存。

 

 

分代收集

Java 堆分为新生代,老年代。

新生代每次GC 都有大量对象死去, 只有少量存活, 使用复制算法回收

老年代中对象存活率高, 使用标记-清理或者标记-整理算法来回收

 

STW: 进行GC时必须停顿所有Java执行线程(STW:StopThe World), 以保障一致性:  不能在可达性分析过程中对象的引用关系还在不断的变化。

 

 

垃圾收集器:

收集算法是方法论, 收集器是算法的具体实现。

 深入理解Java 虚拟机读书笔记--第三章 垃圾收集器与内存分配策略

 

 

Young heneration:

Serial :  单线程复制算法,   GC线程工作是会STW 直到收集结束。

              简单高效, VM 在Client模式下的默认新手代收集器

ParNew:  Serial 的多线程版本, 其余行为为Serial一样。 复制算法

                在多CPU 环境下应该更高效。

Parallel Scavenge:   复制算法 , 多线程

                                     控制吞吐量, 吞吐量优先收集器(其他收集器主要关注停顿时间), 适合后台运算而不需要太多交互的任务

 

Old generation:

Serial Old :Serail 的老年代版本, 单线程,  标记-整理算法

Parallel  Old: Parallel Scavenge 的老年代版本, 多线程, 标记-整理算法

CMS(ConcurrentMark Sweep):  获取最短停顿时间为目的的收集器

              标记-清除算法, 多线程

              优点: 并发收集, 低停顿

              缺点: 浮动垃圾, 内存碎片

   其他的收集器在标记和清除/整理过程中都会STW,  CMS 的清理线程和用户线程是并发的, 所以会产生浮动垃圾(Floating Garbage), 即并发的清理过程中新产生的垃圾。

CMS之所以使用标记-清除算法的原因也在这里, GC线程和用户线程都在工作, 整理对象是不合适的。

 

G1: 太复杂, 作者也不怎么了解

 

 

对象分配策略:

1.      大多数情况下, 对象在新生代Eden 区中分配,  当Eden 区中没有足够空间时, 虚拟机将发起一次MinocGC

2.      大对象直接进入老年代

大小由-XX:PretenureSizeThreshold 指定,  大于该值得对象直接在老年代中分配

3.      长期存活的对象进入老年代

对象年龄(Age): 在Eden 出生进过第一次Minoc GC, 进入Survivor, 年龄为1;  在Survivor 中每经历一次Minor GC , 年龄增加1;

对象年龄达到一定程度(默认15岁, 可以由参数 –XX:MaxTenuringThreshold 设置)将会被晋升到老年代。

 

 

 

重新总结下Java 内存布局:

 深入理解Java 虚拟机读书笔记--第三章 垃圾收集器与内存分配策略

 

1.      程序计数器

线程私有

2.      虚拟机栈

线程私有

3.      本地方法栈

线程私有

4.      方法区

线程共享,jdk 8 的hotSpot虚拟机由metaSpace实现方法区, 而不再是以前的“永久代”

5.      Java 堆

        1.      新生代

             Eden:From Survivor : To Survivor 默认为8:1:1

        2.      老年代

 

JDK8 下的一次GC日志可以明确证明这些内存划分:

 [GC (System.gc())[PSYoungGen: 8033K->744K(57344K)] 8033K->752K(188416K), 0.0012865 secs][Times: user=0.00 sys=0.00, real=0.00 secs]

[Full GC (System.gc()) [PSYoungGen: 744K->0K(57344K)][ParOldGen: 8K->617K(131072K)] 752K->617K(188416K), [Metaspace:3209K->3209K(1056768K)], 0.0064976 secs] [Times: user=0.00 sys=0.00,real=0.00 secs]

Heap

 PSYoungGen      total 57344K, used 1474K [0x0000000780380000,0x0000000784380000, 0x00000007c0000000)

  eden space 49152K, 3% used[0x0000000780380000,0x00000007804f0bc8,0x0000000783380000)

  from space 8192K, 0% used[0x0000000783380000,0x0000000783380000,0x0000000783b80000)

 to   space 8192K, 0% used[0x0000000783b80000,0x0000000783b80000,0x0000000784380000)

 ParOldGen      total 131072K, used 617K[0x0000000700a00000, 0x0000000708a00000, 0x0000000780380000)

  object space131072K, 0% used [0x0000000700a00000,0x0000000700a9a450,0x0000000708a00000)

 Metaspace       used 3216K, capacity 4494K, committed4864K, reserved 1056768K

  class space    used 355K, capacity 386K, committed 512K,reserved 1048576K