类加载的过程/类的生命周期
加载、验证、准备、解析、初始化
加载
1.通过全限定类名来获取定义此类的二进制字节流
2.将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
3.在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
验证
确定Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害到虚拟机自身的安全
准备
为类变量分配内存并设置类变量初始值,这些变量所使用的内存都将在方法区中进行分配
解析
虚拟机将常量池内的符号引用替换为直接引用的过程
解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符七类符号引用进行
初始化
到初始化阶段,才真正开始执行类中定义的Java程序代码,此阶段是执行 < clinit>()方法的过程
类加载器
类加载器可分为:启动类加载器、扩展类加载器、应用程序类加载器、自定义类加载器,他们的关系如下:
类加载机制
双亲委派机制:类加载器接收到类加载的请求,先调用父类加载器进行加载,每一层类加载器都是如此,一直找到启动类加载器。每一层无法加载时,让下层类加载器来加载
类加载应用的相关问题:
(1)能否自定义类加载器加载自定义的java.lang.Obejct类
(2)jdk如何保证一定是调用jdk提供的java.lang.Object类
结论:遵循双亲委派机制的类加载,不会加载到自定义的java.lang.Object。如果时不遵循双亲委派机制的类加载,类加载会校验,不允许java.开头的类进行加载
new一个对象发生了什么过程?
- 确定类元信息是否存在。当JVM接收到new指令时,首先在metaspace内检查需要创建的类元信息是否存在。若不存在,那么在双亲委派模式下,使用当前类加载器以ClassLoader + 包名 + 类名为Key进行查找对应的class文件。如果没有找到文件,则抛出ClassNotFoundException异常,如果找到,则进行类加载(加载-验证-准备-解析-初始化),并生成对应的Class类对象
- 分配对象内存。首先计算对象占用空间大小,如果实例成员变量是引用变量,仅分配引用变量空间即可,即4个字节大小,接在在堆中划分一块内存给新对象。在分配内存空间时,需要进行同步操作,比如采用CAS(Compare And Swap)失败重试、区域加锁等方式保证分配操作的原子性
- 设定默认值。成员变量值都需要设定成默认值,即不同形式的零值
- 设置对象头。设置新对象的哈希码、GC信息、锁信息、对象所属的类元信息等。这个过程的具体设置方式取决于JVM实现
- 执行init方法。初始化成员变量,执行实例化代码块,调用类的构造方法,并发堆内对象的首地址赋值给引用变量