009_Java 虚拟机

本文深入探讨了JVM的内存结构,包括类加载器、类加载步骤、类加载器的层次以及类的卸载条件。详细阐述了JVM内存模型,如方法区、堆、栈、程序计数器和本地方法栈的运作方式。重点讲解了垃圾收集的原理,如标记复制、标记清除、标记整理和分代收集算法,以及各种垃圾收集器的工作机制。此外,还介绍了JVM的内存调优策略和常用JVM参数,以及如何利用工具进行性能监控和分析。

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

  • Java中有两种对象:Class对象和实例对象
  • .class静态文件被JVM加载后,会在堆内存中创建一个全局唯一的Class对象
  • 该Class对象中包含了创建实例对象所必需的【元数据和指令】,是创建实例对象的入口

ClassLoader【类加载器】

  • 启动类加载器主要负责【核心类库】的加载[ jre/lib/rt.jar和jre/lib/resource.jar等等 ]
  • 扩展类加载器主要负责【jre/lib/ext】目录下的jar包和class文件
  • 应用程序类加载器主要负责【 当前应用classpath目录】下的所有jar包
  • 自定义类加载器主要负责加载开发者自定义的类

加载原则【双亲委派:按照自上而下的顺序进行加载】

第一步:依次向上委派给父类加载器进行加载【避免了类的重复加载且保证核心类库中的类不被破坏】
第二步:加载成功,则直接返回
第三步:加载失败,则委派给子类加载器进行加载,直到最底层的类加载器

加载过程

  • 触发创建实例对象的条件:
    显示加载:反射。运行期间动态获取类的元信息并调用对象的属性和方法
    隐士加载:1、使用new关键字 2、调用静态属性 3、子类加载前会优先触发其父类的加载
  • 【本地编译期间】把 java 静态文件编译成 class 静态文件
  • 【线上运行期间】将 class 静态文件中的【静态数据结构】加载到【方法区】中的【运行时数据】
  • 【线上运行期间】再在【堆】中生成一个该类的【Class对象】作为访问方法区中运行时数据的入口

卸载条件(同时满足以下3个条件)

  • 所有实例对象都被回收
  • 加载该类的 ClassLoader 被回收
  • Class 对象没有被引用
    注意:静态变量的生命周期取决于类的生命周期。当Class对象被销毁时,静态变量也会被销毁

【JVM内存模型(1.8)】

堆(内存公有)

传统物理机环境:
初始堆大小(-Xms):物理内存的 1/64
最大堆大小(-Xmx):物理内存的 1/4
服务器一般设置-Xms(最小分配内存)和-Xmx(最大分配内存)相等以避免在每次GC后调整堆的大小
  • JVM中最大的一块内存区域,在虚拟机启动时创建,由所有线程共享使用
  • 堆分为年轻带和老年代,年轻代:老年代 = 1:2
1、年轻代(Young Generation)【年轻代中Eden:S1:S2的内存分配比例为8:1:1】
	1.1、Eden区:新创建的对象首先放在Eden区
	1.2、Survivor区:分为From Survivor和To Survivor,存放从Eden区经过垃圾回收后仍然存活的对象
2、老年代(Old Generation):存放长期存活的对象(多次垃圾回收后仍然存活)或者大对象(直接进入老年代)

方法区(内存公有)

  • JVM中定义的一种规范,由所有线程共享使用
  • JDK1.8对其实现的是本地元空间
类的元数据(Class Metadata):
1、类的完整结构信息(父类、接口、字段、方法等)
2、运行时常量池(Runtime Constant Pool)
3、字段和方法的字节码
4、类加载器引用

常量池

在这里插入图片描述

  • 静态常量池【class文件常量池】
    位置:静态存在class文件中,每个 class 文件对应一个
    存储内容:编译期生成的各种字面量和符号引用

  • 运行时常量池
    位置:方法区的一部分
    存储内容:类加载后,静态常量池中的数据进入运行时常量池,并将符号引用转换为直接引用

  • 字符串常量池
    位置:JDK7之前在方法区,JDK7 及以后移至堆区
    存储内容:字符串字面量的引用或实例(遵循享元模式)

