前言

创建对象是 Java 语言绕不开的话题,那么对象是如何创建出来的呢?我们今天就来聊一聊。对象创建第一步就是检查类是否加载,而类的加载又牵扯到类的加载过程。如果单说对象的创建而绕开类的加载过程,感觉有点牵强。干脆就将这2块内容结合起来一起讲。希望通过我的讲解带你搞懂类的加载过程和对象的创建。

在进行阅读前你需要先了解 Java 运行时数据区域 的相关概念,如果你已经了解可以直接向下阅读,如果对Java 运行时数据区域一无所知,请先一步我的另一篇博客 Java 虚拟机学习笔记 | 运行时数据区总结 一文进行了解。

对象的创建和类的加载执行流程

对象的创建

首先介绍一下对象的创建需要大致流程如下:

  1. 类加载检查
  2. 为新对象分配内存
  3. 为对象值初始化为零值
  4. 设置对象头
  5. 执行init方法

类加载检查: 当虚拟机遇到 new 指令的时候会去常量池中检查者个类的符号引用是否存在,进行类加载检查操作,如果没有执行就进行类加载过程。这里所说的 new 执行不是我们写代码时候的 new。这个new指令可以通过 javap进行反汇编进行查看。

如下所示的Demo.java的代码:

编译 Demo.java

将 Demo.class 字节码文件进行反汇编操作。

生成的反汇编内容如下:
0:new 就是虚拟机遇到的 new 指令。

为新对象分配内存: 如果该类已经进行了类加载,会在堆内存中为新对象分配一块内存空间。内存空间的大小在类加载的时候就已经确定了。分配内存空间方式有2种:1. 指针碰撞、2. 空闲列表。
采用那种方式进行,取决于垃圾收集器采用那种收集算法。如果是标记整理那么会采用指针碰撞的方式,如果是标记清除则会采用空闲列表的方式。

分配内存是一个高并发的操作,虚拟机在分配内存的时候通过2中方式来保证线程安全的问题:1. TLAB、 2. CAS+重试。
默认情况下会为每个线程预先在堆上分配一个内存空间,我们称之为 TLAB(Thread Local Allocation Buffer)线程本地分配缓存。当对象内存需要内存空间大于 TLAB 或者 TLAB用完是采用 CAS+重试的方式进行。

为新对象值初始化为零值:
分配完内存后,接下来的操作就是将对象成员属性值设置为零值。这样可以保证这些示例属性在不赋值的情况下就可以使用。需要注意的是这个操作不包含对象头。

设置对象头: 分配完内存后需要对对象头进行设置,类型指针(找到类的元信息)、对象GC分代年龄、线程持有的锁、锁的状态标志(否启用偏向锁) 对象头也被称之为 Mark Word。

对象在内存中的布局包含了3部分:对象头、实例数据、对齐填充。每一块包含的内容如下图所示:

执行init方法: 在执行完上述的操作后对象基本算是在我们的堆内存中创建完毕,但是对象真正的创建才刚刚开始。还需要执行 init方法,完成成员属性字段真实的赋值后才开始按照我们编写的代码的逻辑进行执行。

这里所说的 init 方法就是我们在上面进行反汇编代码中的 invokespecial 指令。如下图所示:

类加载过程

创建对象过程中第一步就是执行类加载检查,如果已经进行类的加载就继续执行对象创建过程,如果没有进行类的加载就会执行类的加载过程。具体执行流程如下:

  • 加载
  • 链接
  • 初始化

链接过程又可以拆分为:

  • 验证
  • 准备
  • 解析

加载: 这里说的加载和类加载不是一回事,加载时类加载的一个执行过程。加载这个动作是读取字节码的信息并将为该类创建一个Class对象到方法区中 也可以理解为 将.class文件加载到方法区中。另外还有一点就是加载和连接是同交叉进行的。

