1.前言了解Java对象如何创建、存储和使用的整个过程非常重要。 相应的过程是:对象创建、对象内存布局、对象访问和定位。 2.对象创建开发使用时,创建Java
一、简介
了解Java对象从创建、存储到如何使用的整个过程非常重要
对应的过程是:对象创建、对象内存布局、对象访问定位三个过程。
2. 对象创建
开发使用时,创建Java对象只需通过关键字new即可:
A a = 新A();
然而,在虚拟机中创建Java对象相对复杂。
1、创作过程
当遇到关键字new指令时,Java对象创建过程就开始了,整个过程如下:
2. 处理步骤
第 1 步:类加载检查
第二步:为对象分配内存
方法一:指针碰撞
注意:
对象创建是虚拟机中非常频繁的操作。 即使只是修改指针指向的位置,在并发情况下也会造成线程不安全。
因此,给对象分配内存就会存在线程不安全的问题。
线程不安全的解决方案有两种:
同步分配内存空间的行为
虚拟机采用CAS+失败重试的方式保证更新操作的原子性
内存分配行为根据线程划分为不同的内存空间,即每个线程在Java堆中预先分配一小块内存(本地线程分配缓冲区(Local,TLAB)),由哪个线程分配内存,其中线程分配在TLAB上,只有当TLAB用完并分配新的TLAB时才需要同步锁。
2、虚拟机是否使用TLAB可以通过-XX:+/-参数来设置。
步骤3:将内存空间初始化为零值
内存分配完成后,虚拟机需要将分配的内存空间初始化为零(不包括对象头)
1、保证对象的实例字段可以直接使用,无需赋初始值(对应值=0)
2、如果使用本地线程分配缓冲区(TLAB),这个工作过程也可以在分配TLAB时提前进行。
步骤 4:对对象进行必要的设置
比如设置这个对象是哪个类的实例,如何查找类的元数据信息,对象的哈希码,对象的GC分代年龄等信息。
该信息存储在对象的对象头中。
3、Java对象创建后,该对象在Java内存中的存储布局是怎样的?
答:在Java虚拟机()中,对象在Java内存中的存储布局可以分为三部分:
对象头存储区 实例数据存储区 对齐填充存储区
1.对象头区域1.如hash code()、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等。
2、这部分数据被设计为非固定的数据结构,以在很小的空间内存储尽可能多的信息(存储空间会根据对象状态进行复用) 1、是对象的指针它的类元数据
2、虚拟机通过这个指针来判断该对象是哪个类的实例
注意
如果对象是数组,则对象头中还必须有一段数据用于记录数组的长度
因为虚拟机可以通过普通Java对象的元数据信息来确定对象的大小,但是无法从数组的元数据中确定数组的大小。
2.实例数据区
// 虚拟机默认的分配策略如下:
长整型/、整数、/字符、字节/、oop( )
// 从分配策略可以看出,相同宽度的字段总是分配在一起
// 满足这个前提的情况下,父类中定义的变量会出现在子类之前
=真;
// 如果参数值为true,则子类中较窄的变量也可能被插入到父类变量的间隙中。
3. 对齐填充区域
4. 对象的访问和定位 对象创建后,如何访问对象?
答:Java程序通过栈上的引用类型data()来访问Java堆上的对象
由于引用类型data()只指定了对Java虚拟机中对象的引用,因此它并没有定义应该如何使用该引用来定位和访问该对象在堆中的具体位置
所以对象的访问方式取决于虚拟机的实现。 目前主流的对象访问方式有两种: