内的 32 位数据,而对象元数据占用了其余 128 位。
Java 数组对象详解 数组对象(例如一个 int 值数组)的形状和结构与标准 Java 对象相似。主要差别在于数组对象包含说明数组大小的 额外元数据。因此,数据对象的元数据包括: 类:一个指向类信息的指针,描述了对象类型。举例来说,对于 int 字段数组,这是 int[] 类的一个指针。 标记:一组标记,描述了对象的状态,包括对象的散列码(如果有),以及对象的形状(也就是说,对象是否 标记 是数组)。 锁:对象的同步信息,也就是说,对象目前是否正在同步。 大小:数组的大小。 大小
图 3 展示了一个 int 数组对象的布局示例:
组对象的布局示例 图 3. 一个 32 位 Java 进程的 int 数组对象的布局示例
如 图 3 所示,有 160 位的数据用于存储 int 值内的 32 位数据,而数组元数据占用了其余 160 位。对于 byte、 int 和 long 等原语,从内存的方面考虑,单项数组比对应的针对单一字段的包装器对象(Byte、Integer 或 Long) 的成本更高。
更为复杂数据结构详解
良好的面向对象
设计与编程鼓励使用封装(提供接口类来控制数据访问)和委托(使用 helper 对象来实施任务)。 封装和委托会使大多数数据结构的表示形式中包含多个对象。一个简单的示例就是 java.lang.String 对象。 java.lang.String 对象中的数据是一个字符数组,由管理和控制对字符数组的访问的 java.lang.String 对象封装。 图 4 展示了一个
32 位
Java 进程的java.lang.String 对象的布局示例:
图 4. 一个 32 位 Java 进程的 java.lang.String 对象的布局示例
如 图 4 所示,除了标准对象元数据之外,java.lang.String 对象还包含一些用于管理字符串数据的字段。通常情况 下,这些字段是散列值、字符串大小计数、字符串数据偏移量和对于字符数组本身的对象引用。 这也就意味着,对于一个 8 个字符的字符串(128 位的 char 数据),需要有 256 位的数据用于字符数组,224 位的 数据用于管理该数组的 java.lang.String 对象,因此为了表示 128 位(16 个字节)的数据,总共需要占用 480 位 (60 字节)。开销比例为 3.75:1。 总体而言,数据结构越是复杂,开销就越高。下一节将具体讨论相关内容。
32 位和 64 位 Java 对象
之前的示例中的对象大小和开销适用于 32 位 Java 进程。在 背景信息:Java 进程的内存使用 一节中提到,64 位处 理器的内存可寻址能力比 32 位处理器高得多。对于 64 位进程,Java 对象中的某些数据字段的大小(特别是对象元 数据或者表示另一个对象的任何字段)也需要增加到 64 位。其他数据字段类型(例如 int、byte 和 long )的大小 不会更改。图 5 展示了一个 64 位 Integer 对象和一个 int 数组的布局:
图 5. 一个 64 位进程的 java.lang.Integer 对象和 int 数组的布局示例
图 5 表明,对于一个 64 位 Integer 对象,现在有 224 位的数据用于存储 int 字段所用的 32 位,开销比例是 7:1。对于一个 64 位单元素 int 数组,有 288 位的数据用于存储 32 位 int 条目,开销比例是 9:1。这在实际应用
程序中产生的影响在于,之前在 32 位 Java 运行时中运行的应用程序若迁移到 64 位 Java 运行时,其 Java 堆内存 使用量会显著增加。通常情况下,增加的数量是原始堆大小的 70% 左右。举例来说,一个在 32 位 Java 运行时中使 用 1GB Java 堆的 Java 应用程序在迁移到 64 位 Java 运行时之后,通常需要使用 1.7GB 的 Java 堆。 请注意,这种内存增加并非仅限于 Java 堆。本机堆内存区使用量也会增加,有时甚至要增加 90% 之多。
表 1 展示了一个应用程序在 32 位和 64 位模式下运行时的对象和数组字段大小:
表 1. 32 位和 64 位 Java 运行时的对象中的字段大小 字段大小(位)
字段类 字段类型
boolean byte char short int float long double
对象字段 对象元数据
对象 32 位 32 32 32 32 32 32 32 32 32 32
64 位 32 32 32 32 32 32 32 32 64 (32*) 64 (32*