需要注意的是类的加载时通过类加载器执行的。在 Java 虚拟机中类加载器有一下几种:

  • BootstrapClassLoader(启动类加载器)
  • ExtensionClassLoader(扩展类加载器)
  • AppClassLoader(应用程序类加载器)
  • 自定义类加载器


类在通过类加载器进行加载的时候才用的是双亲委派模型,具体的定义就是:
当了进行加载的时候先判断该类是否被加载过,如果已经被加载的类直接返回。如果没有被加载过开始进行加载,加载的过程中会将加载的请求委派给它的父类加载器进行加载。如果父类加载器无法加载时,才由自己的加载器进行加载。最总的效果是我们的加载操作基本都由BootstrapClassLoader(启动类加载器) 执行。简单点理解就是:有啥事各自找各自的爸去做,老爸不做在由自己去做。我个人理解就是啃老模式

每个类都有与他对应的类记加载器,当父类加载器为null时,BootstrapClassLoader(启动类加载器)会作为该类的父类加载器。

这样做的好处是:保证了Java程序的稳定运行,可以避免类的重复加载,保证了 Java 的核心 API 不被篡改。例如:如果不使用双亲委派模型,我们编写一个称为 java.lang.Object 类的话,那么程序运行的时候,系统就会出现多个不同的 Object 类。

验证: 验证会对 文件格式、元数据、字节码、符号引用进行验证。确保我们的代码的正确性。
准备: 为类变量分配内存,并将类变量的值初始化为零值。这步和创建对象过程中 为新对象值初始化为零值 的操作类似。
解析: 将常量池内的符号引用替换为直接引用的过程
初始化: 初始化操作会执行类构造器,并且会对类变量进行真实的赋值操作。这步和创建对象中执行init方法类似。执行完之后就可以开始创建对象了。

类加载过程和对象的创建 具体执行流程图如下:

类加载过程中各阶段在在Java 运行时数据区域处理流程图如下:

类加载过程中各阶段在在Java 运行时数据区域处理流程具体介绍图如下:
对象的创建各阶段在在Java 运行时数据区域处理流程图如下:

小结

关于类加载过程和对象的创建,不仅仅是它们如何创建这么简单。还有很多细节需要我们了解,例如:对象的创建在分配内存的时候需要通过 指针碰撞或空闲列表的方式进行分配;同时关于分配的线程安全问题采用 TLAB 和 CAS+重试的方式来保证;对象内存布局包含:对象头、示例数据、对其填充;类的加载过程中还涉及到类加载器的双亲委派模型来保证程序稳定运行。

参考文献

深入理解Java虚拟机 JVM 高级特性与最佳实践

