详尽的类加载完整流程图
2023-05-10 22:35:33 0 举报
详尽的类加载完整流程图
作者其他创作
大纲/内容
解析
语义分析
本地方法栈
CodeCacheJIT编译代码产物
运行时常量池
年轻代回收过程
from1/10
survivor区
方法区(JDK1.7以前叫永久代,之后叫元空间)
字节码执行引擎
执行init方法
1、math对象逐渐变多,填满eden区,此时会发生minor gc(yong gc)
方法信息
类型信息
minor gc回收年轻代
1、将一个int型常量3压入栈2、将这个int类型值存入局部变量a中,(实际上JVM是存入变量1中,a就是第一个变量,第0个是this)
线程2
HelloWord.class
操作数栈
程序计数器,记录程序执行的行号,每个线程都有自己计数器,线程切换的时候可以根据行号找到之前执行的位置
记录行号
2、剩余的存活对象会进入from区
比如一批对象(年龄n)进来,年龄1+年龄2+。。。+年龄n,大于50%,那么年龄大于n的对象直接进入老年代
初始化为0值
JVM参数:
老年代2/3
没配置直接full gc
实例数据
1. 当遇到一个new指令时,虚拟机会首先进行类加载检查: - 检查new指令的参数是否能在常量池中定位到一个类的符号引用。 - 检查这个符号引用的类是否被加载解析和初始化过,没有则先执行类加载。2. 对象所需内存的大小在类加载完成后便可完全确定,于是分配空间等同于把一块确定大 小的内存从堆中划分出来,具体分配方式取决于堆内存是否规整,而是否规整又取决 于所采用的GC收集器是否带有压缩整理功能。 分配方式: - 指针碰撞(堆规整),就是规整分为两块,一块用过一块没用过,每次分配内存只 需要移动指针 - 空闲列表(堆不规整),找空位分配,要维护一个列表记录哪些有分配过 为保证并发情况下线程安全问题,在分配内存时有两种方案: - 同步:CAS+失败重试 (比较指针有没有被别的线程改动过) - TLAB:-XX:+/-UseTLAB (每个线程分配一个自己的TLAB,这个线程优先往这个 内存中分配)3. 接着,虚拟机将分配到的内存空间( 但不包括对象头) 都初始化为零值。4. 设置对象头,如设置MarkWord、类型指针等。5. 最后就是执行对象的<init>方法,既执行对象的构造方法。
语法分析
老年代装满后发生full gc,stw时间比minor gc长,要尽量减少full gc,full gc后内存还是不够就会OOM
栈
math
回收一次后,survicor区装不下,直接进入老年代
把符号引用替换成直接引用,一些静态方法替换成指向数据所在内存的指针
:线程共享
eden8/10
动态链接
判断参数开启了没有
to1/10
类加载检查
老年代回收过程
full gc
配置了
内存分配
初始化
程序计数器
3.eden区继续增加,满了之后会回收整个年轻代,并且还存活的对象移到to区,年龄+1
局部变量
:线程独占
直接内存
对象优先分配eden区,不够的情况下去回收
是
执行字节码
词法分析生成器
数组长度,4字节
堆内存
4.eden区继续增加,满了之后会回收整个年轻代,并且还存活的对象移到from区,年龄+1
设置对象头
main线程
对象
栈帧1
对象创建过程
MarkWord,32位4字节,64位8字节
老年担保机制
加载
发生短暂的stw
连接
类型信息表
老年代剩余可用大小<每次minor gc进入老年代对象的平均大小
类加载器的引用
方法出口
验证
词法分析
卸载
字段信息
校验字节码文件格式,元数据和符号引用等
类型指针,开启压缩后4字节,不开启8字节
年轻代总共1/3,然后里面默认8:1:1
使用
javac编译器
大对象(比配置大小还大,这种就可以省去高额的内存复制消耗了),直接进入老年代,可以通过参数-XX:PretenureSizeThreshold配置大对象的大小
对其填充,8字节的倍数
从硬盘中查找并通过IO读入字节码文件,执行的时候加载,会在内存中生成一个代表这个类的class对象
class实例引用
准备
math的class对象,开发人员可以getClass来使用类对象
full gc/major gc回收整个堆
类型的常量池
Math.class
栈帧2,每一个栈帧就是一个方法
-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:输出默认的垃圾回收器
本地方法栈,native,里面是c语言
否
年龄到了15就会进入老年代,因为对象头中的分代年龄只有4bit,也就是最大1111,十进制就是15
指向类元信息,一个类的不同实例对象,他的代码是一样的,也就是在方法区的符号引用是同一份,堆里面的对象math1和math2的对象头里面的类型指针会指向方法区的Math.class(类信息)
如果局部变量赋值是一个Math对象,那么他指向的对象是在堆中的math实例对象
比如有一个变量声明,int a = 3
给静态变量分配内存空间,并赋予默认值
抽象语法树
给静态变量初始化指定值,执行静态代码块
class对象存静态变量,类元信息,属性,方法等
对象头
HelloWord.java
类元信息
类的加载过程
minor gc之前
老年代剩余可用空间大小<年轻代现有所有对象
new
注解抽象语法树
类加载过程中生成这个class对象
0 条评论
回复 删除
下一页