JVM原理+GC自动垃圾回收机制原理(详细且通俗易懂)

本文详细介绍了Java虚拟机(JVM)的概念、内存模型及其工作原理,包括类加载过程、执行引擎和垃圾回收机制(GC),并解析了JVM如何管理内存以提高程序运行效率。

什么是JVM?
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。 JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。
在这里插入图片描述
JVM内存模型分析
1.类加载器: 将class文件加载到内存
2.运行时数据区:包括方法区、堆、Java栈、PC寄存器、本地方法栈

a.程序计数器:用于存储每个线程下一步将执行的JVM指令;

b.虚拟机栈:方法的执行
  栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构(栈帧存储了方法的局部变量表、操作数栈、动态链接和方法返操作数栈、动态链接和方法返回地址等信息。每一个方法从调用开始至执行完成的过程,都对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程。每个栈帧都包括了局部变量表、操作数栈、动态链接、方法返回地址和一些额外的附加信息。)

c.堆:java堆存放的是解释执行Class文件时创建的对象,随着对象的创建而存在的区域,而这一块区域存在的是该类中的所有属性,每一个变量都有一个默认值,string的默认值为null, int的默认值为0 , double的默认值为0.0 , boolean的默认值为 false, char 的默认值为字符a

d.方法区:存放class的结构信息,例如常量池、字段、方法、构造函数和普通方法的字节码内容

e.运行时常量池:存放class中定义的常量

f.本地方法栈:JVM采用本地方法栈来支持native方法的执行,此区域用于存储每个native方法调用的状态。
  
3.执行引擎: 解释执行加载到运行时数据区的数据(方法区),每次读取一个字节码指令并执行。

执行引擎主要有以下三个部分
①解释器:读取字节码指令并串行执行之。java命令就是一个解释器
②JIT(Just In Time)编译器:
这里的编译不是从java源码到字节码,而是将字节码编译成操作系统特定的指令。JIT会监控字节码的执行过程,将部分常用的代码优化成本地指令,以加速执行。Oracle用了Hotspot虚拟机,会搜索Hotspot(热点)并将其编译成本地指令。
③GC垃圾回收器:清理无用内存以提供更多的可用内存

jvm类加载的过程
分为五个步骤:加载 -> 验证 -> 准备 -> 解析 -> 初始化

1.加载:
查找并加载类的二进制文件(class文件),
把类的信息(例如常量池、字段、方法、构造函数和普通方法的字节码内容)放入方法区,
Class文件对应的实例放入堆中。

2.验证:
确保加载的类信息是正确的

3.准备:
为类的静态变量进行初始化,并分配空间赋予初始值

4.解析:
将符号应用转化为直接应用
解释:
①只要遇到new关键字,则说明会在堆内存中开辟一块空间,用于存放该类的属性
②每当堆内存创建了一个空间之后,则会系统分配一个内存地址,而这个地址是十六进制数
③并将这个地址值赋值给栈内存中的对象名
④对象名和堆内存的空间地址,通过相同的地址值进行链接
在这里插入图片描述
如图例:
Person p1 创建对象后 在堆内存中开辟一块空间 new Person()分配一个内存地址比如0x8899
比如 Person p1 的地址就是0X8899与堆内存中的空间相连接

5.初始化
JVM对类进行初始化。
解释:
java堆存放的是解释执行Class文件时创建的对象,随着对象的创建而存在的区域,而这一块区域存在的是该类中的所有属性,每一个变量都有一个默认值,string的默认值为null, int的默认值为0 , double的默认值为0.0 , boolean的默认值为 false, char 的默认值为字符a
比如上面这个图片中,String name 的 默认值就为null

**

## 二丶自动垃圾回收机制(GC)

当对象填充满堆内存后会导致内存溢出,所以需要内存回收机制GC来清理对象

GC=Garbage Collection 是JVM自动内存管理的机制。JVM运行时数据区中,栈的内存分配在入栈的时候已经确定了,出栈即回收,所以不需要内存回收。需要回收的主要是堆区。主要是以下问题

1.如何判定对象是否应该被删除
举例一个判断标准:GC算法 →GC Root

GC方法一
1) 标记 2)清理
从GC Root对象开始遍历整个JVM的内存,标记出哪些是存活的对象,遍历结束后,未标记的则是不要的对象,即垃圾对象应该被删除。然后删除垃圾对象。

  • 缺点:产生内存碎片。内存碎片什么意思(比如 对象1的大小为1KB 当对象1被清理后会腾出1KB的空间,如果这个时候创建对象2大小为2KB,对象1被清理的1KB空间是放不下对象2的,导致空间不能为合理利用,所以产生GC第二个方法)