栈(内存私有)

  • 随着线程的创建而创建,用于存储方法调用的局部变量、操作数栈等信息
  • 线程每执行到一个方法时便创建一个栈帧入栈,当方法执行完毕栈帧也随之出栈,所有方法执行完毕后线程也就被释放,栈也就随之释放

本地方法栈

与栈类似,但是服务于本地方法(非Java代码)

程序计数器

每个线程独立拥有,指向当前线程正在执行的字节码指令的地址(如果当前执行的是本地方法,则为空)。

【JVM垃圾.收集算法】

1、标记复制

将内存分为两块,每次只使用一块,GC 时将存活对象复制到另一块,清空原区域,这样做的好处是清理速度快。缺点是:始终有一块内存空间处于空闲状态,适用于新生代(Eden + 2 个 Survivor 区,比例 8:1:1)的垃圾回收

2、标记清除

先标记记存活的对象,再清除未标记的对象,缺点是:清理结束后会产生大量的空间碎片,导致大对象无法分配内存

3、标记整理

先标记存活对象,再将存活对象移向一端,最后再清理边界外内存。无内存碎片,适合存活对象多的老年代

4、分代收集(根据对象存活周期进行分类,采用不同算法)

  • 各版本默认收集器
新生代(Young Generation):对象朝生夕灭,使用复制算法
老年代(Old Generation):对象存活久,使用标记-清除或标记-整理
元空间(Metaspace):存储类元数据,GC不频繁
  • GC 触发条件
1Minor GC(新生代 GC):Eden 区满时触发
2Major GC/Full GC(老年代/整堆GC)

【JVM垃圾.收集器】

  • JDK 8:Parallel Scavenge + Parallel Old
  • JDK 9+:G1
  • JDK 17:ZGC(需手动启用:-XX:+UseZGC)

新生代收集器

  • Serial:单线程,STW(Stop The World),适合单 CPU 环境
  • ParNew:Serial 多线程版本,与 CMS 配合使用
  • Parallel Scavenge【帕-了-乐.斯开-问之】:关注吞吐量,JDK8 默认

老年代收集器

  • Serial Old:单线程,标记 - 整理,适合 Client 模式
  • Parallel Old:Parallel Scavenge 的老年代版本,多线程标记 - 整理
  • CMS:追求低停顿,标记 - 清除算法,分阶段与用户线程并发执行
  • G1:面向服务端,将堆划分为多个 Region,动态回收价值最大的 Region

全区域收集器

  • ZGC(Z Garbage Collector):JDK 11 引入,超低延迟(<10ms),使用染色指针和读屏障技术
  • Shenandoah:与 ZGC 类似,支持并发整理

【JVM参数】

  • 配置收集器
# 新生代使用UseParallelGC(吞吐量优先)
-XX:+UseParallelGC

# 老年代使用UseParallelOldGC(吞吐量优先)
-XX:+UseParallelOldGC
# 老年代使用CMS(停顿时间优先)
-XX:+UseConcMarkSweepGC	

# 新生代和老年代使用G1GC(停顿时间优先)
-XX:+UseG1GC
# G1最大停顿时间。太小会导致G1跟不上垃圾产生的速度,最终退化成FullGC,所以对这个参数的调优是一个持续的过程,逐步调整到最佳状态
-XX:MaxGCPauseMillis=200ms
  • 配置元空间
# 设置原空间大小	
-XX:MetaspaceSize=50M
-XX:MaxMetaspaceSize=50M
  • 配置堆
# 设置堆大小
-XX:InitialHeapSize=100M
-XX:MaxHeapSize=100M
# 设置年轻代的大小
-XX:NewSize=20M
-XX:MaxNewSize=50M
# 设置老年代大小
-XX:OldSize=50M	
# 新老生代的比值。比如-XX:Ratio=4则表示新生代:老年代=1:4,也就是新生代占整个堆内存的1/5
-XX:NewRatio
  • 配置栈
