类加载
类加载时机
隐式加载:new 创建类的实例
显式加载:loaderClass,forName等
访问类的静态变量或静态方法
使用反射方式创建某个类或者接口对象的Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类
类加载流程
加载
- 通过类全路径加载Class文件二进制流
- 将这个类字节流代表的静态存储结构转为方法区的运行时数据结构
- 堆中生成一个代表此类的java.lang.Class对象,作为访问方法区这些数据结构的入口
验证
确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机的自身安全
准备
- 为类变量分配内存
- 为类变量赋初值0
- final类型的直接赋值完毕
解析
常量池中的符号引用转换为直接引用
初始化
执行其静态初始化器(静态代码块)和静态初始化成员变量
初始化时机
- 使用new关键字实例化对象、访问或者设置一个类的静态字段(被final修饰、编译器优化时已经放入常量池的例外)、调用类方法,都会初始化该静态字段或者静态方法所在的类。
- 初始化类的时候,如果其父类没有被初始化过,则要先触发其父类初始化。
- 使用java.lang.reflect包的方法进行反射调用的时候,如果类没有被初始化,则要先初始化。
- 虚拟机启动时,用户会先初始化要执行的主类(含有main)
类加载器类别
- 引导类加载器
- 拓展类加载器
- 系统类加载器
- 自定义加载器
两个类是否相同
- 全限定名一致
- 类加载器一致
forName和loaderClass区别
- Class.forName()得到的class是已经初始化完成的。
- Classloader.loaderClass得到的class是还没有链接(验证,准备,解析三个过程被称为链接)的。
双亲委派
流程
- 检查类是否已被加载
- 若没有被加载则委托父加载器加载
- 父加载器没有加载则findClass加载类,然后defineClass返回Class对象
SPI
https://blog.csdn.net/qq_41894099/article/details/104558522
Java对象的分配流程
- 确保类已执行类加载过程
当虚拟机遇到new指令时,会根据new指令的参数去常量池中查找是否有对应的符号引用,以及符号引用代表的类是否已经加载、连接、初始化流程,若否,需限制性类加载过程
2.分配内存
对象的大小在类加载完成后就已经确定,根据对象的大小,进行内存分配
根据分配方式的不同,可分为指针碰撞和空闲列表两种方式,指针碰撞适用于规整的内存,即使用含整理功能的垃圾收集器,系统使用指针碰撞,否则,使用空闲列表(实际上,使用空闲列表找到一块Linear Allocation Buffer,在该缓冲区内再使用碰撞指针分配,如此可提高分配效率)
对象空间分配流程如下:
1)如果开启了栈上分配,则尝试使用栈上分配
依赖于逃逸分析和标量替换
如果未启用栈上分配或栈上分配失败,则在堆上分配,使用CAS(cmpxchg)和失败重试的方式,确保在并发下分配成功
2)尝试TLAB分配
TLAB占用Eden空间
3)大对象直接进入老年代
典型的有大字符串和大数组
4)在Eden区分配
当Eden区没有足够的空间是,发起一次Minor GC
5)长期存活的对象进入老年代
6)动态年龄判断
年龄从小到大进行累加,当加入某个年龄段后,累加和超过survivor区域*TargetSurvivorRatio(一半)的时候,就从这个年龄段之上的年龄的对象进行晋升
7)空间分配担保
YGC期间,survivor区空间不足,通过空间担保直接进入老年代
3.实例初始化零值(不含对象头)
将对象的实力字段初始化零值
4.设置对象头
设置类型指针、markword(含锁信息、GC分代年龄、hashcode等)等信息
5.实例初始化
通过 invokeSpecial指令,执行
invokeSpecail只能在一下三种情况下使用:
1)
2)private方法
3)super.method()
对象的内存布局
1.对象头
1.1 markword
未锁定:对象hash码、GC分代年龄、01
可偏向:偏向线程ID、偏向时间戳、GC分代年龄、01
轻量级锁:指向锁记录的指针、00
重量级锁:指向重量级锁的指针、10
GC标记:11
1.2 类型指针
通过类型指针定位对象所属类的元数据信息,对应直接指针访问的对象定位方式
- 实例数据
默认分配顺序为 doubles/longs、ints、shorts/chars、bytes/booleans、oops(普通对象指针)
如此,相同宽度的字段被分配到一起存放,那么父类定义的变量可能出现在子类之前,子类中定义的变量可能出现在父类定义的变量的间隙中。
-
对齐填充
自动内存管理系统要求对象起始地址必须是8字节的整数倍 -
计算对象的大小
mark word 8位 | calss 4位(未压缩8位) | [数组长度4位] | 内容
8字节对齐
reference4个字节