一、引言

如你所知,Java是一门面向对象的编程语言。我们平常在写代码的时候也是在不停的操作各种对象,那么当你在写出User user = new User();这样一行代码的时候,JVM都做了些什么呢?

二、了解对象

1、内存布局

在Hotspot虚拟机中一个对象的内存布局分为三个部分:对象头、实例数据、对齐填充。

  • 对象头又有两部分的信息,第一部分是用于存储对象自身的运行数据(HashCode、GC分代年龄、锁状态标志等)。另一部分是类型指针,指向它的类元数据,虚拟机通过这个指针确定这个对象是哪个类的实例(如果使用句柄池方式则不会有)。如果是数组还会有一个记录数组长度的如下表所示:

Mark Word是一个非固定的数据结构以便在极小的空间内存储尽量多的信息,它会根据对象的状态复用自己的存储空间。各状态下的存储内容如下表所示:

  • 实例数据部分是真正存储的有效信息,就是在代码中定义的各种类型的字段内容。无论是父类继承下来的,还是在子类中的。
  • 对齐填充不是必须存在的,仅仅起着占位符的作用,因为HotSpot虚拟机要求对象的起始地址必须是8字节的整数倍。

2、对象的访问

Java程序中我们操作一个对象是通过指向这个对象的引用。我们都知道对象存在堆中,这个引用存在虚拟机栈中。那么引用通过什么方式去定位堆中对象的位置呢?

  • 直接指针法(HotSpot实现):引用中直接存储的就是堆中对象的地址。好处就是一次定位速度快,缺点是对象移动(GC时对象移动)引用本身需要修改。

  • 句柄法:Java堆中划分出一部分作为句柄池,引用存储的是对象的句柄地址,而句柄中包括了对象实例和类型的具体位置信息。好处是对象移动只会改变句柄中的实例数据指针,缺点是两次定位。

三、创建对象流程

上面介绍了对象的基本信息,现在来讲一讲创建对象的流程:

  1. 当虚拟机遇到一条new指令时,会去检查这个指令的参数能否在常量池中定位到一个类的符号引用,并检查代表的类是否已经被类加载器加载。如果没有被加载那么必须先执行这个类的加载。
  2. 类加载检查通过后,虚拟机将为新对象分配内存,对象所需内存的大小在类加载后便可以确定。
  3. 内存分配完成后,虚拟机需要将对象初始化为零值,保证对象的实例变量在代码中不赋初始值就能直接使用。类变量在类加载的准备阶段初始化为零值。
  4. 对对象头进行必要信息的设置,比如如何找到类的元数据信息、对象的HashCode、GC分代年龄等。
  5. 经过上述操作,一个新的对象已经产生,但是<init>方法还没有执行,所有的字段都是零值。这时候需要执行<init>方法(构造方法)把对象按照程序员的意愿进行初始化。类变量的初始化操作在类加载的初始化阶段<clinit>方法完成

分配内存有两种方式:

  • Java堆内存是规整的(使用标记整理或带压缩的垃圾收集器),使用一个指针指向空闲位置,分配内存既将指针移动与分配大小相等的距离
  • 内存不是规整的(使用标记清除的垃圾收集器),虚拟机维护一个可用内存块列表,分配内存时从列表中找到一个足够大的内存空间划分给对象并更新可用内存列表。

无法找到足够的内存时会触发一次GC

分配内存时并发问题解决方案:

  • 对分配内存空间的动作进行同步操作---采用CAS失败重试的方式保证更新操作的原子性。
  • 每个线程在堆中预先分配一块小内存,称为本地线程分配缓冲(Thread Local Allocation Buffer,TLAB),哪个线程要分配内存就在它的TLAB上分配,只有TLAB用完并分配新的TLAB时才需要同步锁定。通过-XX:+/-UseTLAB参数来设定。

四、创建对象指令重排序问题

A a = new A();

new一个对象的简单分解动作:

  1. 分配对象的内存空间
  2. 初始化对象
  3. 设置引用指向分配的内存地址

其中2、3两步间会发生指令重排序,导致多线程时如果在初始化之前访问对象则会出现问题,单例模式的双重检测锁模式正是会存在这个问题。可以使用volatile来禁止指令重排序解决问题

