JVM相关流程的总结

打印 上一主题 下一主题

主题 1070|帖子 1070|积分 3210

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
记录和思考,不断完善中…
  类加载的流程

java的类加载机制是按需加载,当必要某个类时,通过类的全限定名或其他方式获取对应的字节码文件,然后类加载器会采用双亲委派模型对字节码文件进行加载,即起首尝试使用父类加载器进行加载,假如加载不了,再交由子类加载器进行加载,这种加载方式可以有效保护java的焦点类库。加载的过程如下:
加载阶段

就是将类的字节码加载到内存中并天生代表该类的Class对象,这个阶段中类的静态存储布局转化为方法区的运行时数据布局,而创建的Class对象就是方法区中这个类的数据访问入口
   个人明白:
可以认为类加载的过程中,在加载阶段就完成了外部类到JVM内的加载过程,因为此时确实已经在堆区创建了代表类的class对象了。加载过程的后续环节可以认为是对class对象的不断丰富,比如校验,初始化等。
  链接

包罗验证、准备、剖析三个小阶段
验证

验证字节码正当性
   思考:为什么在加载之前不进行验证呢?假如字节码不正当,岂不是进行一次无用的加载。
Class对象是类在JVM中的表现形式,必要在加载进JVM并天生相应的代表此类的Class对象后,才气通过Class对象对该类的元数据进行各种判定验证,以确保该class不会对JVM运行产生危害。
  准备

对类对象的静态变量设置默认初始值。根本范例,如int设置为0,对象则设置为null。
剖析

这个过程则是将类对象中的符号引用剖析成直接引用。这一阶段的主要任务是剖析类的常量池中的符号引用
   扩展:
  

  • 符号引用: 在 Java 源代码中,我们使用的类名、方法名、字段名等都是符号引用。这些符号引用在编译时是无法直接定位到内存地点的,而是必要在运行时动态剖析。
  • 直接引用: 直接引用是可以直接定位到内存地点的引用,是符号引用经过剖析后的效果。直接引用使得捏造机可以或许快速访问到对应的类、方法或字段的内存地点,而不必要再进行一次动态的剖析过程。
  初始化

这个阶段是为了让类对象完成初始化并处于可用状态。
当类中包罗静态变量或静态代码块时,JVM会天生一个clinit方法用来包罗这些静态代码并实行,假如没有静态代码,则不会天生clinit方法。
   注意:
与链接阶段中对静态变量分配内存并设置默认初始值差别,此阶段是显示进行赋值,区别如下:
  1. public class Example {
  2.     // 静态变量在链接阶段的准备阶段分配内存并设置零初始值
  3.     // 静态变量 num 被初始化为 0
  4.     // 静态变量 str 被初始化为 null
  5.     // 注意:这只是准备阶段的设置,默认初始值,并没有执行具体的赋值操作
  6.     public static int num;
  7.     public static String str;
  8.     static {
  9.         // 静态代码块中的赋值操作,将 num 和 str 初始化为实际的初始值
  10.         num = 42;
  11.         str = "Hello, World!";
  12.     }
  13.     public static void main(String[] args) {
  14.         // 在初始化阶段,静态变量 num 和 str 被赋予实际的初始值
  15.         System.out.println(num); // 输出 42
  16.         System.out.println(str); // 输出 Hello, World!
  17.     }
  18. }
复制代码
对象的创建流程


  • 检查类是否已加载
  • 在堆中为对象的创建分配内存
  • 初始化内存(根本数据类初始化)
  • 设置对象头(mark word和类指针)
  • 实行构造方法,返回新创建对象的引用 (Class对象的创建略有差别,是通过JVM直接设置相关属性完成最终初始化的)
   思考:
  

  • 调用构造函数之前,对象还没有创建实例,没有对象实例该怎样设置对象头?
    在内存分配和对象头设置完成后,JVM会持有这个内存地点的引用。这个引用相当于“对象实例”,但还没有完全初始化,JVM会将这个引用传递给构造函数。在构造函数中,this引用指向的就是这个已经分配了内存和设置了对象头的内存区域。
  • 对象创建的过程是看不见的,是否可以通过代码或工具证实创建过程
    // 字节码参考java捏造机规范
  1. public class Test {
  2.     public static void main(String[] args) {
  3.         Test obj = new Test();
  4.     }
  5. }
  6. //javap -c Test   反编译结果
  7. 0: new           #2                  // 创建对象 ,未完整创建
  8. 3: dup                               // 复制对象引用
  9. 4: invokespecial #1                  // 调用构造函数
  10. 7: astore_1                          // 保存引用
  11. 8: return                            // 返回
  12. // 说明:
  13. //new 指令不会完全创建新实例;直到对未初始化的实例调用
  14. //实例初始化方法invokespecial指令
复制代码
从字节码可以看到,JVM先创建对象(堆区分配了内存且设置了对象头的内存区域)并复制引用,然后调用构造函数,构造函数中this将引用内存区域的地点。
   思考:
对象实例只是JVM中存储了数据的一片内存空间,实例化、初始化都是通过在内存中存储数据所呈现的差别的状态或阶段。
  怎样定位对象

JVM中有两种定位对象的方式,差别的JVM实现,定位方式大概差别。


  • 通过句柄定位对象
  • 通过直接指针定位对象
句柄

  1. +------------+     +------------+      +------------------+
  2. |   引用      | --> |   句柄表    | --> |    对象实例       |
  3. +------------+     +------------+      +------------------+
  4. | ref: handle |     | handle: obj_ptr |      |  data      |
  5. +------------+     +------------+      +------------------+
复制代码
在句柄方式中,引用变量包罗一个指向句柄表的指针。句柄表中的每个条目包罗指向现实对象实例数据的指针。
优点:当对象在内存中移动时,只必要更新句柄表中的指针即可,而不必要修改引用本身。(个人感觉有点像代码中的防腐层)
缺点:复杂,必要额外维护句柄表
直接指针

  1. +------------+      +------------------+
  2. |   引用      |  --> |    对象实例       |
  3. +------------+      +------------------+
  4. | ref: obj_ptr |      |     data       |
  5. +------------+      +------------------+
复制代码
在直接指针方式中,引用变量直接指向对象在堆中的现实内存地点。如许,当引用访问对象时,只需一次内存定位操作即可。
优点:直接指针访问更快,因为少了一次间接访问。
缺点:对象在堆中的移动和内存压缩(Compact),必要修改全部引用。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

鼠扑

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表