类对象的生命周期
类对象的生命周期
参考文章:类加载过程详解 | JavaGuide
http://www.iigrowing.cn/java_dui_xiang_tou_xiang_jie.html
类的生命周期
类的加载过程
- 编写相应的.java文件
- 通过javac编译为.class文件
- 为编译期间可确定的常量赋值
- 加载(双亲委派机制)
- 通过全类名在字节码文件中获取定义此类的二进制字节流
- 通过二进制字节流加载到方法区中
- 在内存中生成相应的class对象
- 验证
- 验证文件格式
- 验证元数据
- 验证字节码
- 验证符号引用
- 准备
- 为类静态变量分配内存并赋初值(如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的子类
向上验证,向下加载
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 柳门竹巷!
评论