马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
记录和思考,不断完善中…
类加载的流程
java的类加载机制是按需加载,当必要某个类时,通过类的全限定名或其他方式获取对应的字节码文件,然后类加载器会采用双亲委派模型对字节码文件进行加载,即起首尝试使用父类加载器进行加载,假如加载不了,再交由子类加载器进行加载,这种加载方式可以有效保护java的焦点类库。加载的过程如下:
加载阶段
就是将类的字节码加载到内存中并天生代表该类的Class对象,这个阶段中类的静态存储布局转化为方法区的运行时数据布局,而创建的Class对象就是方法区中这个类的数据访问入口
个人明白:
可以认为类加载的过程中,在加载阶段就完成了外部类到JVM内的加载过程,因为此时确实已经在堆区创建了代表类的class对象了。加载过程的后续环节可以认为是对class对象的不断丰富,比如校验,初始化等。
链接
包罗验证、准备、剖析三个小阶段
验证
验证字节码正当性
思考:为什么在加载之前不进行验证呢?假如字节码不正当,岂不是进行一次无用的加载。
Class对象是类在JVM中的表现形式,必要在加载进JVM并天生相应的代表此类的Class对象后,才气通过Class对象对该类的元数据进行各种判定验证,以确保该class不会对JVM运行产生危害。
准备
对类对象的静态变量设置默认初始值。根本范例,如int设置为0,对象则设置为null。
剖析
这个过程则是将类对象中的符号引用剖析成直接引用。这一阶段的主要任务是剖析类的常量池中的符号引用
扩展:
- 符号引用: 在 Java 源代码中,我们使用的类名、方法名、字段名等都是符号引用。这些符号引用在编译时是无法直接定位到内存地点的,而是必要在运行时动态剖析。
- 直接引用: 直接引用是可以直接定位到内存地点的引用,是符号引用经过剖析后的效果。直接引用使得捏造机可以或许快速访问到对应的类、方法或字段的内存地点,而不必要再进行一次动态的剖析过程。
初始化
这个阶段是为了让类对象完成初始化并处于可用状态。
当类中包罗静态变量或静态代码块时,JVM会天生一个clinit方法用来包罗这些静态代码并实行,假如没有静态代码,则不会天生clinit方法。
注意:
与链接阶段中对静态变量分配内存并设置默认初始值差别,此阶段是显示进行赋值,区别如下:
- public class Example {
- // 静态变量在链接阶段的准备阶段分配内存并设置零初始值
- // 静态变量 num 被初始化为 0
- // 静态变量 str 被初始化为 null
- // 注意:这只是准备阶段的设置,默认初始值,并没有执行具体的赋值操作
- public static int num;
- public static String str;
- static {
- // 静态代码块中的赋值操作,将 num 和 str 初始化为实际的初始值
- num = 42;
- str = "Hello, World!";
- }
- public static void main(String[] args) {
- // 在初始化阶段,静态变量 num 和 str 被赋予实际的初始值
- System.out.println(num); // 输出 42
- System.out.println(str); // 输出 Hello, World!
- }
- }
复制代码 对象的创建流程
- 检查类是否已加载
- 在堆中为对象的创建分配内存
- 初始化内存(根本数据类初始化)
- 设置对象头(mark word和类指针)
- 实行构造方法,返回新创建对象的引用 (Class对象的创建略有差别,是通过JVM直接设置相关属性完成最终初始化的)
思考:
- 调用构造函数之前,对象还没有创建实例,没有对象实例该怎样设置对象头?
在内存分配和对象头设置完成后,JVM会持有这个内存地点的引用。这个引用相当于“对象实例”,但还没有完全初始化,JVM会将这个引用传递给构造函数。在构造函数中,this引用指向的就是这个已经分配了内存和设置了对象头的内存区域。
- 对象创建的过程是看不见的,是否可以通过代码或工具证实创建过程
// 字节码参考java捏造机规范
- public class Test {
- public static void main(String[] args) {
- Test obj = new Test();
- }
- }
- //javap -c Test 反编译结果
- 0: new #2 // 创建对象 ,未完整创建
- 3: dup // 复制对象引用
- 4: invokespecial #1 // 调用构造函数
- 7: astore_1 // 保存引用
- 8: return // 返回
- // 说明:
- //new 指令不会完全创建新实例;直到对未初始化的实例调用
- //实例初始化方法invokespecial指令
复制代码 从字节码可以看到,JVM先创建对象(堆区分配了内存且设置了对象头的内存区域)并复制引用,然后调用构造函数,构造函数中this将引用内存区域的地点。
思考:
对象实例只是JVM中存储了数据的一片内存空间,实例化、初始化都是通过在内存中存储数据所呈现的差别的状态或阶段。
怎样定位对象
JVM中有两种定位对象的方式,差别的JVM实现,定位方式大概差别。
句柄
- +------------+ +------------+ +------------------+
- | 引用 | --> | 句柄表 | --> | 对象实例 |
- +------------+ +------------+ +------------------+
- | ref: handle | | handle: obj_ptr | | data |
- +------------+ +------------+ +------------------+
复制代码 在句柄方式中,引用变量包罗一个指向句柄表的指针。句柄表中的每个条目包罗指向现实对象实例数据的指针。
优点:当对象在内存中移动时,只必要更新句柄表中的指针即可,而不必要修改引用本身。(个人感觉有点像代码中的防腐层)
缺点:复杂,必要额外维护句柄表
直接指针
- +------------+ +------------------+
- | 引用 | --> | 对象实例 |
- +------------+ +------------------+
- | ref: obj_ptr | | data |
- +------------+ +------------------+
复制代码 在直接指针方式中,引用变量直接指向对象在堆中的现实内存地点。如许,当引用访问对象时,只需一次内存定位操作即可。
优点:直接指针访问更快,因为少了一次间接访问。
缺点:对象在堆中的移动和内存压缩(Compact),必要修改全部引用。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |