为什么在 Java 对象中存在内部碎片,即使每个字段都是 4 字节对齐的?
Why is there internal fragmentation in a Java object even if every field is 4-byte aligned?
简介:
出于研究目的,我使用 JOL (Java Object Layout) tool 分析了 Java 个对象的内部和外部碎片。
在这样做的过程中,我偶然发现了以下内容:
x@pc:~/Util$ java -jar jol-cli-0.9-full.jar internals sun.reflect.DelegatingClassLoader
# WARNING: Unable to attach Serviceability Agent. You can try again with escalated privileges. Two options: a) use -Djol.tryWithSudo=true to try with sudo; b) echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# WARNING | Compressed references base/shifts are guessed by the experiment!
# WARNING | Therefore, computed addresses are just guesses, and ARE NOT RELIABLE.
# WARNING | Make sure to attach Serviceability Agent to get the reliable addresses.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Instantiated the sample instance via sun.reflect.DelegatingClassLoader(java.lang.ClassLoader)
sun.reflect.DelegatingClassLoader object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 3b 13 00 f8 (00111011 00010011 00000000 11111000) (-134212805)
12 4 java.lang.ClassLoader ClassLoader.parent null
16 4 java.util.concurrent.ConcurrentHashMap ClassLoader.parallelLockMap null
20 4 java.util.Map ClassLoader.package2certs (object)
24 4 java.util.Vector ClassLoader.classes (object)
28 4 java.security.ProtectionDomain ClassLoader.defaultDomain (object)
32 4 java.util.Set ClassLoader.domains (object)
36 4 java.util.HashMap ClassLoader.packages (object)
40 4 java.util.Vector ClassLoader.nativeLibraries (object)
44 4 java.lang.Object ClassLoader.assertionLock (object)
48 4 java.util.Map ClassLoader.packageAssertionStatus null
52 4 java.util.Map ClassLoader.classAssertionStatus null
56 8 (alignment/padding gap)
64 1 boolean ClassLoader.defaultAssertionStatus false
65 7 (loss due to the next object alignment)
Instance size: 72 bytes
Space losses: 8 bytes internal + 7 bytes external = 15 bytes total
问题:
在这种情况下困扰我的是每个字段都是 4 字节对齐的(请参阅 OFFSET 列),但仍然在偏移量 56 处添加了对齐间隙 (56 8 (alignment/padding gap)
)。我在 Java 9 中做了同样的测试,对象布局发生了一点变化,alignemnt/padding 差距仍然存在,但甚至大了 12 个字节。
为什么会这样?为什么它是 8 个字节大,而我看到的所有其他对象都是 4 字节内部对齐的?我自己找不到解释。
我的系统:
openjdk version "1.8.0_151"
OpenJDK Runtime Environment (build 1.8.0_151-8u151-b12-0ubuntu0.16.04.2-b12)
OpenJDK 64-Bit Server VM (build 25.151-b12, mixed mode)
使用默认设置(具有压缩 oops 的 ParallelOldGC)
有时 HotSpot 会注入在 Java 级别不可见的内部特定于 VM 的字段。在 ClassLoader
的情况下,这是指向 ClassLoaderData
:
的指针
#define CLASSLOADER_INJECTED_FIELDS(macro) \
macro(java_lang_ClassLoader, loader_data, intptr_signature, false)
这是一个 intptr_t
类型的字段,即恰好 8 个字节长。
以下是 HotSpot 来源 describe ClassLoaderData 的方式:
// A ClassLoaderData identifies the full set of class types that a class
// loader's name resolution strategy produces for a given configuration of the
// class loader.
// Class types in the ClassLoaderData may be defined by from class file binaries
// provided by the class loader, or from other class loader it interacts with
// according to its name resolution strategy.
//
// Class loaders that implement a deterministic name resolution strategy
// (including with respect to their delegation behavior), such as the boot, the
// extension, and the system loaders of the JDK's built-in class loader
// hierarchy, always produce the same linkset for a given configuration.
//
// ClassLoaderData carries information related to a linkset (e.g.,
// metaspace holding its klass definitions).
// The System Dictionary and related data structures (e.g., placeholder table,
// loader constraints table) as well as the runtime representation of classes
// only reference ClassLoaderData.
//
// Instances of java.lang.ClassLoader holds a pointer to a ClassLoaderData that
// that represent the loader's "linking domain" in the JVM.
//
// The bootstrap loader (represented by NULL) also has a ClassLoaderData,
// the singleton class the_null_class_loader_data().
你给了我一个绝好的机会 post 我也看到了一些有趣的东西(这应该是评论,但太长了):
System.out.println(ClassLayout.parseInstance(Class.class).toPrintable());
运行 这会给你:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 27 2b fd (00000001 00100111 00101011 11111101) (-47503615)
4 4 (object header) 5f 00 00 00 (01011111 00000000 00000000 00000000) (95)
8 4 (object header) df 03 00 f8 (11011111 00000011 00000000 11111000) (-134216737)
12 4 Constructor Class.cachedConstructor null
16 4 Class Class.newInstanceCallerCache null
20 4 String Class.name (object)
24 4 (alignment/padding gap) N/A
28 4 SoftReference Class.reflectionData (object)
32 4 ClassRepository Class.genericInfo null
36 4 Object[] Class.enumConstants null
40 4 Map Class.enumConstantDirectory null
44 4 AnnotationData Class.annotationData (object)
48 4 AnnotationType Class.annotationType null
52 4 ClassValueMap Class.classValueMap null
56 32 (alignment/padding gap) N/A
88 4 int Class.classRedefinedCount 0
92 556 (loss due to the next object alignment)
实例大小:648 字节
Space 损失:内部 36 字节 + 外部 556 字节 = 总共 592 字节
或总共 space 丢失 556 字节;我发现这令人印象深刻。
简介:
出于研究目的,我使用 JOL (Java Object Layout) tool 分析了 Java 个对象的内部和外部碎片。
在这样做的过程中,我偶然发现了以下内容:
x@pc:~/Util$ java -jar jol-cli-0.9-full.jar internals sun.reflect.DelegatingClassLoader
# WARNING: Unable to attach Serviceability Agent. You can try again with escalated privileges. Two options: a) use -Djol.tryWithSudo=true to try with sudo; b) echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope
# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# WARNING | Compressed references base/shifts are guessed by the experiment!
# WARNING | Therefore, computed addresses are just guesses, and ARE NOT RELIABLE.
# WARNING | Make sure to attach Serviceability Agent to get the reliable addresses.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Instantiated the sample instance via sun.reflect.DelegatingClassLoader(java.lang.ClassLoader)
sun.reflect.DelegatingClassLoader object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 3b 13 00 f8 (00111011 00010011 00000000 11111000) (-134212805)
12 4 java.lang.ClassLoader ClassLoader.parent null
16 4 java.util.concurrent.ConcurrentHashMap ClassLoader.parallelLockMap null
20 4 java.util.Map ClassLoader.package2certs (object)
24 4 java.util.Vector ClassLoader.classes (object)
28 4 java.security.ProtectionDomain ClassLoader.defaultDomain (object)
32 4 java.util.Set ClassLoader.domains (object)
36 4 java.util.HashMap ClassLoader.packages (object)
40 4 java.util.Vector ClassLoader.nativeLibraries (object)
44 4 java.lang.Object ClassLoader.assertionLock (object)
48 4 java.util.Map ClassLoader.packageAssertionStatus null
52 4 java.util.Map ClassLoader.classAssertionStatus null
56 8 (alignment/padding gap)
64 1 boolean ClassLoader.defaultAssertionStatus false
65 7 (loss due to the next object alignment)
Instance size: 72 bytes
Space losses: 8 bytes internal + 7 bytes external = 15 bytes total
问题:
在这种情况下困扰我的是每个字段都是 4 字节对齐的(请参阅 OFFSET 列),但仍然在偏移量 56 处添加了对齐间隙 (56 8 (alignment/padding gap)
)。我在 Java 9 中做了同样的测试,对象布局发生了一点变化,alignemnt/padding 差距仍然存在,但甚至大了 12 个字节。
为什么会这样?为什么它是 8 个字节大,而我看到的所有其他对象都是 4 字节内部对齐的?我自己找不到解释。
我的系统:
openjdk version "1.8.0_151"
OpenJDK Runtime Environment (build 1.8.0_151-8u151-b12-0ubuntu0.16.04.2-b12)
OpenJDK 64-Bit Server VM (build 25.151-b12, mixed mode)
使用默认设置(具有压缩 oops 的 ParallelOldGC)
有时 HotSpot 会注入在 Java 级别不可见的内部特定于 VM 的字段。在 ClassLoader
的情况下,这是指向 ClassLoaderData
:
#define CLASSLOADER_INJECTED_FIELDS(macro) \
macro(java_lang_ClassLoader, loader_data, intptr_signature, false)
这是一个 intptr_t
类型的字段,即恰好 8 个字节长。
以下是 HotSpot 来源 describe ClassLoaderData 的方式:
// A ClassLoaderData identifies the full set of class types that a class
// loader's name resolution strategy produces for a given configuration of the
// class loader.
// Class types in the ClassLoaderData may be defined by from class file binaries
// provided by the class loader, or from other class loader it interacts with
// according to its name resolution strategy.
//
// Class loaders that implement a deterministic name resolution strategy
// (including with respect to their delegation behavior), such as the boot, the
// extension, and the system loaders of the JDK's built-in class loader
// hierarchy, always produce the same linkset for a given configuration.
//
// ClassLoaderData carries information related to a linkset (e.g.,
// metaspace holding its klass definitions).
// The System Dictionary and related data structures (e.g., placeholder table,
// loader constraints table) as well as the runtime representation of classes
// only reference ClassLoaderData.
//
// Instances of java.lang.ClassLoader holds a pointer to a ClassLoaderData that
// that represent the loader's "linking domain" in the JVM.
//
// The bootstrap loader (represented by NULL) also has a ClassLoaderData,
// the singleton class the_null_class_loader_data().
你给了我一个绝好的机会 post 我也看到了一些有趣的东西(这应该是评论,但太长了):
System.out.println(ClassLayout.parseInstance(Class.class).toPrintable());
运行 这会给你:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 27 2b fd (00000001 00100111 00101011 11111101) (-47503615)
4 4 (object header) 5f 00 00 00 (01011111 00000000 00000000 00000000) (95)
8 4 (object header) df 03 00 f8 (11011111 00000011 00000000 11111000) (-134216737)
12 4 Constructor Class.cachedConstructor null
16 4 Class Class.newInstanceCallerCache null
20 4 String Class.name (object)
24 4 (alignment/padding gap) N/A
28 4 SoftReference Class.reflectionData (object)
32 4 ClassRepository Class.genericInfo null
36 4 Object[] Class.enumConstants null
40 4 Map Class.enumConstantDirectory null
44 4 AnnotationData Class.annotationData (object)
48 4 AnnotationType Class.annotationType null
52 4 ClassValueMap Class.classValueMap null
56 32 (alignment/padding gap) N/A
88 4 int Class.classRedefinedCount 0
92 556 (loss due to the next object alignment)
实例大小:648 字节 Space 损失:内部 36 字节 + 外部 556 字节 = 总共 592 字节
或总共 space 丢失 556 字节;我发现这令人印象深刻。