为什么Java对象中存在内部碎片,即使每个字段都是4字节对齐的?

介绍:

我使用JOL(Java对象布局)工具来分析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源描述 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 (eg, // metaspace holding its klass definitions). // The System Dictionary and related data structures (eg, 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(). 

你给了我一个绝佳的机会发布我看到过有趣的东西(这应该是一个评论,但它太长了):

  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字节空间丢失:内部36字节+外部556字节=总共592字节

或总空间损失556字节 ; 我觉得这很令人印象深刻。

Interesting Posts