JVM
2023-02-17 09:33:45 18 举报
java虚拟机
作者其他创作
大纲/内容
虚拟机栈
垃圾收集算法
高速内存
字节码指令
1、自旋锁:不放弃CPU占用,默认自旋10次,可通过配置调整 -XX:PreBlockSpin=xx2、锁消除:在对象变量不存在逃逸(不会被其他线程访问到)时,无需同步,直接执行。不管是自己写的同步代码,还是只在方法内部存在的变量如 StringBuffer,编译执行时会忽略同步。3、锁粗化:对同个对象连续加锁的操作,会被优化为只加一次锁。如StringBuffer.appen()4、偏向锁:根据对象头中的ThreadId匹配是否是当前线程,如果是则直接执行同步代码,无需加锁。即偏向于第一个获取到锁的线程,不用CAS操作5、轻量锁:如果对象当前记录的是偏向锁,且有另一条线程同时争抢锁,此时对象的锁标志会升级为轻量锁,已获取锁的线程可以继续执行同步代码。需要CAS操作6、重量锁:如果对象当前记录的是轻量锁, 且有另一条线程同时争抢锁,此时对象的所标志会升级为重量锁(锁膨胀)。
针对对象且是多线程环境下而言,如果有多个线程同时访问和操作同一个对象,并且都能返回自己需要的结果,那么这个对象就是线程安全的对象。线程安全实现方法:1、互斥同步。互斥是方法,同步是目的。synchronized通过monitorenter/monitorexit指令实现安全同步,Lock接口实现类ReentranLock悲观锁,通过CAS实现安全同步。jdk1.5之前synchronized只有重量级锁,jdk1.5之后可以有偏向锁、轻量级锁。Lock接口也是jdk1.5之后新加入的。2、非阻塞同步。即CAS思想,通过比较交换,调用底层操作系统指令保证原子性。3、无同步环境。即不适用共享数据,也就没有线程安全问题。
初始化Initialation
主内存
Class文件格式
JVM运行时数据区
对象属性,父类属性 Field
hash码GC标记GC次数同步锁标记偏向锁持有标记
Class文件含义
本地方法栈
处理器
Blocked阻塞
6、G1(Garbage First)将堆分为多个独立区域(Region 2Mb-32Mb)每次根据允许的收集时间,优先回收价值最大的Region。化整为零的思想。
对类和接口、方法、接口方法、字段、字段类型、方法句柄这几种符号引用进行解析,将符号引用替换为直接引用(指向内存的指针,在内存中已存在)。
Terminated结束
new一个空对象大小为16个字节
常用参数配置
锁状态
25bit
31bit
1bit
分代年龄(最大15)4bit
是否偏向锁 1bit
同步锁标志 2bit
无锁
undefined
hashcode
cms_free
分代年龄
0
01
偏向锁
ThreadId 52bit
Epoch 2bit
1
轻量锁
指向栈中
锁记录
的指针
=
62bit
00
重量锁
指向
重量级锁
10
GC标志
空
11
Running运行
run()结束
User Class Loader
验证是为了加载到虚拟机的类不会破坏虚拟机运行,包括:1、格式验证:是否以0xCAFEBABE开始,主、次版本号是否在此虚拟机接收范围内,常量池中的常量类型(tag标志)2、元数据验证:是否有继承Object,是否继承了final修饰的类,如果不是抽象类是否实现父类和接口所有方法,类中字段、方法的合规性3、字节码验证:保证字节码指令不会跳转到方法体以外的字节码指令上4、符号应用验证:通过字段全限定名能否找到对应的类、类、方法、字段的作用域是否能被当前类访问
工作内存
参数
说明
性能参数
用来定义内存分配的大小和比例,相比于行为参数和调试参数,一个比较明显的区别是性能参数后面往往跟的有数值
-Xms1024m
堆区内存初始内存分配的大小,通常为操作系统可用内存的1/64大小
-Xmx1024m
堆区内存可被分配的最大上限,通常为操作系统可用内存的1/4大小
-Xmn300m
对 -XX:newSize、-XX:MaxnewSize两个参数的同时配置,也就是说如果通过-Xmn来配置新生代的内存大小,那么-XX:newSize = -XX:MaxnewSize = -Xmn
-XX:NewSize=300m
新生代初始占用内存,小于-Xms
-XX:MaxNewSize=300m
新生代能占用内存的最大值,小于-Xmx
-XX:MaxPermSize=64m
方法区所能占用的最大内存(非堆内存)jdk1.8之后无此配置
-XX:PermSize=64m
方法区分配的初始内存(非堆内存)jdk1.8之后无此配置
-XX:MaxTenuringThreshold=15
对象在新生代存活区切换的次数(坚持过MinorGC的次数,每坚持过一次,该值就增加1),大于该值会进入老年代(年龄阈值)
-XX:MaxHeapFreeRatio=70
GC后java堆中空闲量占的最大比例,大于该值,则堆内存会减少
-XX:MinHeapFreeRatio=40
GC后java堆中空闲量占的最小比例,小于该值,则堆内存会增加
-XX:NewRatio=2
新生代内存容量与老生代内存容量的比例2:1
-XX:ThreadStackSize=512
设置线程栈大小,若为0则使用系统默认值
-XX:PretenureSizeThreshold=1024000
大于该值1M 的对象直接晋升入老年代(这种对象少用为好)-XX:+UseSerialGC 下生效
-XX:SurvivorRatio=8
Eden区域Survivor区的容量比值,如默认值为8,代表Eden:Survivor1:Survivor2=8:1:1
行为参数
主要用来选择使用什么样的垃圾收集器组合,以及控制运行过程中的GC策略对性能进行调优
-XX:+UseSerialGC
启用串行GC,即采用Serial+Serial Old模式
-XX:+UseParNewGC
使用ParNew+Serial Old收集器组合jdk9后不再支持
-XX:+UseParallelGC
启用并行GC,即采用Parallel Scavenge+Serial Old收集器组合(-Server模式下的默认组合)
-XX:+UseParallelOldGC
使用Parallel Scavenge +Parallel Old组合收集器
-XX:+UseConcMarkSweepGC
使用ParNew+CMS+Serial Old收集器组合,Serial Old作为CMS出现Concurrent Mode Failure时使用
-XX:+UseG1GC
使用G1收集器 jdk9后-Server模式下默认使用
-XX:-DisableExplicitGC
禁止调用System.gc();但jvm的gc仍然有效
-XX:+ScavengeBeforeFullGC
新生代GC优先于Full GC执行
-XX:GCTimeRatio=99
设置用户执行时间占总时间的比例(默认值99,即1%的时间用于GC)仅在Parallel Scavenge收集器生效
-XX:ParallelGCThreads=4
设置执行内存回收的并行线程数,在+UseParNewGC的情况下使用
-XX:MaxGCPauseMillis=100
设置GC的最大停顿时间(这个参数只对Parallel Scavenge有效)
-XX:+HandlePromotionFailure
是否开启空间分配担保,Minor GC 前先看老年代最大连续空间是否大于新生代所有对象之和,如果大于则直接GC,如果不大于则判断开关是否开启(开启:检查是否大于历史平均大小,大于则直接GC,小于或者是关闭时,则Full GC)jdk6.24之后不用判断开关是否开启。
调试参数
调试参数,主要用于监控和打印GC的信息
-XX:+HeapDumpOnOutOfMemoryError
当首次遭遇OOM时导出此时堆中相关信息
-XX:HeapDumpPath=./java_pid<pid>.hprof
指定导出堆信息时的路径或文件名
-XX:+PrintGCDetails
每次GC时打印相关信息
-XX:+TraceClassLoading
跟踪类的加载信息
-XX:+TraceClassResolution
跟踪常量池
-XX:+TraceClassUnloading
跟踪类的卸载信息
内存屏障
Extension Class Loader
sleep()
jdk的bin目录下有各种小工具,体积都小,是因为基本都是对JDK工具类库的封装,方便直接在应用程序中调试和监控(虽然不建议这么做)
Application Class Loader
对象标记(Mark Word)8字节
垃圾收集器组合使用:Serial+Serial OldParallel Scavenge+Parallel OldParNew+CMS
加载Loading
锁优化
主要定义各种环境下读写访问内存变量的规则,包括实例字段,静态字段,但不包括局部变量和方法参数,因为后者是线程私有变量,不涉及竞争和安全问题。主内存:物理内存,线程不能直接操作主内存工作内存:线程私有内存,线程对变量的操作都要先经过工作内存主内存和工作内存之间的操作:lock:作用于主内存,表示线程独占这个资源。unlock:作用于主内存,表示线程释放了这个资源的独占锁。read:作用于主内存,从主内存传输到工作内存。load:作用于工作内存,将read的变量副本存入工作内存。use:作用于工作内存,使用变量。assign:作用于工作内存,赋值操作store:作用于工作内存,将赋值写入工作内存write:作用于主内存,将工作内存的值写入到主内存中以上完成主内存到工作内存的操作,注意:read-load-store-write中间允许有其他操作,但顺序不能变,8种操作可以简化为4种 lock-load-store-unlock
高速缓存
准备
类型
名称
数量
u4
magic
u2
minor_version
major_version
constant_pool_count
cp_info
constant_pool
constant_pool_count-1
access_flags
this_class
super_class
interfaces_count
interfaces
interface_count
fields_count
fields_info
fields
methods_count
methods_info
methods
attributes_count
attributes_info
attributes
监控调试工具
Volatile
类模板-Object.class 默认开启压缩指针
3、Parallel Scavenge 自适应调节策略,吞吐量优先,基于复制算法
第一个操作
第二个操作普通读
第二个操作volatile读
第二个操作volatile写
普通读写
可以重排
不可以重排
volatile读
volatile写
1、Serial和Serial Old(单线程收集器)
对象头
JMM(Java Memory Model)Java内存模型
保证8字节倍数
cpu处理器的运算速度远大于内存,为了解决两边效率不等问题,出现了高速缓存(一级缓存、二级缓存、三级缓存)
start()
线程
内存一致性协议
解析
1、magic(魔数):class文件开头 4 字节,值为 0xCAFEBABE2、minor_version(次版本号)占用 2 字节3、major_version(主版本号)占用 2 字节,java版本号从45开始即jdk1=45每升一个版本,主版本号+1。jdk6=50,jdk7=51,jdk8=52,jdk13=574、constant_pool_count(常量池数量)占用 2 字节有21种常量5、constant_pool(常量池)表数据类型,每种常量都是一个表数据类型6、access_flags(访问标志)用于标识类或接口访问信息,如是接口还是普通类,是否public,是否有final修饰等。7、this_class(类索引、通过类索引从常量池中找到类的全限定名)、super_class(父类索引)、interfaces(接口索引)这三者确定类的继承关系。8、fields(字段表)用于标识作用域public、private,protected,span style=\"font-size: inherit;\
数组长度(Length)数组对象
对象的内存布局new Object()
Bootstrap Class Loader
类加载全部完成需要做三件事:1、通过类的全限定名获取类的二进制字节流2、将字节流中的静态存储结构转换为方法区中运行时数据结构3、在内存中生成一个此类的java.lang.Class对象,作为方法区各种数据(常量,方法)访问入口
可见(volatile、synchronized)、有序(禁止指令重排序,volatile、synchronized)、原子(唯一,要么成功有么失败,synchronized)
notify()
可视化处理工具:1、jConsole:JDK5以上自带,对应 jconsole.exe2、VisualVM:JDK6以上自带,现已经从OracleJDK中独立出来一个项目,对应 jvisualvm.exe3、JCM(Java Mission Control):BEA公司旗下付费使用的图形工具,对应 jmc.exe
wait()
volatile 保证可见性(对变量的操作会立即回写到主内存,保证其他线程从主内存获取到的是最新的数据,线程使用前都会从主内存重新读取)、有序性(禁止指令重排-内存屏障),但不保证原子性。i++问题,在执行iadd指令时,只能保证当前的值是正确的,并发情况下存在其他现在同时执行iadd指令,所以用旧的值+1再回写到主内存,导致达不到预期值。
程序计数器
类加载机制
TimeWaiting限时等待
验证
OOM异常
* volatile变量原则;对volatile变量的写操作先行发生这个变量的读操作 * volatile单例 new SingleDemo()是非原子操作,需要经过1、分配内存空间;2、初始化实例数据;3、返回内存地址 * * 1、volatile读——》任何读写,NO-不允许重排序:保证V读后的操作不会排到V读前执行 * 2、任何读写——》volatile写,NO-不允许重排序:保证V写前的操纵不会排到V写后执行 * 3、volatile写——》volatile读,NO-不允许重排序:保证写后读 * 在适当的位置插入内存屏障指令保证禁止重排序 * Load1 * LoadLoad ---------保证Load1读先于Load2读执行 * Load2 * * Store1 * StoreStore ---------保证Store1写先于Store2写执行 * Store2 * * Load1 * LoadStore ---------保证Load1读先于Store2写执行 * Store2 * * Store1 * StoreLoad ---------保证Store1写先于Load2读执行 写后读屏障,能处理其他三种类型的情形 * Load2 * * 内存屏障演示1 * volatile读 ---------第一个操作为V读,将禁止后面的所有操作排在V读前 * LoadLoad ---------禁止后面的所有读操作排在V读前 * LoadStore ---------禁止后面的所有写操作排在V读前 * * 内存屏障演示2 * 普通读、写 * StoreStore ---------禁止前面的所有写操作排在V写后面 * volatile写 ---------第二个操作为V写,将禁止前面的所有操作排在V写后 * StoreLoad ---------禁止前面的V写操作排在V读后面
对齐填充
类型指针(Class Point)8字节
内存泄露:存在GC Roots引用链,即对象使用完后没有进行释放,垃圾数据无法回收,久而久之将导致内存溢出。内存溢出:新对象申请内存不足。GC overhead limit exceeded:执行GC次数太多,有效运算太少,每次只能回收少部分内存,或者方法区内存不足,最终导致OOM。Java head space:内存不足,导致OOM
程序计数器:记录线程执行字节码的执行行号,如果是native方法,计数器的值为空,唯一没有OOM异常的区域。虚拟机栈:记录线程执行的方法,出栈入栈操作(栈帧)。深度不够抛StackOverflow异常,宽度不够OOM异常(Classic抛出OOM,HotSpot不会抛OOM异常)。本地方法栈:记录调用了native修饰的方法。深度不够抛StackOverflow异常,宽度不够OOM异常。方法区:也称永久代(1.8之后是元数据区)存放类信息、常量、静态变量、缓存信息等。String.intern()会存入方法区。OOM异常(HotSpot不会抛OOM异常)堆:分为(新生代(Eden、From Survivor、To Survivor)、老年代),新对象的创建需经过堆内存。OOM异常
线程隔离的数据区
如何判断对象已死?1、引用计数器:有引用值+1,引用失效值-1,值为0的视为可回收对象。优点:高效。缺点:对象互相引用时,无法被回收,造成内存泄露。2、可达性算法:GC Roots为根节点,如果对象与此节点不可达,视为可回收对象。可作为GC Roots节点的对象如:方法参数、常量、静态变量、native应用的对象、synchronized修饰的对象等。四种引用类型强引用(Strongly Reference):最常见如 new 一个对象,只要引用关系还存在,就不会被GC软引用(Soft Reference):执行垃圾回收时如果发现内存不足,就把软引用对象GC弱引用(Week Reference):无论内存是否充足,都会被GC虚引用(Phantom Reference):无法直接读取对象,需要和队列同时使用,唯一目的是对象被回收时,触发finalize通知,可以在finalize中让对象复活一次(把this赋值给一个变量),但finalize只会被执行一次。
线程安全
双亲委派模型
默认丢给父类加载器去加载,如果父类加载器不能加载,自己再进行加载。保证一个类只通过一个加载器加载到虚拟机。1、检查是否已被加载2、交给父类加载器加载,如果父类加载器为空直接交给Bootstrap Class Load加载,若无法加载,抛出异常3、如果出现异常,调用本身的findClass进行类加载
Happens-Before(先行发生原则)
实例数据
方法区
卸载Unloading
垃圾收集器
1、准备阶段是为静态变量分配内存,并设定初始值,如public static int value=123 ;准备阶段之后value值被设置为0,而不是1232、如果是final修饰的字段,直接赋值,如public static final int value=123;准备阶段之后value值被设置为123
使用Using
synchronized
4、Parallel Old 多线程并发,基于标记-整理算法
Waiting等待
CLass类文件结构
线程数据共享区
命令参数
作用
jps [options] [hostid]
列出虚拟机的进程id(LVMID)
jps -q
只输出LVMID,省略主类名称
jps -m
输出虚拟机进程启动时main参数
jps -l
输出主类全名,如果是jar启动则输出jar包路径
jps -v
输出虚拟机进程启动参数
jstat [option] [vmid] [intervel] [count]
虚拟机统计分析工具,显示本地或远程虚拟机类加载、内存、垃圾收集运行时数据例如:jstat -gc 1234 1000 20每隔1秒输出虚拟机进程id为1234的gc日志,重复执行20次
jstat -class
输出类加载、卸载、空间、时间消耗信息
jstat -gc
输出新生代、老年代、永久代内存信息,垃圾收集时间合计等
jstat -gcnew
监视新生代内存、垃圾收集信息
jstat -gcold
监视老年代内存、拉垃圾收集信息
jstat -gcutil
同-gc 更注重空间使用占比
jstat -compiler
输出即时编译和编译过的方法、耗时等信息
jinfo [option] [vmid]
输出和更改虚拟机参数,使用 -flag [±]name 或者 -flag name=[option] 修改部分启动参数
jinfo -flag
例如 jinfo -flag newRatio 1234查询虚拟机进程id为1234的 newRatio 启动参数配置值
jmap [option] [vmid]
生成堆快照文件,dump文件
jmap -dump
jhat [dumpFileName]
结合jmap使用,在线浏览器查看jmap生成的快照文件
jhat fileName
例如:jhat abc等待打印 “Server is ready”即可按提示端口打开浏览器查看abc快照文件
jstack [option] [vmid]
堆栈跟踪工具,查看线程死锁、死循环、线程快照
jstack -F
当请求不被响应时,输出堆栈信息
jstack -l
除堆栈外,输出锁相关信息
jstack -m
调用native方法时,显示C/C++堆栈
堆内存
加载
对象标记Mark Word
内存模型
https://blog.csdn.net/m0_54356563/article/details/121141150
指令类别
指令
加载指令
load-从内存加载到操作栈:iload/lload/fload/dload/aload,数组-iaload/baload……
存储指令
store-从操作栈写到内存:istore/lstore/fstore/dstore/astore,数组-iastore/bastore……
运算指令
add/sub/mul/dev-加减乘除iadd/isub/imul/idev
类型转换指令
窄类型转换可能会精度丢失:i2b/i2c/i2s/l2i/f2i/f2l/d2i/d2l/d2f
对象创建与访问指令
创建-new/newarray,访问-getfeild/putfeild/getstatic/putstatic
操作转移指令
条件分支:ifeq/ifne/ifnull/ifcmple,复合条件分支:tableswitch,无条件分支:goto
方法调用与返回指令
调用:invokevirtual/invokeinterface/invokestatic,返回:ireturn/lreturn/freturn/dreturn/areturn
异常处理指令
抛出异常:athrow
同步指令
monitorenter/monitorexit(配对执行)+monitorexit(异常时执行)
5、CMS 基于标记-清除算法,优点:低停顿;缺点:需占用资源,CPU运算能力
在Java虚拟机的参数中,有3种表示方法标准参数(-),所有的JVM实现都必须实现这些参数的功能,而且向后兼容;如-client/-server非标准参数(-X),默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足,且不保证向后兼容;如-Xms/-Xmx/-Xmn非Stable参数(-XX),此类参数各个jvm实现会有所不同,将来可能会随时取消,需要慎重使用(但是,这些参数往往是非常有用的);如表示开关类-XX:±<option>、表示赋值类-XX:<option>=xx
准备Preparation
同一个类,如果不是同一个类加载器加载进来的,会被视为两个不同对象,使用instanceof()方法返回false。1、Bootstrap Class Loader:启动类加载器,主要加载\\lib目录下的jar如rt.jar,tools.jar2、Extension Class Loader:扩展类加载器,主要加载\\lib\\ext目录下的jar3、Application Class Loader:应用类加载器,主要加载应用程序lib目录下的jar4、User Class Loader:用户自定义类加载器
验证Verification
线程安全与锁优化
2、ParNew(Serial的多线程版本)
New新建
真正执行程序java代码,执行<clinit>()方法和构造函数,注意:父类的<clinit>()优于子类先执行,即导致父类static{}块优先执行
分代收集定义:新生代收集(Minor GC)、老年代收集(Major GC)、整堆收集(Full GC)一般老年代收集会伴随一次新生代收集。标记-清除算法(Mark-Sweep):先标记再清除,缺点:1、执行效率不稳定;2、产生大量不连续的内存空间碎片,容易导致大对象无法获取连续内存空间而进行GC。标记-复制算法(Mark-Copy):将内存分为两块区域,当一块区域用完了,GC之后将存活对象复制到另一块区域,并清除已使用区域。缺点:内存使用率缩小一半。基于对象“朝生夕灭”特点,优化后的复制算法:将内存分为Eden、Survivor1、Survivor2区域,比例是8:1:1,新生代内存实用率达到90%标记-整理算法(Mark-Compact):复制算法需要有老年代作为担保,所以不适用于老年代。对象标记,GC之后将存活对象移动到一端,这个过程需要暂停用户线程(Stop the world)HotSpot算法实现:1、可达性算法(GC Roots 根节点枚举)此过程需暂停用户线程,用OopMap记录对象指针;2、安全点(Safepoint)在特定位置记录OopMap,主动式中断-GC需要中断线程时,不直接中断而是设置一个标志位,并不断轮询标志位,一旦发现标志位为真就在最近的安全点挂起。如Thread.interrupted()取中断标志位。3、安全域(Safe Region)
Class文件是由8字节为基础的二进制流,各项数据紧密排在一起,中间没有任何分隔符。1、无符号数属于基本的数据类型,u1/u2/u4/u8 表示1/2/4/8个字节2、表是有无符号数组合成的复合数据类型,通常用 _info 结尾
连接
线程状态切换
垃圾收集
初始化
解析Resolution
三大特性
类加载器(Class Loader)
0 条评论
下一页