new一个对象的时候发生了什么?相关推荐

  1. 第二十二期:New一个对象的时候发生了什么?

    如你所知,Java是一门面向对象的编程语言.我们平常在写代码的时候也是在不停的操作各种对象,那么当你在写出User user = new User();这样一行代码的时候,JVM都做了些什么呢? 作者 ...

  2. new(创建)一个对象时都发生了什么?

    以 Student s=new Student() 为例: Java语言中创建一个对象时的整个过程主要有两个步骤: 1.类加载过程 加载:将类名下的字节码文件加载进虚拟机(VM),并存储在方法区,并转 ...

  3. restful url 设计规范_restFul接口设计规范

    1. 域名 应该尽量将API部署在专用域名之下. https://api.example.com 如果确定API很简单,不会有进一步扩展,可以考虑放在主域名下. https://example.org ...

  4. 符合RESTful的接口规范

    把api放在专属域名下,要带版本号 api的url中应该只有名词,和数据库的表或文档资源相对应:对资源(collection)的具体操作类型则由http方法动词表示 (安全性:不改变资源状态,类似只读 ...

  5. 【Java】类与对象 - 对象的组合

    一个类的成员变量可以是Java允许的任何数据类型,因此,一个类可以把某个对象作为自己的成员变量,也就是说,该对象将其他对象作为自己的组成部分. 组合和复用 如果一个对象a组合了对象b,那么对象a就可以 ...

  6. 花5分钟看这篇之前,你才发现你不懂RESTful

    前言 在学习RESTful 风格接口之前,即使你不知道它是什么,但你肯定会好奇它能解决什么问题?有什么应用场景?听完下面描述我想你就会明白: 在互联网并没有完全流行的初期,移动端也没有那么盛行,页面请 ...

  7. RESTful API -备

    网络应用程序,分为前端和后端两个部分.当前的发展趋势,就是前端设备层出不穷(手机.平板.桌面电脑.其他专用设备......). 因此,必须有一种统一的机制,方便不同的前端设备与后端进行通信.这导致AP ...

  8. RESTful API 设计指南(转)

    一.协议 API与用户的通信协议,总是使用HTTPs协议. 二.域名 应该尽量将API部署在专用域名之下. https://api.example.com 如果确定API很简单,不会有进一步扩展,可以 ...

  9. REST 架构是什么

    为什么80%的码农都做不了架构师?>>>    网络应用程序,分为前端和后端两个部分.当前的发展趋势,就是前端设备层出不穷(手机.平板.桌面电脑.其他专用设备......). 因此, ...

最新文章

  1. 自学python爬虫要多久-初学Python爬虫要学多久?原来这么快
  2. 学习HTML-Beautify.js
  3. 云开发数据库又增新技能!
  4. C#与数据库访问技术总结(十七)
  5. Web应用架构-Full-text Search Service
  6. 还在纠结Dapper或者EF Core?不妨试试“混合ORM”--RepoDb
  7. php 动态生成文件,php动态程序生成静态文件示例
  8. 用Fragments创建动态UI(翻译)
  9. Android 沉浸式状态栏完美解决方案
  10. 条款23: 必须返回一个对象时不要试图返回一个引用
  11. Java线程与Android线程,Android线程篇(三):深入理解Java线程池(一)
  12. C#分割多页Tif文件
  13. Python实现贷款用户的信用评分卡
  14. linux脚本无法执行命令,shell脚本在Windows下能执行,而Linux不能执行的原因及解决...
  15. Neo4j学习笔记-Embedded嵌入模式简单示例
  16. Neo4j CQL 常用语句
  17. 小型电商平台的项目估算
  18. 计算机应用基础题,2017计算机应用基础模拟试题及答案
  19. 二级计算机等级证水平高吗,全国计算机水平一级高还是二级高
  20. javascript代码前端debug调试方法

热门文章

  1. IntelliJ IDEA全界面汉化(官方插件)
  2. Python语言程序设计 - 测验6: 组合数据类型 (第6周)
  3. Kali linux安装步骤,史上最详细的步骤,傻瓜式教学
  4. 用Bootstrap写一份简历
  5. 骨架屏-css实现方式
  6. 【ICLR 2023】RankCSE:基于排序学习的无监督句子表示学习
  7. python的numpy教程_ROS与Python入门教程-使用numpy
  8. python matplotlib 画图保存图片简单例子
  9. 阿里云网站备案与域名解析操作流程
  10. python更复杂的输出格式