GC方法二
1) 标记 2)整理

  • 整理的意思是,前面一个对象被清理后,后面一个对象会自动补齐。这样会减少内存碎片。带来另一个缺点,对象移动的代价太大。所以产生了第三个方法

GC方法三
1) 标记 2)复制
解释:
把整个内存一分为二。第一个内存区域 中标价出来要 清理的对象 。把没有被标记的好对象 全部移入第二个内存区域中。
缺点:需要内存空间大

实际中的GC
在这里插入图片描述
把堆区划分成年轻代(young),和老年代(old)
年轻代中又分为 Eden区 和两个Survivor区
重点:
NEW对象的时候 产生在 Eden区 当Eden区快满的时候就会触发young GC(只属于年轻代的GC)
采用上面方法中的复制方法,把好的对象 放入 Survivor区
(两块Survivor区交替使用 E区+S1区 复制 到 S2区 ***** E区+S2区 复制到 S1区 如此反复)

老年代(old区):
young GC 过程中 每存活一次 年龄age就会加1. 如果age>=6.就往老年区中复制
(大对象 例如 →1千万大小的int数组 也会存入old区)
如果老年区也存满了会触发 Full GC 会引起 Stop The World (STW停止全世界)
Stop The World:整个JAVA程序暂停来进行垃圾清理

### Major GC 是否会触发 Full GC机制原理Java 垃圾回收机制中,Major GC Full GC 是两个容易混淆的概念。尽管它们都涉及老年代的垃圾回收,但它们的触发条件执行范围有所不同。 #### Major GC 的定义 Major GC 通常指的是针对老年代(Old Generation)进行的垃圾回收操作。当对象从年轻代(Young Generation)晋升到老年代时,如果老年代的空间不足,则会触发 Major GC 来清理老年代中的无用对象[^1]。 #### Full GC 的定义 Full GC 是一种更为全面的垃圾回收操作,它不仅包括老年代,还可能涉及整个堆内存(包括年轻代、老年代以及方法区或元空间)。Full GC 的触发通常会导致更长的停顿时间,因为它需要扫描清理更多的内存区域[^2]。 #### Major GC 是否会触发 Full GC Major GC 并不一定会直接等同于 Full GC。理论上,Major GC 只负责清理老年代中的垃圾对象。然而,在某些情况下,Major GC 的执行可能会间接触发 Full GC。以下是具体的原因: 1. **老年代空间不足** 如果 Major GC 后仍然无法为即将晋升的对象腾出足够的空间,JVM 可能会扩展堆内存或触发 Full GC 来进一步清理整个堆区域[^1]。 2. **方法区或元空间不足** 在 JDK 1.7 及以前的版本中,方法区是通过永久代实现的。如果永久代空间不足,即使只进行 Major GC,也可能因为无法分配新的类信息而触发 Full GC[^3]。从 JDK 1.8 开始,永久代被元空间替代,但由于元空间依赖于本地内存,当其空间不足时,同样可能导致 Full GC。 3. **CMS 收集器的失败** 使用 CMS(Concurrent Mark-Sweep)收集器时,如果并发清理阶段未能及时完成,或者发生“浮动垃圾”过多的情况,系统会回退到 Serial Old 或 Parallel Old 收集器,从而触发 Full GC。 4. **显式调用 `System.gc()`** 调用 `System.gc()` 方法虽然不一定会立即触发 GC,但如果 JVM 决定执行 GC 操作,通常会触发 Full GC,而不是单纯的 Major GC[^2]。 #### Major GC 触发 Full GC机制 Major GC 触发 Full GC 的核心机制在于 JVM 的内存管理策略。当 Major GC 无法满足当前内存需求时,JVM 会选择升级为 Full GC 来进行全面的垃圾回收。这种升级通常发生在以下场景中: - 老年代碎片化严重,导致无法分配大对象。 - 并发收集器未能及时完成任务,导致内存压力增大。 - 元空间或永久代空间不足,无法加载新类。 #### 示例代码:模拟触发 Full GC 以下代码展示了如何通过创建大量对象来模拟触发 Full GC 的情况: ```java public class FullGCDemo { public static void main(String[] args) { try { while (true) { byte[] bytes = new byte[1024 * 1024]; // 创建 1MB 的数组 Thread.sleep(100); // 模拟业务逻辑延迟 } } catch (OutOfMemoryError e) { System.out.println("Out of Memory: " + e.getMessage()); } catch (InterruptedException e) { e.printStackTrace(); } } } ``` 运行上述代码时,可以通过 `-XX:+PrintGCDetails` 参数观察 GC 日志,分析 Major GC Full GC 的触发情况。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值