一、对象的内存布局
对象的内存结构又可以被分为:对象头,实例数据,对象填充
对象头:对象头结构在32位JVM与64位JVM中的实现细节是不同的
32bit:
64bit:
实例数据:对象真正存储的有效信息,也是在程序代码中定义的各种类型字段内容。无论是从父类继承下来的还是子类定义的,都需要记录下来。
对象填充:没有实际意义,仅仅起着占位符的作用。以为对象的大小必须是8字节的整数倍。
二、对象的访问定位
对象的访问定位分为两种:
通过句柄访问对象:
通过指针访问对象:
两种对象访问方式各有优势,使用句柄访问最大好处是reference中存储的是稳定的句柄地址,在对象被移动是只会改变句柄中的实例数据指针,而reference本身不需要修改。
使用直接指针访问方式的最大好处是速度快,他节省了一次指针定位的时间开销。
三、对象的创建过程
这里主要讨论实例化普通对象的过程,不包括数组和Class对象。
1、检查指令的参数是否可以在常量池中定位到一个类的符号引用
2、检查这个符号引用代表的类是否被加载,解析初始化过
3.1、如果被加载并初始化了继续下一步
3.2、如果没有没有初始化,那么先执行类的加载过程
4、位新生对象分配内存(两种方法)
1)指针碰撞
如果Java 堆当中的内存是规则的,已使用内存在一边,未使用内存在另外一边, 中间 放着一个指针作为分界点的指示器,如果需要分配内存就只需要把指针向空闲空间那边挪动一段与对象大小相等的距离
2)空闲列表
如果已用内存和空闲内存是交互存在的,这样虚拟机就必须维护一个列表,记录哪块内存是可用的,在分配内存的时候从列表中找到一个足够大的空间划分给对象实例,并且更新空闲列表。
分配内存时可能出现的问题:并发创建对象会出现指针没来得及修改导致的重复地址覆盖对象
解决办法:
- 分配内存动作同步-采用 CAS配上失败重试方式保证更新操作的原子性
- 预先给 Java 线程在堆中分配一小块内存,称为本地线程分配缓冲(TLAB),只有当该线程用完才需要同步锁。是否使用 TLAB 可以通过 -XX:+/-UseTLAB
5、将分配到的内存空间都初始化为零值(不包括对象头)
6、对对象进行必要的设置
例如:
- 设置这个对象是哪个类的实例
- 如何才能找到类的元数据信息
- 对象的hash码
- 对象的GC分代年龄
- 是否启用偏向锁
7、执行<init>方法,可以根据一段代码来讨论变量的具体初始化过程:
class Parent {
private int p_01 = getP_01();
private static int p_02 = getP_02();
{
System.out.println("I am Parent Local code block");
}
static {
System.out.println("I am Parent static code block");
}
public Parent() {
System.out.println("I am Parent Constructor!");
}
private int getP_01() {
System.out.println("p_01 is initialized!");
return 13;
}
private static int getP_02() {
System.out.println("static p_02 is initialized!");
return 14;
}
}
class Son extends Parent {
private int s_01 = getS_01();
private static int s_02 = getS_02();
{
System.out.println("I am Son Local code block");
}
static {
System.out.println("I am Son static code block");
}
public Son() {
System.out.println("I am Son Constructor!");
}
private int getS_01() {
System.out.println("s_01 is initialized!");
return 13;
}
private static int getS_02() {
System.out.println("static s_02 is initialized!");
return 14;
}
}
public class CreateObject {
public static void main(String[] args) {
Son s = new Son();
}
}
执行结果如下:
static p_02 is initialized!
I am Parent static code block
static s_02 is initialized!
I am Son static code block
p_01 is initialized!
I am Parent Local code block
I am Parent Constructor!
s_01 is initialized!
I am Son Local code block
I am Son Constructor!
然后可以总结得出<init>初始化过程如下:
1)父类静态变量,静态代码块执行初始化(静态变量,静态代码按顺序执行,属于同一优先级)
2)子类静态变量,静态代码块执行初始化
3)父类全局变量,代码块被初始化(全局变量,代码按顺序执行,属于同一优先级)
4)父类构造函数执行
5)子类全局变量,代码块被初始化
6)子类构造函数被执行
误区:其实并不是真的子类构造函数在父类构造函数之后执行,而是在子类构造器的第一行隐式调用了父类的构造函数;
- 大小: 12.4 KB
- 大小: 9.5 KB
- 大小: 47.2 KB
- 大小: 43.1 KB
分享到:
相关推荐
理解对象内存布局有助于优化程序性能,例如减少对象创建、理解和使用对象池,或者通过调整JVM参数来改善垃圾回收效率。`SizeOfAgent.java`、`Rule1.java`和`Rule2.java`可能包含了用于分析对象大小的代码,而`rule1....
本文将详细探讨Java对象在JVM中的创建过程以及其内存布局,帮助读者更深入地理解Java对象是如何在内存中产生的。 #### 二、对象的创建 Java对象是由类实例化的结果,当我们使用`new`关键字创建一个对象时,实际上...
在Java中,对象的创建主要包括定义对象变量和构造对象两个步骤,而构造对象的过程又分为为对象分配内存和初始化对象两个阶段。 ##### 3.1 定义对象变量 定义对象变量实际上就是定义一个指向对象的引用,例如: ```...
Java对象的内存布局是Java虚拟机(JVM)中至关重要的一部分,它涉及到对象创建、内存分配以及对象访问等核心概念。在Java程序中,我们通常使用`new`关键字创建对象,但这只是表面操作,背后涉及的内存管理和构造器...
首先,我们来关注对象的创建过程。在Java中,对象的创建始于`new`关键字。当执行`new`操作符时,JVM会执行以下步骤: 1. 分配内存:JVM在堆内存中为新对象分配空间。堆是Java中存储对象的主要区域,由垃圾收集器...
例如,`org.openjdk.jol`(Java Object Layout)库提供了一种方便的方式来获取对象的内存布局信息。 总结来说,这个例子旨在帮助开发者理解Java对象在内存中的表示方式,以及不同类型和结构的对象如何影响内存使用...
在Java编程语言中,计算一个对象的大小是一个相对复杂的过程,因为对象的内存布局涉及到多个因素,包括对象头、实例字段、对齐填充等。这个主题通常与性能优化和内存管理有关,尤其是在处理大规模数据结构时。这篇...
Java虚拟机(JVM)内存结构是Java程序运行的基础,它将内存划分为若干个不同的数据区域,包括...而Java对象模型则决定了对象在内存中的布局和访问方式。每个主题都值得深入研究,以便更好地理解和调试Java程序的行为。
总结来说,类对象的创建及内存解析是Java编程的基础,理解这些概念对于编写高效、无错误的代码至关重要。通过掌握对象实例化、内存分配、垃圾收集以及类和数组的特殊性,开发者可以更好地管理和优化程序的内存使用,...
对象内存布局则涉及到实例字段、对齐填充等,JVM会根据这些信息计算出对象的实际内存地址。 在“Java界面版 内存地址转换的三种方式过程演示”项目中,开发者可能创建了一个用户界面,用户可以通过这个界面触发不同...
2. 关闭压缩参数后的对象内存布局: 如果关闭这两项压缩选项,对象引用将变为64位,即8字节。实验显示`Person`对象占用40字节,这是因为对象头的Klass Pointer从4字节增长到8字节,其他部分保持不变,总大小增加了8...
总的来说,计算Java对象的大小是一个复杂的过程,涉及到JVM的内部机制。开发者可以通过各种工具和源码分析来获取这些信息,并将其应用于实际的性能优化工作中。理解这些细节不仅有助于提高程序效率,也是成为一名...
JVM会根据对象的类型信息解析出字段在对象内存布局中的相对位置,然后计算出实际地址进行访问。 在Java的HotSpot虚拟机中,使用了一种叫做“指针压缩”的技术来优化内存占用。在64位系统上,如果不开启指针压缩,...
七、对象内存布局 在HotSpot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。对象头包括两部分信息:运行时数据和类型指针。运行时数据用于...
当一个Java对象被创建时,它会占用内存空间,这个空间包括对象头、实例数据和对齐填充三部分。 1. **对象头**:对象头包含了对象的元数据,如对象的类型信息、哈希码、GC分代年龄等。在HotSpot JVM中,对象头分为两...
Java虚拟机(JVM)在创建和管理对象时涉及多个关键概念和技术,这些概念与对象的内存布局、对象头、对象锁以及`synchronized`关键字的底层实现密切相关。在JVM中,对象的创建过程分为几个步骤: 1. **类加载检查**:...
本章节将通过《JAVA 内存视图与封装》这一课件,详细介绍Java程序在运行时内存布局的基本概念及其重要性。 #### 二、对象的内存视图 对象的内存视图是指在程序运行过程中,对象实例在计算机内存中的具体表现形式。...
总结来说,Java内存模型定义了对象的内存布局以及在多线程环境中对共享变量访问的规则,它为Java程序员提供了一个高级的内存模型来保证内存的有序性和一致性。理解和应用Java内存模型是编写高效且稳定Java程序的关键...
3. **堆内存**:Java对象的主要存储区域,所有实例对象和数组都存储在堆中。堆内存是线程共享的,通过垃圾收集器进行自动内存管理。Java的新生代、老年代和永久代(JDK 8前)都是堆的一部分,它们各自有不同的垃圾...
堆内存是Java对象的主要存储区域,而栈内存主要用于存储方法的局部变量。方法区存储类的信息,如类名、方法信息等。程序计数器记录了当前线程执行的指令地址,本地方法栈则服务于Java的本地方法接口。 接着,我们...