关于对象创建,有很多种方法。比如可以通过反射,或者通过 new关键字来创建。不管是何种方式,最终都是会创建一个对象。而我们平常工作中最常用的就是通过new关键字来创建对象。对于我们而言,只要new一下,就会有一个新的对象供我们使用。但是对于程序,对于虚拟机而言,new一下,它是如何去创建对象的呢?

对于JVM来说,当他碰到new对象的指令时会做如下几个步骤

对象的创建过程

  • 1.判断类是否已经被加载
  • 2.分配内存
    • 问题1 如何分配内存?
      • 指针碰撞
      • 空闲列表
    • 问题2 并发情况下如何去处理内存分配?
      • CAS(compare and swap)
      • TLAB(Thread Local Allocation Buffer)
  • 3.初始化
  • 4.设置对象头
  • 5.执行init方法

1.判断类是否已经被加载

我们都知道当我们要使用某个类的时候,这个类是要先被编译成class文件,然后再被类加载器加载到内存的,其实就是把类的class文件信息加载到方法区的元空间当中。这些信息可以简单的理解为类的版本、字段、方法、接口等一些描述信息。除此之外,class文件还有一个信息会被也会被加到到元空间。也就是常量池。用于存放编译期生成的字面量符号引用
例如JvmStudyDemo生成的常量池,也就是下图标红的区域

实际上当JVM发现你要new的对象的类已经被加载过了以后就会继续往下执行,如果还没被加载,那么会先加载,然后执行接下来的步骤。
关于类的加载这一篇文章有描述 JVM根据源码深度解析类加载器,以及类加载器的原理

2.分配内存

当类已经被加载完毕了,那么会执行第二步,也就是分配内存。我们都知道new对象一般情况来说生成的对象都是会存放在堆当中(当存在栈上分配时,逃逸对象会优先分配在栈当中),那么存放肯定是需要内存空间去存放对象的。这里就涉及到两个问题。

问题1 如何分配内存?

分配内存的方式有两种。第一种是指针碰撞,第二种是空闲列表。

指针碰撞

指针碰撞的前提条件是堆中的内存是规整的,也就是说没有内存碎片的产生。因为对象实际上是以连续的内存空间去存放的。所以,当内存规整的时候,通过指针碰撞的方式就可以更加充分的利用内存。这里画一张图给大家理解一下。

堆中内存是绝对规整的,所有用过的内存都被放在了一边,没有用过的内存放在另外一边。中间通过一个指针来进行划分。当有新new的对象要在堆中划分内存时,这个指针会向空闲内存空间偏移一段可以存放下新对象的内存地址,然后再将新的对象存放到刚刚划分出来的新的内存空间当中。

空闲列表

空闲列表的方式是在内存不规整的情况下的一种内存的分配方式。如下图

空闲列表是指,堆中可用空间跟已经使用的空间都相互交错,就没有办法通过指针碰撞这种方式来进行内存分配。这个时候虚拟机会维护一个列表去记录堆当中大大小小的可用内存空间,当新的对象需要进来分配内存空间的时候,会从空闲列表中找到一块能够存放进新对象的内存区域去存放对象,并且更新空闲列表的记录。

通过这两种方式,我们了解了JVM分配内存的机制。但是这里有一个问题,我们从一开始就在讨论规整的内存与不规整的内存的内存分配方式,但是大家有没有想过堆中的内存规整不规整这个又是由什么导致的呢?

其实这个跟使用的垃圾回收器有关。关于垃圾回收器的我会另外再开一篇文章来讲。

问题2 并发情况下如何去处理内存分配?

创建对象肯定是会发生并发情况的,当某个线程调用的方法在创建对象的时候,他并不知道这个时候会不会有其他线程在这个时候恰巧也在创建对象。这就会产生并发争抢内存的现象。
JVM针对这种现象也给出了相应的解决措施,一种是CAS,另外一种则是TLAB。

CAS(compare and swap)

通过CAS + 失败重试,保证以原子性的方式来对分配内容的动作进行同步处理。

TLAB(Thread Local Allocation Buffer)

TLAB翻译过来叫做本地线程分配缓冲区。是指把内存分配的执行按照线程划分到不同的空间之中进行,也就是说每个开启的线程都会在堆中事先分配一小块内存空间,用这一块空间来存放对象。也就避免的多个线程同时分配对象内存的资源争抢的问题。

JVM默认是开启了TLAB
可以通过 -XX:+/-UseTLAB来决定开启或者关闭,还可以通过-XX:TLABSize指定每个线程的缓冲区大小。

3.初始化

当内存分配好了之后,虚拟机就会将分配到的内存空间都初始化为零值,但是这一过程不包括对对象头的初始化。这一步完成之后,就能够保证访问对象的没有赋值的字段也可以被我们使用了。比如int类型的字段初始值是0,这个初始值可以被我们程序所访问到。

4.设置对象头

对象头是一个非常重要的属性。例如这个对象是哪个类的实例,如何才能找到类的元数据信息,以及对象的哈希码,对象的GC分代年龄等等。这些信息都存放在对象的对象头之中。
虚拟机有很多种,但是我们市面上用的最多的就是HotSpot虚拟机。HotSpot虚拟机的对象在内存中的存储可以划分为三部分。第一部分是对象头(Header),第二部分是实例数据(Instance Data),第三部分是对齐填充(Padding)

5.执行init方法

