类对象的生命周期

参考文章:类加载过程详解 | JavaGuide

http://www.iigrowing.cn/java_dui_xiang_tou_xiang_jie.html

类的生命周期

类的生命周期

类的加载过程

  • 编写相应的.java文件
  • 通过javac编译为.class文件
    • 为编译期间可确定的常量赋值
  • 加载(双亲委派机制)
    1. 通过全类名在字节码文件中获取定义此类的二进制字节流
    2. 通过二进制字节流加载到方法区中
    3. 在内存中生成相应的class对象
  • 验证
    1. 验证文件格式
    2. 验证元数据
    3. 验证字节码
    4. 验证符号引用
  • 准备
    • 为类静态变量分配内存并赋初值(如public static int a)
  • 解析
    • 将符号引用转变为直接引用(比如在代码中调用了静态方法A(),此时将其替换成A()方法的实际内存地址)
  • 初始化
    • 为静态变量赋正确的值
  • 使用
  • 卸载
    • 该类所有的实例都已经被回收
    • 该类的类加载器的实例被回收(在 JVM 生命周期内,由 jvm 自带的类加载器加载的类是不会被卸载的。但是由我们自定义的类加载器加载的类是可能被卸载的(jdk 自带的 BootstrapClassLoader, ExtClassLoader, AppClassLoader 负责加载 jdk 提供的类,所以它们(类加载器的实例)肯定不会被回收。而我们自定义的类加载器的实例是可以被回收的,所以使用我们自定义加载器加载的类是可以被卸载掉的。)
    • 该类没有在其他任何地方被引用(如在B类中引用A类的某个静态属性、方法)
    • 虚拟机可以对满足上述 3 个条件的无用类进行回收,这里说的仅仅是“可以”,而并不是和对象一样不使用了就会必然被回收。

对象的加载过程

  • 确保相应的类加载完成
  • 在堆上分配内存并赋初值
    • 当类加载完成后,对象所需内存便可完全确定
    • 内存分配方式:
      • 选择哪种分配方式由 Java 堆是否规整决定,而 Java 堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定
      • 指针碰撞(内存规整)
      • 空闲列表(内存不规整)
    • 内存分配并发问题
      • CAS+失败重试
      • TLAB:为每一个线程预先在 Eden 区分配一块儿内存,JVM 在给线程中的对象分配内存时,首先在 TLAB 分配,当对象大于 TLAB 中的剩余内存或 TLAB 的内存已用尽时,再采用上述的 CAS 进行内存分配
  • 设置对象头
    • 这个对象来自哪个类
    • 对象自身的信息
      • 哈希码
      • GC年龄
      • 偏向锁
      • ……
  • 初始化
    • 如果其父类还未初始化,则先触发该父类的初始化
    • 顺序为父静、子静、父非静、父构造、子非静、子构造
  • 使用
  • 回收
    • 没有被任何强引用指向
    • 被软引用指向,但内存不足时
    • 被弱引用指向,被GC时

双亲委派机制

jdk 自带的 BootstrapClassLoader, ExtClassLoader, AppClassLoader 负责加载 jdk 提供的类,其为组合关系,并非父子类关系

  • BootstrapClassLoader:负责加载 JVM 运行时核心类,这些类位于 JAVA_HOME/lib/rt.jar 文件中,我们常用内置库 java.xxx.* 都在里面,比如 java.util.java.io.java.nio.java.lang.等等
  • ExtensionClassLoader:负责加载 JVM 扩展类,比如 swing 系列、内置的 js 引擎、xml 解析器 等等,这些库名通常以 javax 开头,它们的 jar 包位于 JAVA_HOME/lib/ext/*.jar 中
  • AppClassLoader :它会加载 Classpath 环境变量里定义的路径中的 jar 包和目录,我们自己编写的代码以及使用的第三方 jar 包通常都是由它来加载的
  • 自定义类加载器是AppClassLoader的子类

向上验证,向下加载

双亲委派机制的原理和作用是什么?