# 设置每个线程的堆栈大小(经验值是3000-5000最佳)
-Xss128k

【JVM分析工具】

jps查看应用进程PID

jps -l # 14512 com.snoob.springboot.BffControllerApplication

jmap 查看JVM分配和使用情况

jmap -heap 14512
Heap Configuration:
   【备注:初始元空间,替代1.8以前的Permgen】
   MetaspaceSize            = 21807104 (20.796875MB) 
   【备注:最大元空间,调整元空间会Full GC,一般将MetaspaceSize和MaxMetaspaceSize设置一样,防止修改时触发FGC】
   MaxMetaspaceSize         = 17592186044415 MB 
   
   MinHeapFreeRatio         = 0 【备注:最小堆使用比例】
   MaxHeapFreeRatio         = 100【备注:最大堆使用比例】
   MaxHeapSize              = 104857600 (100.0MB)  【备注:最大堆空间】
  
  【备注:表示新生代:老年代=1:2,也就是新生代占整个堆内存的1/3】
   NewRatio                 = 2 
  【备注:表示Eden占新生代的8/10,From幸存区和To幸存区各占新生代的1/10】
   SurvivorRatio            = 8 
   
   NewSize                  = 34603008 (33.0MB)
   MaxNewSize               = 34603008 (33.0MB)
   OldSize                  = 70254592 (67.0MB)

   CompressedClassSpaceSize = 1073741824 (1024.0MB)
   G1HeapRegionSize         = 0 (0.0MB) 
   【备注:G1垃圾回收算法时,JVM会将Heap空间分隔为若干个Region,该参数用来指定Region的大小】

Heap Usage:
PS Young Generation
Eden Space:
   capacity = 17825792 (17.0MB)
   used     = 14837016 (14.149681091308594MB)
   free     = 2988776 (2.8503189086914062MB)
   83.2334181841682% used
From Space:
   capacity = 7864320 (7.5MB)
   used     = 851968 (0.8125MB)
   free     = 7012352 (6.6875MB)
   10.833333333333334% used
To Space:
   capacity = 8388608 (8.0MB)
   used     = 0 (0.0MB)
   free     = 8388608 (8.0MB)
   0.0% used
   
PS Old Generation
   capacity = 47710208 (45.5MB)
   used     = 13607144 (12.976783752441406MB)
   free     = 34103064 (32.523216247558594MB)
   28.520403851519575% used

jstat 监控JVM垃圾回收情况

jstat -gcutil PID
S0/S1:幸存者区
E:伊甸园区,初生区
0:老年代
M:元空间
YGC:年轻带回收次数
YGCT:年轻带回收花费时间(秒)
FGC:full GC
FGC:full GC花费时间(秒)
GCT:GC总时间(秒)

jstack 查看指定线程的堆栈信息

top # 查询消耗CPU最多的(进程)PID
top -Hp PID # 根据PID查询消耗(线程)CPU最多的PID
printf '%x\n' PID # 将PID转化成16进制数字2ac
jstack PID | grep 2ac -A 100 # 查询进程号7下的线程运行日志

JVM可视化工具

  • jmc.exe
  • jconsole.exe
  • jvisualvm.exe

JVM性能优化

  • JVM调优没有固定的参考参数,需根据不同服务器和系统需求来调整,默认JVM是会自动优化,不到万不得已不建议调整
  • 根据项目运行情况,增加适当的堆内存,注意:最小堆内存和最大堆内存设置一致【避免JVM频繁的切换堆内存】
java -jar \
-XX:MetaspaceSize=100M \
-Xms300M \
-Xmx300M \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \ # 设置最大GC停顿时间指标
-XX:InitiatingHeapOccupancyPercent=45 \ # 堆内存使用率大于45%时触发GC
demo.jar
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值