微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

JVM学习(三)对象创建过程

对象的创建过程 new指令过程

跟类的加载其实差不多

  1. 检查new指令参数对应的类是否加载,若无则执行类加载
  2. 给对象分配内存
  3. 将对象的实例变量自动初始化为其变量类型的认值
  4. 初始化对象,给实例变量赋予正确的初始值

检查new指令参数对应的类是否加载,若无则执行类加载

当java虚拟机遇到一条字节码new指令时,首先去检查这个指令而的参数是否能定位到一个类的符号引用,并且检查该类是否已被加载,解析和初始化过。如果没有,那必须先执行相应的类加载过程。

给对象分配内存

  • 对象所需内存的大小在类加载后便可确定
  • 给对象分配内存实际上就是把一块确定大小的内存块从Java堆中划分出来
  • 对象头设置

将对象的实例变量自动初始化为其变量类型的认值

  • 将对象的实例变量自动初始化为其变量类型的认值
  • 当给对象分配内存之后,这块存储空间会被清零,这就自动地将对象中的所有基本类型数据都设置成了缺省值(对数字来说就是 0,对 boolean 和 str 也相同),而引用则被设置成了 null;

初始化对象,给实例变量赋予正确的初始值

  • 执行构造函数,也叫做<init>()方法
  • Java 在编译之后会在字节码文件生成 init 方法,称之为实例构造器,该实例构造器会将语句块,变量初始化,调用父类的构造器等操作收敛到 init 方法中,收敛顺序为:
  1. 类变量初始化
  2. 父类语句块
  3. 父类构造函数
  4. 类变量初始化
  5. 子类语句块
  6. 子类构造函数

对象的内存布局

对象头

  • 第一部分用于存储自身运行时的数据例如GC分代年龄、哈希码、锁状态等信息。

当这个对象被synchronized关键字当成同步锁时,围绕这个锁的一系列操作都和Mark Word有关。(我还没学这些锁是啥。。)

https://blog.csdn.net/lkforce/article/details/81128115

  • 第二部分存放指向方法区类型元数据的指针。Java虚拟机通过这个指针来确定该对象是那个类的实例。如果是数组那么还会有一块用于记录数组长度的数据。

实例数据

对象的实例数据就是在java代码中能看到的属性和他们的值。

对齐填充字节

JVM要求java的对象占的内存大小应该是8bit的倍数,所以后面有几个字节用于把对象的大小补齐至8bit的倍数,没有特别的功能

对象的访问定位

句柄

Java堆中将会划分出一块内存来作为句柄池,reference中 存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息,有两个指针。

 

直接指针

使用直接指针访问,Java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而reference中存储的直接就是对象地址,指针放在对象实例的存储中,也就是上面提到的类型指针

(1)使用句柄来访问的最大好处就是reference中存储的是稳 定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中 的实例数据指针,而reference本身不需要修改

(2)使用直接指针访问方式的最大好处就是速度更快,它节省了一次指针定位的时间开销, 由于对象的访问在Java中非常频繁,因此这类开销积少成多后也是一项非常可观的执行成本。(HotSpot采用的就是这个)

对象的内存分配

1.对象优先在Eden区分配,如果Eden区没有足够空间分配将发起一次MinorGC

2.如果对象很大,可以通过PretenureSizeThreshold设置,会直接进入老年代

3.如果对象年龄增加到一定程度,认15就会进入老年代

4.如果相同年龄的所有对象大小总和大于Survivor空间一半,那么年龄大于或等于该年龄的对象就可以直接进入老年代。(Survivor区空间如果设置太小,就会导致对象会导致全部移到老年代) --很重要的调优重点

5.如果MinorGC完后大量对象存活超过了Survivor空间,那么就会把多出的对象放入老年代

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