问题1:什么是JVM?
问题2:JVM的内存区是如何划分的?(运行时数据区)
JVM 运行时数据区:堆、栈、方法区(JDK8之前是 永久代实现的,之后就是元空间了)、程序计数器。其中堆和方法区是线程共享数据。
在Java虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配。
堆是垃圾回收管理器管理的主要区域。
GC: 垃圾回收机制
1、什么是垃圾回收机制(什么是GC?)
程序在运行的时候会产生大量的内存垃圾,为了确保运行是的性能,JVM在程序运行过程中,不断的进行自动的垃圾回收(GC)。
1.1 什么是"垃圾"?
一些没有引用指向的内存对象都属于内存垃圾,因为这些对象已经无法被访问到了,程序用不了他们了,对程序而言,他们已经死亡。
总结:程序中不再被任何存活对象引用的对象。
2、什么是GC Roots
对象可达性的起点:
- 虚拟机栈中的局部变量引用
- 本地方法栈中的JNI引用的对象
- 方法区中的静态变量引用
- 方法区中常量引用
- 所有被同步锁持有的对象
- JVM内部引用(如系统类加载器)
3、垃圾判定算法
引用计数法(不推荐使用)
- 原理:每个对象维护一个引用计数器,引用增减时更新计数器。计数器=0时判定为垃圾
- 缺点:无法解决循环引用问题(A->B ,B->A)。
可达性分析算法
- 原理:从 GC Roots 出发,遍历所有引用链。不在引用链上的对象即为垃圾。
- 步骤:
- 1、枚举所有GC Roots。
- 2、递归标记所有可达对象
- 3、清除所有未标记的对象(或将其回收)。
4、垃圾回收算法
标记-清除
- 过程:标记存活对象 -> 清除未标记对象。
- 缺点: 内存碎片化,效率低(需要扫描全堆)
- 适用场景:老年代回收(如:CMS的并发标记阶段)
复制算法
- 过程:将内存分为两块(如 From / To)。将存活的对象复制到To区 -> 清空From区。
- 缺点:内存利用率低(需要保留一半空闲空间)
- 优点:无内存碎片、效率高(仅扫描存活对象)
- 适用场景:年轻代回收(如:Minor GC )
标记-整理
- 过程:标记存活对象 -> 将对象移动到一端 -> 清理边界外的内存
- 优点:无碎片。内存利用率高
- 缺点:移动对象开销大(需要新引用地址)
- 适用场景:老年代回收(如:Serial Old、Parallel Old)。
分代收集(重点)
- 核心思想:对不同代使用不同算法
- 年轻代:对象死亡率高 -> 复制算法(高效)
- 老年代:对象存活率高 -> 标记-清除 或 标记-整理 (减少复制开销)。
- 核心思想:对不同代使用不同算法
5、堆内存模型与分代设计
Young Generation 年轻代
- Eden 区(伊甸园)
- 新创建的对象都会分配到Eden区(一些大对象特殊处理)。
- Survivor(辛存者)
- From-S0
- To-S1
- 分配比例:(Eden)8:(S0)1:(S1)1
- Eden 区(伊甸园)
Old Generation 老年代
MetaSpace 元空间
- 以后会出一篇专门的文章说这个事(JDK 8 后元空间取代了永久代)
- 如果感兴趣可以去了解:什么是永久代、永久代的缺点、永久代被替换的原因、元空间特点、元空间内存查看分析方法(以后的文章也是围绕这几点讲的)
6、核心回收流程(以分代收集为例)
对象分配
- 新对象 --> Eden区
- 大对象 --> 直接进入老年代
- 大对象是我们设置 : -XX:PretenureSizeThreshold
Minor GC 触发条件
- Eden区满时触发,执行过程
- 在GC开始的时候,对象只会存在Eden区和名为“From-S0"区,“To-S1”区是空的。
- 紧接着进行GC,Eden区中所有存活的对象都会被复制到To-S1区,
- 在From-S0区中仍然存活的对象会根据年龄决定去向:年龄达到一定值的对象会被移动到老年代中。没有达到阈值的对象会被复制到To-S1区
- 年龄阈值:XX:MaxTenuringThreshold设置
- 经过这次GC后,Eden区和From-S0区已经被清空。
- From-S0 和 To-S1 会交换他们的角色,也就是新的To-S1就是上次GC前的Form-S0,不管怎么样,都会使To-S1是空的。
对象晋升规则
- 年龄阈值:XX:MaxTenuringThreshold=15(默认)
- 动态判定:当某年龄对象大小 > Survivor区50% -----> 大于等于该年龄的对象集体晋升
Major GC / Full GC 触发
- 老年代空间不足
- 元空间不足
- 显示调用 System.gc()