在Sun JVM中,(除了数组之外的)对象都有两个机器字(words)的头部。第一个字中包含这个对象的标示哈希码以及其他一些类似锁状态和等标识信息,第二个字中包含一个指向对象的类的引用。另外,任何对象都是8个字节为粒度进行对齐的。这就是对象内存布局的第一个规则:
规则1:任何对象都是8个字节为粒度进行对齐的。
比如,如果调用new Object(),由于Object类并没有其他没有其他可存储的成员,那么仅仅使用堆中的8个字节来保存两个字的头部即可。
继承了Object的类的内存布局
除了上面所说的8个字节的头部,类属性紧随其后。属性通常根据其大小来排列。例如,整型(int)以4个字节为单位对齐,长整型(long)以8个字节为单位对齐。这里是出于性能考虑而这么设计的:通常情况下,如果数据以4字节为单位对齐,那么从内存中读4字节的数据并写入到处理器的4字节寄存器是性价比更高的。
为了节省内存,Sun VM并没有按照属性声明时的顺序来进行内存布局。实际上,属性在内存中按照下面的顺序来组织:
1. 双精度型(doubles)和长整型(longs)
2. 整型(ints)和浮点型(floats)
3. 短整型(shorts)和字符型(chars)
4. 布尔型(booleans)和字节型(bytes)
5. 引用类型(references)
内存使用率会通过这个机制得到优化。例如,如下声明一个类:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class MyClass {
byte a;
int c;
boolean d;
long e;
Object f;
} |
如果JVM并没有打乱属性的声明顺序,其对象内存布局将会是下面这个样子:
1
2
3
4
5
6
7
8
9
|
[HEADER: 8 bytes] 8
[a: 1 byte ] 9
[padding: 3 bytes] 12
[c: 4 bytes] 16
[d: 1 byte ] 17
[padding: 7 bytes] 24
[e: 8 bytes] 32
[f: 4 bytes] 36
[padding: 4 bytes] 40
|
此时,用于占位的14个字节是浪费的,这个对象一共使用了40个字节的内存空间。但是,如果用上面的规则对这些对象重新排序,其内存结果会变成下面这个样子:
1
2
3
4
5
6
7
8
|
[HEADER: 8 bytes] 8
[e: 8 bytes] 16
[c: 4 bytes] 20
[a: 1 byte ] 21
[d: 1 byte ] 22
[padding: 2 bytes] 24
[f: 4 bytes] 28
[padding: 4 bytes] 32
|
这次,用于占位的只有6个字节,这个对象使用了32个字节的内存空间。
因此,对象内存布局的第二个规则是:
规则2:类属性按照如下优先级进行排列:长整型和双精度类型;整型和浮点型;字符和短整型;字节类型和布尔类型,最后是引用类型。这些属性都按照各自的单位对齐。
现在我们知道如何计算一个继承了Object的类的实例的内存大小了。下面这个例子用来做下练习: java.lang.Boolean。这是其内存布局:
1
2
3
|
[HEADER: 8 bytes] 8
[value: 1 byte ] 9
[padding: 7 bytes] 16
|
Boolean类的实例占用16个字节的内存!惊讶吧?(别忘了最后用来占位的7个字节)。
继承其他类的子类的内存布局
JVM所遵守的下面3个规则用来组织有父类的类的成员。对象内存布局的规则3如下:
规则3:不同类继承关系中的成员不能混合排列。首先按照规则2处理父类中的成员,接着才是子类的成员。
举例如下:
1
2
3
4
5
6
7
8
9
|
class A {
long a;
int b;
int c;
} class B extends A {
long d;
} |
类B的实例在内存中的存储如下:
1
2
3
4
5
|
[HEADER: 8 bytes] 8
[a: 8 bytes] 16
[b: 4 bytes] 20
[c: 4 bytes] 24
[d: 8 bytes] 32
|
如果父类中的成员的大小无法满足4个字节这个基本单位,那么下一条规则就会起作用:
规则4:当父类中最后一个成员和子类第一个成员的间隔如果不够4个字节的话,就必须扩展到4个字节的基本单位。
举例如下:
1
2
3
4
5
6
7
8
9
10
11
12
|
class A {
byte a;
} class B {
byte b;
} [HEADER: 8 bytes] 8
[a: 1 byte ] 9
[padding: 3 bytes] 12
[b: 1 byte ] 13
[padding: 3 bytes] 16
|
注意到成员a被扩充了3个字节以保证和成员b之间的间隔是4个字节。这个空间不能被类B使用,因此被浪费了。
最后一条规则在下面情况下用来节省一些空间:如果子类成员是长整型或双精度类型,并且父类并没有用完8个字节。
规则5:如果子类第一个成员是一个双精度或者长整型,并且父类并没有用完8个字节,JVM会破坏规则2,按照整形(int),短整型(short),字节型(byte),引用类型(reference)的顺序,向未填满的空间填充。
举例如下:
1
2
3
4
5
6
7
8
9
|
class A {
byte a;
} class B {
long b;
short c;
byte d;
} |
其内存布局如下:
1
2
3
4
5
6
7
|
[HEADER: 8 bytes] 8
[a: 1 byte ] 9
[padding: 3 bytes] 12
[c: 2 bytes] 14
[d: 1 byte ] 15
[padding: 1 byte ] 16
[b: 8 bytes] 24
|
在第12字节处,类A“结束”的地方,JVM没有遵守规则2,而是在长整型之前插入一个短整型和一个字节型成员,这样可以避免浪费3个字节。
数组的内存布局
数组有一个额外的头部成员,用来存放“长度”变量。数组元素以及数组本身,跟其他常规对象同样,都需要遵守8个字节的边界规则。
下面是一个有3个元素的字节数组的内存布局:
1
2
3
4
5
|
[HEADER: 12 bytes] 12
[[ 0 ]: 1 byte ] 13
[[ 1 ]: 1 byte ] 14
[[ 2 ]: 1 byte ] 15
[padding: 1 byte ] 16
|
下面是一个有3个元素的长整型数字的内存布局:
1
2
3
4
5
|
[HEADER: 12 bytes] 12
[padding: 4 bytes] 16
[[ 0 ]: 8 bytes] 24
[[ 1 ]: 8 bytes] 32
[[ 2 ]: 8 bytes] 40
|
内部类的内存布局
非静态内部类(Non-static inner classes)有一个额外的“隐藏”成员,这个成员是一个指向外部类的引用变量。这个成员是一个普通引用,因此遵守引用内存布局的规则。内部类因此有4个字节的额外开销。
相关推荐
本文主要为计算机类专业课“软件工程”结课大作业参考资料,主要围绕餐厅的自助点餐系统进行一系列的研究,采用面向对象模型进行开发,完整资源包括需求分析、面向对象设计书、可行性分析、测试文档和使用java初步...
"基于WFMC规范的工作流引擎——Java对象模型转换为DB数据模型接口的解决方案.pdf" 基于WFMC规范的工作流引擎是指遵循WFMC(WorkFlow Management Coalition,工作流管理联盟)制定的规范和标准,开发出的工作流引擎...
对象模型:描述对象、类及其相互之间关系的静态数据结构;用对象图加以描述。 动态模型:描述系统内对象的相互作用;通常采用状态图加以表示。 功能模型:描述系统内数据数据的变化工程,通常可采用数据流图形式加以...
JAXB与其他XML处理技术(如DOM、SAX、StAX)相比,更注重于对象模型与XML之间的绑定,适用于需要频繁进行对象与XML转换的场景。而DOM适合处理小规模的XML文档,SAX和StAX则适用于大文件流式处理。 总结,JAXB是Java...
《Java面向对象DVD管理系统》是一套综合性的软件应用,它以Java编程语言为基础,采用面向对象的设计理念,实现了包括查询、租借等在内的多种功能,旨在有效地管理DVD资源。在这个系统中,我们可以深入探讨以下几个...
Java3D是建立在Java平台之上的,支持32位和64位操作系统,旨在提供跨平台的3D图形渲染能力。以下是关于Java3D的一些关键知识点: 1. **Java3D架构**:Java3D的架构基于OpenGL和DirectX等底层图形库,通过Java的API...
在这个"java面向对象---宠物领养系统"案例中,我们将深入探讨如何运用面向对象的三大基本概念:封装、继承和多态。我们将特别关注方法的封装和重写,以及流程控制语句的应用。 1. 封装: 封装是面向对象编程的基础...
Java内存模型(Java Memory Model,JMM)是Java虚拟机(JVM)中的一种内存模型,它描述了程序中各个变量之间的关系,以及在实际计算机系统中将变量存储到内存和从内存中取出变量这样的底层细节。JMM允许编译器和缓存...
面向对象与对象模型是软件开发领域中的核心概念,特别是在Java这样的面向对象编程语言中尤为重要。面向对象编程(Object-Oriented Programming, OOP)是一种基于对象的编程范式,它强调将数据和操作数据的方法封装在...
Java Swing电子相册管理系统是一个基于面向对象思想设计的桌面应用程序,它允许用户管理和查看他们的照片集合。Swing是Java提供的一种用于创建图形用户界面(GUI)的库,它提供了丰富的组件和工具,使得开发者可以...
### 基于Java的对象模型消息机制实现 #### 摘要 Java作为一种广泛应用的面向对象编程语言,虽然强大且灵活,但在标准中并未直接提供消息机制的支持。这导致了在面向对象建模中实现消息机制较为不便。本文介绍了一...
面向对象设计java信息系统部署,软件的MVC模型 JDBC访问数据流程 (1)加载驱动程序 (2)连接数据库 (3)创建SQL执行器 (4)执行SQL语句 (5)处理查询结果 (6)断开与数据库的连接
Hibernate是一个持久层框架,它简化了Java对象与数据库表之间的映射过程,实现了ORM(对象关系映射)。在模型库管理系统中,Hibernate可能是用来管理模型数据的主要工具,它可以自动处理SQL语句的生成和执行,大大...
针对上述问题,本文利用Java的接口、继承及多态机制,实现一种风格与微软基础类库中framework的消息机制相似的对象模型消息机制,将其应用于一款基于LEGO机器人的协同式智能化多机器人系统,结果证明该消息机制具有...
Java内存模型(Java Memory Model,JMM)是Java虚拟机(JVM)规范中的一个重要组成部分,它定义了程序中各个变量(包括实例域、静态域和数组元素)的访问规则,以及在实际计算机系统中如何将这些变量存储在内存和从...
### Java内存模型(有助理解多线程) #### JMM简介 Java内存模型(JMM,Java Memory Model)是Java虚拟机规范中一个重要的概念,它规定了程序中各种变量(包括实例字段、静态字段和数组元素)的访问规则,以及在...
在新闻推荐系统的设计与实现中,Java作为一种广泛使用的开发语言,具有强大的面向对象特性、丰富的类库和跨平台的优势,因此被广泛应用。本系统利用Java技术构建了一个高效、精准的新闻推荐平台,旨在为用户提供个性...
堆内存主要用于存储Java对象实例和数组。当程序创建一个新的对象时,对象会被分配在堆内存中,并且每个对象都有一个指向它的引用,这个引用通常被存储在栈内存中。 - **对象的生命周期**:对象在堆内存中的生命周期...