最后也就是执行init方法了,有看过反编译的class字节码文件的同学就会知道,new指令在class字节码文件中会紧跟着一个init方法,这个就是对对象的属性进行一个赋值操作了
这里可以截个图给大家看一下

那么到这里,整个的对象创建过程已经结束了。一个新的对象被new了出来。
现在你还觉得new一个对象是一个简单的操作了吗? 其实对于程序而言,一点都不简单。

我这里还没有深入展开去讲内存分配的各种机制
比如栈上分配,大对象,长期存活对象,分代年龄判断机制,老年代空间分配担保机制。
这一些机制都将是我们进行JVM调优的重要参考机制。在创建对象这一章先不展开讲,先给大家提一笔。

深入理解JVM的对象创建过程相关推荐

  1. JVM篇--详解对象创建过程-对象结构-对象访问方式

    hello,hello,刚学过的东西瞬间忘记,是我年龄大了还是年龄大了,可我明明才20出头啊(凑不要脸),其实25了,偏题了....今天整理一下关于JVM对象篇的结构,会记录对象创建过程,还有对象的内 ...

  2. JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配

    文章目录 前言 零.排序规范 1.happens-before原则 2.找文档位置 一.一线互联网企业关于对象面试题: (后面回答的就是这几个问题) 二.对象创建过程 三.对象在内存中的存储布局 1. ...

  3. Java类加载及对象创建过程详解

    类加载过程 类加载的五个过程:加载.验证.准备.解析.初始化. 加载 在加载阶段,虚拟机主要完成三件事: 通过一个类的全限定名来获取定义此类的二进制字节流. 将这个字节流所代表的静态存储结构转化为方法 ...

  4. java 创建对象的init_Java 对象创建过程。init 方法和 clinit方法。

    Java 对象创建过程 判断是否加载.分配内存(指针碰撞或者空闲链表).初始化为零值.设置对象头(实例是哪个类的实例.类的元信息位置.GC 分代年龄等).init 方法. Java 虚拟机创建一个对象 ...

  5. java方法区对象类型_浅谈Java内存区域与对象创建过程

    一.java内存区域 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域.这些区域都有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而存在,有的区域则 ...

  6. Mybatis--SqlSession对象创建过程

    Mybatis--SqlSession对象创建过程 一. 源码阅读 二. 文字总结 三. 图像总结 mybatis是个很方便的框架,其中最重要的无疑就是session对象了.知其然,知其所以然才能不断 ...

  7. Java 并发编程解析 | 如何正确理解Java对象创建过程,我们主要需要注意些什么问题?

    苍穹之边,浩瀚之挚,眰恦之美: 悟心悟性,善始善终,惟善惟道! -- 朝槿<朝槿兮年说> 写在开头 从接触 Java 开发到现在,大家对 Java 最直观的印象是什么呢?是它宣传的 &qu ...

  8. Java虚拟机:对象创建过程与类加载机制、双亲委派模型

    一.对象的创建过程: 1.对象的创建过程: 对象的创建过程一般是从 new 指令(JVM层面)开始的,整个创建过程如下: (1)首先检查 new 指令的参数是否能在常量池中定位到一个类的符号引用: ( ...

  9. 指令级别解释对象创建过程和DCL为什么要volatile

    先上图 先明白几点 1.jvm stack指的是线程栈,一个线程有一个jvm stack. 2.线程里的一个方法对应一个栈帧,一个栈帧有Local Variables(本地变量表,记录了方法的参数.局 ...

最新文章

  1. 华为eNSP和SecureCRT连接乱码问题
  2. React-状态提升
  3. 【性能优化】EKPO/EKBE和LIPS做JOIN的问题
  4. Web项目中引进EasyUI的路径问题
  5. Buildroot构建指南--Overview
  6. Sentinel(十九)之主流框架的适配
  7. docker 定时重启脚本_使用 Go 添加启动脚本
  8. 掌握MySQL数据库这些优化技巧,事半功倍!
  9. 迭代开发需要一种不同的观点[4]
  10. Linux下查看CPU使用率 --- top命令的使用
  11. C++读写注册表的问题
  12. 大二物竞金牌转北大计算机,物理竞赛保送去北大还是清华?
  13. 4分钟看尽Top编程语言15年沉浮:C#默Java泪,Python终上位!
  14. 吴恩达机器学习课程资源(笔记、中英文字幕视频、课后作业,提供百度云镜像!)
  15. 数据结构PTA期末复习题集
  16. 安装cmsv7的具体方法
  17. Vue 脚手架的搭建
  18. od 查找特征码和特征码模糊搜索教程
  19. 计算机专业软件工程专业学科排名2015,软件工程专业排名
  20. UVM糖果爱好者教程 - 31.provides_responses?

热门文章

  1. 七二法则-“莫道君行早,更有早行人”
  2. Linux-系统管理10-服务器RAID及配置实战
  3. AliOS-Things+ESP32 BLE篇 (1)BLE peripheral
  4. 【已解决】“10.1.1.2‘ is blocked because of many connection errors; unblock with ‘mysqladmin flush-hosts‘
  5. WRAP验厂咨询,WRAP认证证书是受到广泛认可的符合社会和道德标准的象征
  6. Moore Voting
  7. DNA非编码区,外显子,内含子突变区别
  8. linux中top命令cpu,ps命令中的%CPU字段和top命令中的%CPU字段
  9. POI 设置单元格背景色,背景色编码与实际颜色对照表(SXSSFWorkbook4.1.2)
  10. 【解决】JSONDecodeError: Expecting property name enclosed in double quotes