JVM 内存模型
堆分区
GC 发生在哪里?
JVM 内存区域中,程序计数器,JVM栈和本地方法栈时线程私有的,随着线程的创建而创建,销毁而销毁,每个栈帧分配多少内存都是已知的,因此这三个区域的内存分配和回收都具有确定性。
那么GC的重点就是堆和方法区的内存,堆中是对象的回收,方法区是废弃常量和无用类的回收。
GC 过程理解
- Java 应用创建对象,通常分配在 Eden 区域,当其空间占用到达一定阈值,触发 Minor GC。依然被引用的对象存活下来,复制到
Survivor
区域,没有被引用的对象被回收。 - 经过一次 Minor GC,Eden 空闲下来,直到再次触发
Minor GC
。这时,另一个Survivor
成为to
区域,Eden 区域存活对象和From
区域对象复制到to
区域,并且存活年龄计数 +1. - 第2步发生很多次,直到有对象年龄计数达到阈值,这时发生所谓的晋升(Promotion),超过阈值的对象晋升到老年代。
- 后面就是 老年代 GC,取决于选择的 GC 选项。
老年代 GC 叫做 Major GC
,整个堆清理叫做 Full GC
.
对象什么时候被回收?
当一个对象不在被引用,就代表改对象可以被回收。有两种算法可以判断对象是否可以被回收:
引用计数算法:每个对象有一个引用计数器,被引用+1,引用失效-1,引用=0代表可以被回收了。虽然该方法实现简单,但它存在对象间循环引用的问题。
可达性分析算法:GC Roots 是所有对象的根对象, JVM 加载时,会创建一些普通对象引用正常对象。这些对象作为正常对象的起始点,GC时,从 GC Roots 向下搜索,当一个对象到 GC Roots 没有任何引用链时,表示该对象可被回收了。HotSpot JVM采用的改算法。
强引用,软引用,弱引用,虚引用有什么区别?
不同的引用类型,区别在对象可达性状态和对GC的影响。
引用类型 | 特点 |
---|---|
强引用 | 存在引用,永远不会被 GC |
软引用 | 通过 SoftReference 实现,当要 OOM 时才会回收 |
弱引用 | 通过 WeakReference 实现,只要 GC 就会被回收 |
虚引用 | 通过 PhantomReference 实现,唯一作用是被GC时受到一个系统通知 |
参考地址 https://mp.weixin.qq.com/s/8f29ZfGvZVPe0bO-FahokQ
如何回收对象?
JVM GC 遵循以下两个特性:
自动性:Java 提供了一个系统级的线程来跟踪每一块分配出去的内存空间,当 JVM 处于空闲循环时,垃圾收集器线程会自动检查每一块分配出去的内存空间,然后自动回收每一块空闲的内存块。
不可预期性:对象回收的时期不确定。
GC 线程是 JVM 自动执行的,Java 程序无法自动执行。System.gc
只能建议进行 GC
, 具体什么时候执行?仍然是不可预期。
GC 算法
GC 算法 和具体 JVM 紧密相关,不同的JVM 提供的实现可能不一样。
常见的 GC 算法如下表:
标记-清除算法 Mark-Sweep
标记: 存活对象标记
清除: 回收不可达对象
复制算法 Copying
对象创建区 复制到 空闲区,两个区交换
标记-整理 Mark-Compact
标记: 存活对象标记
整理: 移动所有存活对象,按顺序排列
分代收集
组合回收, GC 主流回收算法,将不同声明周期的对象分配到堆中不同区域,采用不同的 GC 算法。
总结
GC 算法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
标记-清除(Mark-Sweep) | 不移动对象,简单高效 | 效率低,内存碎片 | |
复制(Copying) | 不产生内存碎片 | 内存使用率低,频繁复制 | 对象存活率低 |
标记-整理(Mark-Compact) | 综合前两种优点 | 移动对象,成本较高 | 对象存活率高 |
分代收集(Generational Collection) | 分区回收 |
不同内存区域的回收方式
年轻代
使用 Minor GC,采用复制算法。
- Java 应用创建对象,通常分配在 Eden 区域,当其空间占用到达一定阈值,触发 Minor GC。依然被引用的对象存活下来,复制到
Survivor
区域,没有被引用的对象被回收。 - 经过一次 Minor GC,Eden 空闲下来,直到再次触发
Minor GC
。这时,另一个Survivor
成为to
区域,Eden 区域存活对象和From
区域对象复制到to
区域,并且存活年龄计数 +1. - 第2步发生很多次,直到有对象年龄计数达到阈值,这时发生所谓的晋升(Promotion),超过阈值的对象晋升到老年代。
老年代
存放生命周期较长的对象,使用标记-清除算法或标记-整理算法。
回收器分类
回收器分类一图流:
连线为相互配合的GC
年轻代垃圾回收器
回收器类型 | 回收算法 | 特点 | 参数 |
---|---|---|---|
Serial GC | 复制 | 单线程, Client 模式默认回收器 | -XX:+UseSerialGC |
ParNew GC | 复制 | 新生代GC,Serial GC 的多线程版本 ,通常配合 老年带的 CMS GC 工作 | -XX:+UseConcMarkSweepGC -XX:+UseParNewGC |
Parallel Scavenge GC | 复制 | 多线程,高吞吐,JDK8 默认GC | -xx:+UseParallelGC |
老年代回收器
回收器类型 | 回收算法 | 特点 | 参数 |
---|---|---|---|
Serial Old GC | 标记-整理 | 单线程 | -XX:+UseSerialGC |
Parallel Old GC | 标记-整理 | JDK8 默认GC | -xx: +UseParallelOldGC |
CMS(Concueent Mark Sweep) GC | 标记-清除 | 并发收集,低延迟,高吞吐,内存碎片 | -XX:+UseConcMarkSweepGC(默认会将-XX: +UseParNewGC打开) |
G1GC
回收器类型 | 回收算法 | 特点 | 参数 |
---|---|---|---|
G1 GC | 标记-整理+复制 | 高并发、低停顿、可预测停顿时长 | -XX:+UseG1GC -XX:MaxGCPauseMillis=200(最大停顿时间) |
G1(Garbage First)采用了全新的分区算法。
JDK9 默认GC
查看默认GC
查看 JDK 默认GC配置
java -XX:+PrintCommandLineFlags -version
输出如下
-XX:InitialHeapSize=264301824 -XX:MaxHeapSize=4228829184 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGCation -XX:+UseParallelGC
java version "1.8.0_291"
Java(TM) SE Runtime Environment (build 1.8.0_291-b10)
Java HotSpot(TM) 64-Bit Server VM (build 25.291-b10, mixed mode)
可以看到 -XX:+UseParallelGC
,JDK1.8默认采用的是ParallelGC(新生代),老年代默认就是Parallel old了。