Java 虚拟机学习笔记 | 类加载过程和对象的创建流程相关推荐

  1. Java虚拟机学习(7):对象内存分配与回收

    对象优先在Eden上分配 大多数情况下,对象优先在新生代Eden区域中分配.当Eden内存区域没有足够的空间进行分配时,虚拟机将触发一次 Minor GC(新生代GC).Minor GC期间虚拟机将E ...

  2. Java虚拟机学习(6):对象访问

    对象访问会涉及到Java栈.Java堆.方法区这三个内存区域. 如下面这句代码: 1 Object objectRef = new Object(); 假设这句代码出现在方法体中,"Obje ...

  3. java虚拟机学习笔记之垃圾收集(上)

    java程序是运行在java虚拟机当中的,在java虚拟机的堆中运行着程序所创建的对象.可以使用new,newarray,anewarray,multianewarray指令来创建对象,但是没有明确的 ...

  4. java 准备 解析_深入理解JAVA虚拟机学习笔记24——类加载的准备和解析

    每天进步一点点! 今天我们一起看一下类加载的准备阶段和解析阶段. 先看一下准备阶段:主要任务是在方法区中为类变量(仅static修饰变量,不包含实例变量)分配内存并设置类变量初始化的阶段. 这里面的区 ...

  5. Java 虚拟机学习笔记 | 运行时数据区总结

    前言 要想学习好 Java,Java虚拟(JVM)的学习是绕不开的.学习 Java虚拟(JVM)首先就要先了解的就是Java虚拟(JVM)运行时数据区. 在Java语言和虚拟机规范中对运行时数据区进行 ...

  6. 【深入理解Java虚拟机学习笔记】第二章 Java 内存区域与内存溢出异常

    最近想好好复习一下java虚拟机,我想通过深读 [理解Java虚拟机 jvm 高级特性与最佳实践] (作者 周志明) 并且通过写一些博客总结来将该书读薄读透,这里文章内容仅仅是个人阅读后简短总结,加强 ...

  7. 深入理解JAVA虚拟机学习笔记(一)JVM内存模型

    摘要:   上周末搬家后,家里的宽带一直没弄好,跟电信客服反映了N遍了终于约了个师傅明天早上来迁移宽带,可以结束一个多星期没网的痛苦日子了.这段时间也是各种忙,都一个星期没更新博客了,再不写之前那种状 ...

  8. 【深入理解Java虚拟机学习笔记】第三章 垃圾收集器与内存分配策略

    最近想好好复习一下java虚拟机,我想通过深读 [理解Java虚拟机 jvm 高级特性与最佳实践] (作者 周志明) 并且通过写一些博客总结来将该书读薄读透,这里文章内容仅仅是个人阅读后简短总结,加强 ...

  9. java虚拟机学习笔记

    一.java运行时数据区域 1.程序计数器 2.虚拟机栈:用于存储局部变量表.操作数栈.动态链接.方法出口等信息 3.本地方法栈:与虚拟机栈的区别是虚拟机栈是为虚拟机执行的java方法服务,本地方法栈 ...

最新文章

  1. 详解Spring中Bean的自动装配~
  2. sniffer使用查网络故障
  3. 利用partial快乐驱动开发
  4. Caused by: java.lang.ClassCastException: weblogic.xml.jaxp.RegistryXMLReader
  5. onvirt安装linux系统
  6. java se 9.0.4_jre 9下载(Java SE Runtime Environment)下载
  7. linux手动注入网络数据_Linux网络 - 数据包的接收过程【转】
  8. 最常用的10种CSS BUG解决方法与技巧-浏览器兼容教程
  9. mysql limit索引变_Mysql limit 优化,百万至千万级快速分页 复合索引的引用并应用于轻量级框架...
  10. php表单验证插件下载,强力推荐10款Javascript表单验证插件
  11. 延边大学计算机考研经验分享
  12. CF1389G Directing Edges
  13. 安卓各版本大变化(Android 6.0到10.0),兼容适配
  14. 电脑连接手机热点用百度云下载一会后断网
  15. 深度探索JFR - JFR详细介绍与生产问题定位落地 - 3. 各种Event详细说明与JVM调优策略(3)
  16. 别费劲找站长工具共享VIP了 这个工具也不错
  17. 【安全牛学习笔记】扫描工具-Nikto
  18. 分享到微信,qq空间,微博
  19. PHP学习案例9 双色球
  20. 基于echarts+js+fexible.js实现的数据可视化适配案例(附源代码)

热门文章

  1. 源码编译打包_Atlas 2.1.0 实践(1)—— 编译Atlas
  2. 计算机科学基础内容摘抄,科学网-上计算机课,不接触计算机----日记摘抄(161)-武夷山的博文...
  3. OpenShift Security (3) - 准备客户端环境和演示应用
  4. OpenShift 4 之使用https协议访问Route
  5. (四)微调ResNet50以诊断COVID-19
  6. 使用Web API ASP.NET Core 2.2部署Angular 8应用程序
  7. odoo10参考系列--网络控制器(Web Controllers)
  8. odoo10参考系列--视图二(表单视图)
  9. java 值 继承_java中继承的数值传递引用
  10. python输入list_python学习(list增删改查、及常用方法)