JVM内存模型完整版
2020-05-14 15:12:37 62 举报
JVM内存模型完整版
作者其他创作
大纲/内容
Object4
存储内容(30bit)
标志位(2bit)
锁状态
对象哈希码(25bit)、分代年龄(4bit)、偏向模式(1bit)--0未偏向
01
未锁定
调用栈中锁记录的指针(30bit)
00
轻量级锁
重量级锁的指针(30bit)
10
重量级锁
空
11
GC标记
线程ID(23bit)、Epoch(2bit)、分代年龄(4bit)、偏向模式(1bit)--1
可偏向
抽象语法树
1. 当遇到一个new指令时,虚拟机会首先进行类加载检查: - 检查new指令的参数是否能在常量池中定位到一个类的符号引用。 - 检查这个符号引用的类是否被加载解析和初始化过,没有则先执行类加载。2. 对象所需内存的大小在类加载完成后便可完全确定,于是分配空间等同于把一块确定大小的内存从堆中划分出来,具体分配方式取决于堆内存是否规整,而是否规整又取决于所采用的GC收集器是否带有压缩整理功能。 分配方式: - 指针碰撞(堆规整) - 空闲列表(堆不规整) 为保证并发情况下线程安全问题,在分配内存时有两种方案: - 同步:CAS+失败重试 - TLAB:-XX:+/-UseTLAB 分配步骤: - 首先尝试栈上分配 - 尝试TLAB分配 - 判断是否为大对象,是则在Old去分配,否则在Eden区。3. 接着,虚拟机将分配到的内存空间( 但不包括对象头) 都初始化为零值。4. 设置对象头,如设置MarkWord、类型指针等。5. 最后就是执行对象的<init>方法,既执行对象的构造方法。
8大基本类型
JVM栈(Stack)......
对象头
C
热点探测
程序计数器
Class对象(类元信息、属性、方法、静态变量等)
old
Parellel Scavenge(并)复制
执行类构造器<clinit>()
解析
方法出口
本地接口Native Interface
运行时常量池
执行<init>
初始化为零值
方法/回边计数器+1
可以理解为用于计算的临时数据存储区,使用load指令将数据加载到此
MarkWord
常量池中符号引用替换为直接引用
Object7
对象的创建过程
Java方法入口
操作数栈
文本字符串final常量值基本数据类型值其他
类型的常量池
垃圾回收算法--分代
存活对象
与虚拟机栈非常相似,区别在于本地方法栈为虚拟机的Native方法服务;Hotspot将JVM栈和本地方法栈合二为一
语义分析
注意:本示意图全程基于目前最常用的Oracle Hotspot JDK8虚拟机来描述
运行时数据区
两个计数器之和是否超过阈值
解释执行
本地方法栈
GC Root Set
字符串常量池
HelloWorld.java
栈中变量
类加载器的引用
1. JVM使用一组OopMap的数据结构来标记对象引用的位置,在类加载完成的时候,JVM就会把对象在什么偏移量上是什么类型的数据计算出来。这样就可以快速且准确地完成GC Roots枚举。2. 如果为每一条指令都生成对应的OopMap, 那将会需要大量的额外存储空间,所以只需要在特定的安全点添加存储空间。所以具有指令序列复用的指令才会产生Safepoint,如方法调用、循环跳转、异常跳转等。安全点暂停方式:抢先式中断、主动式中断。3. 如果程序sleep或者blocked,就不会走到安全点,这时需要引用安全区域。
Client Compiler(C1)速度
老年代(2/3)(MojorGC)大对象(可设置)会直接进入老年代,年龄达到15或者Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,大于等于改年龄的所有对象直接进入老年代
Eden(8/10)
准备
验证
解释器字节码-->解释执行-->结果
Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认定为“热点代码”。为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器(Just In Time Compiler,下文统称JIT编译器)。
本地方法库Native Libraries
对齐填充-8字节倍数
局部变量表
Class实例引用
实例数据
新生代(1/3)--MinorGC
引用计数
CodeCacheJIT编译代码产物
...
动态链接
栈上替换
类加载检查
词法分析
有一个是空,来回换
CardPage(512bytes)
可回收对象
后台执行编译
From Survivors0(1/10)
根可达分析算法
JIT动态编译
方法区常量
TLAB:在Eden区开辟的每一个线程私有的很小的缓冲空间(Thread Local Allocation Buffer)。线程需要创建对象,只要TLAB空间足够就可在此空间创建。
方法区(元空间Metaspace)
硬件
对象头设置
是
HelloWorld.class
TLAB(线程私有)
Serial(串)复制
JIT代码生成器(热点代码)字节码-->JIT编译(代码膨胀10X)-->执行-->结果
B
new
javac 编译器
编译器是以整个方法作为编译对象,编译完成后,方法的调用入口地址被替换成编译后的方法地址
数组长度(数组)
初始化类结构变量
常驻异常对象
类元信息
年龄15
类型指针
Server Compiler(C2)质量
ParNew(并)复制
垃圾回收器--连线代表可搭配使用
obj@12d34
Java 8以后,关于元空间的JVM参数有两个:-XX:MetaspaceSize=N和 -XX:MaxMetaspaceSize=N,对于64位JVM来说,元空间的默认初始大小是20.75MB,默认的元空间最大值是无限。MaxMetaspaceSize用于设置metaspace区域的最大值,这个值可以通过mxbean中的MemoryPoolBean获取到,如果这个参数没有设置,那么就是通过mxbean拿到的最大值是-1,表示无穷大。由于调整元空间的大小需要Full GC,这是非常昂贵的操作,如果应用在启动的时候发生大量Full GC,通常都是由于永久代或元空间发生了大小调整,基于这种情况,一般建议在JVM参数中将MetaspaceSize和MaxMetaspaceSize设置成一样的值,并设置得比初始值要大,对于8G物理内存的机器来说,一般我会将这两个值都设置为256M。
数组
类型信息
类加载器
同步锁持有者
新生代GC时采用的算法是复制算法;老年代GC时采用的是根搜索算法,标记垃圾对象,若老年代满了会触发FullGC,系统会暂时停止,JVM调优调的就是减少FullGC的发生次数
根可达算法
记忆集与卡表--解决跨代引用
字面量
堆(Heap)--FullGC
Object6
标记清除算法
字段信息
标记复制算法
语法分析
G1(并发)整理+复制
Parellel Old(并)整理
JVM 参数:-Xms:堆最小空间-Xmx:堆最大空间-XX:NewRatio:Old/New的比例-Xmn:年轻代大小,调整会影响老年代大小,官方建议为堆大小的3/8-XX:SurvivorRatio:调整Survivor和Eden去的大小比例-XX:MetaspaceSize:元空间初始化大小,64位JVM默认20.75M-XX:MaxMetaspaceSize:元空间最大大小,逻辑限制为屋里内存上限-XX:PretenureSizeThreshold:大对象直接进入老年代的阈值-XX:MaxTenuringThreshold:进入老年代的分代年龄阈值-XX:-XX:TargetSurvivorRatio:动态年龄判断比例设置-verbose:gc:输出JVM的gc情况-XX:+PrintGCDetails:输出GC详细信息-XX:+PrintTenuringDistribution:输出对象GC年龄信息-XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)-XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息-Xloggc:../logs/gc.log 日志文件的输出路径-XX:+UseG1GC:用G1-GC收集器-XX:-UseConcMarkSweepGC:用CMS-GC收集器-XX:+PrintCommandLineFlags -version:输出默认的垃圾回收器
clean
栈帧(sum)
Heap
引用不可达,将被回收
由于CPU执行指令是可中断的,会有线程切换,程序计数器会记录当前线程执行的字节码指令地址(行号),以便线程切换后能恢复到正确的执行位置
1.获取类的二进制字节流2.将静态结构转化为方法区运行时数据结构3.内存中生成类对象,作为数据入口
JIT
To Survivors1(1/10)
CMS(并发)清除
对象
符号引用
标记整理算法
卸载
执行编译后的机器码
方法返回
字节码校验器
Class字节码
线程1
连接
用于存放boolean,byte,char,short,int,float,long,double种类型的数据,以变量槽(slot)为最小单位(32位),long,double需要2个slot,所以线程不安全;基本数据类型会直接存值,引用数据类型会存放对象的引用
记录出站地址即方法返回地址或者异常地址
Object3
字节码生成器
加载
解释器
使用
垃圾判断算法
已编译?
垃圾收集--GC
Serial Old(串)整理
方法表
类装载子系统
returnAddress
初始化
注解抽象语法树
卡表维护:写屏障问题:1. 写屏障跟新卡表产生额外开销2.伪共享问题解决:增加卡表元素未被标记时才将其标记为变脏的判断-XX:+UseCondCardMark参数设定是否开启卡表更新
A
执行栈上替换
否
提交编译请求
基于计数器的热点探测方法调用计数器回边计数器
RemSet
JVM内部引用
Object5
young
执行引擎Execution Engine
方法区静态变量
reference
类和结构的全限定名字段名称和描述符方法名称和描述符
GC Roots
方法信息
文件格式、 元数据、 字节码和符号引用等验证
D
栈帧(main)
三色标记
对象分配内存
CardTable(1byte)
根节点枚举算法
dirty
动态链接(多态,编译期没有指明运行时才指明对象),指向常量池中方法的引用
Object1
收藏
收藏
0 条评论
下一页