11-jvm

本文详细阐述了Java虚拟机(JVM)的工作原理,包括类加载过程、运行时数据区结构(如栈、堆、方法区)、执行引擎的作用以及垃圾回收算法,如可达性判断、复制法、标记清除法等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. jvm

java虚拟机, 运行在操作系统层面, 可以隔绝操作系统对java应用程序的影响, 运行在jvm上具备可移植性

jvm提供了java语言运行环境, 用来执行jvm指令

jdk提供了java工具集, 有一系列针对jvm指令的工具

1.1. 类加载器

编译: 编译器(jdk提供的javac也是在编译)将java代码编译成.class文件

词法分析->语法分析->语义分析,最终将java文件编译成.class文件

类的加载:

①类在第一次被调换时,会被类加载器进行加载

②加载类会先加载类导入的包

③类加载器加载类时, 会先使用父类加载器进行加载

④父类加载失败使用子类进行加载(双亲委派)

⑤类加载器加载类, 会将类读取到jvm中(加载), 解释器翻译成机器码

⑥类执行初始化

加载: 类加载器加载.class文件进入jvm内存(加载后, 程序执行和编译文件已经无关, 即使删除.class文件也不影响应用程序的执行)

链接:

​ 验证: 验证代码是否正确

​ 准备: 创建静态变量等,为初始化做准备

​ 初始化: 执行静态等, 类的初始化

初始化:

验证->准备->解析, 加载即是将class文件读入到jvm的运行时数据区, 并且准备好静态执行环境

类加载器:

①启动类加载器(Bootstrap): 加载$JAVA_HOME中jre/lib/rt.jar里所有的class,

②拓展类加载器(Extension): 加载$JAVA_HOME中jre/lib/*.jar里所有的class,启动类加载器和拓展类加载器一起加载了java类库下的所有的类

③应用类加载器(AppClassLoader): 加载java编译器编译后的所有类(自定义类, 在./out/路径下)

④自定义类加载器: 自定义

类加载器采用双亲委派原则加载, 加载时会逐级先使用父类类加载器加载, 父类加载失败会采用子类加载

原因: 类加载时, 会先加载该类import的文件, 该import的文件可能被父类加载器加载过了

1.2. 运行时数据区

jvm管理的所有内存

包括本地方法栈, 虚拟机栈, 堆, 方法区, 程序计数器, 方法区, 常量池

1.3. 执行引擎

执行jvm代码, 调度运行时数据区空间分配

本地方法存在本地方法库, 执行交给本地方法执行引擎, 本地执行引擎执行结束交给执行引擎

①加载完成后, 执行引擎在方法区创建空间, 存放静态, 常量, 类信息, 方法信息, 若实例了类则会在堆的新生代Eden中分配空间创建实例

②执行引擎在栈中创建空间, 栈中分配栈帧, 栈帧执行方法, 本地变量表保存对堆的引用, 操作数栈中指向jvm指令, 在动态链接中保存对调用的方法的引用

③调用方法生成新的栈帧, 进行压栈操作, 逐步执行完方法调用的栈帧, 然后执行方法,

④执行引擎根据栈帧的正常返回或异常返回做出响应

2. 程序计数器

绑定线程, 相当于指针, 指向栈中下一行执行的地址值

每一个线程都存在一个程序计数器

3. 栈

包括java虚拟机栈和本地方法栈

应用程序执行再栈帧, 其中有着对象的引用

执行引擎执行方法时, 栈会给该方法分配一个内存空间(栈帧)

栈帧:

​ **本地变量表: **存储对堆中类的实例的引用

操作数栈: jvm的操作指令(栈中具体执行jvm指令的地方)

动态链接: 调用别的方法, 对别的方法的引用

正常方法执行: 方法正常执行完成

中断方法执行: 方法异常

当生成栈帧填满栈会栈内存溢出

4. 堆

存放对象实例

4.1. 年轻代

Eden(伊甸园): 新创建的对象都会在Eden创建内存空间, 当Eden内存达到阈值会进行MinorGC, Eden占比年轻代80%

survivor(幸存者区): 包括from区和 to区, 垃圾回收使用MinorGC执行垃圾回收算法使用复制法, 两个survivor区都占比年轻代(10%)

4.2. 老年代

年轻代进行一定次数(默认15次)MinorGC后进入老年代, 或者对象比Survivor内存大, 在一次MinorGC后也会直接进入老年代

老年代满了之后会进行FullGC, 当fullGC之后再在老年代中存储比剩余空间还大的实例时, 会OOM

4.3. 永久代(jdk8以前)

存放方法区和共享常量池, jdk1.8移除永久代, 方法区移动到元空间, 永久代在堆内, 元空间在堆外

5. 方法区

存放类的信息, 静态信息, 方法信息, 即时编译器编译后的代码缓存

即时编译器编译后的代码缓存超出方法区内存也会导致OOM

元空间:

  • 元空间在堆外

  • 默认直接操作物理内存

  • 方法区放进了元空间, 常量池放进了堆中

6. 运行时常量池

存放常量, 在方法区中

7. GC算法介绍

7.1. 垃圾判断

引用计数法: 判断对象的引用次数来决定是否回收对象, 目前没有垃圾收集器采用这种

可达性算法: 这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在Java语言中,可以作为GC Roots的对象包括下面几种:

  • 虚拟机栈(栈帧中的本地变量表)中的引用对象
  • 方法区中的类静态属性引用的对象。
  • 方法区中的常量引用的对象。
  • 本地方法栈中JNI(Native方法)的引用对象

真正标记以为对象为可回收状态至少要标记两次。

第一次标记:不在 GC Roots 链中,标记为可回收对象。

第二次标记:判断当前对象是否实现了finalize() 方法,如果没有实现则直接判定这个对象可以回收,如果实现了就会先放入一个队列中。并由虚拟机建立一个低优先级的程序去执行它,随后就会进行第二次小规模标记,在这次被标记的对象就会真正被回收了!

7.2. 垃圾回收算法

复制法 : 将内存平均分为两部分, from区存储, 存满后将存活的对象复制到to区, 然后清空from区, 循环往复(默认15次),

标记清除法: 存储满了后, 第一次遍历对应该回收的对象进行标记, 第二次遍历对标记对象进行清除

标记压缩法: 基于标记清除法, 清除后进行压缩, 将所有存活的对象都向一端移动, 以便空出连续空间

8. GC

进行GC时, 其他所有线程都会停止运行, 只有GC线程可以正常执行.

筛选垃圾使用的是可达性判断算法,

可达性算法通过GC-ROOT作为根, 来分析执行链, 若执行链没有对象的引用则判断为垃圾, 进行清除

minorGC: 发生在Survivor区, 使用复制算法, from区复制所有存活对象到to区, 然后清除from区, 完成一次GC, 然后from和to区调换,

majorGC: 发生在老年代, 使用标记清除法和标记整理法进行GC, 每一次MajorGC几乎都会伴随着一次MinorGC

对象经过MinorGC一定次数(默认15次)后会被移送老年代, 或者对象比Survivor内存大经过一次GC也会送入老年代

fullGC: 一次MinorGC+MajorGC, 组合清理, 很慢, 机会每一次majorGC都会是FullGC, 应尽量避免FullGC

, 使用标记清除法和标记整理法进行GC, 每一次MajorGC几乎都会伴随着一次MinorGC

对象经过MinorGC一定次数(默认15次)后会被移送老年代, 或者对象比Survivor内存大经过一次GC也会送入老年代

fullGC: 一次MinorGC+MajorGC, 组合清理, 很慢, 机会每一次majorGC都会是FullGC, 应尽量避免FullGC

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值