JDK-6964458 introduced a special case for java.lang.Class instances in JvmtiEnv::GetObjectSize:
JvmtiEnv::GetObjectSize(jobject object, jlong* size_ptr) {
oop mirror = JNIHandles::resolve_external_guard(object);
NULL_CHECK(mirror, JVMTI_ERROR_INVALID_OBJECT);
if (mirror->klass() == SystemDictionary::Class_klass() &&
!java_lang_Class::is_primitive(mirror)) {
Klass* k = java_lang_Class::as_Klass(mirror);
assert(k != NULL, "class for non-primitive mirror must exist");
*size_ptr = (jlong)k->size() * wordSize;
} else {
*size_ptr = (jlong)mirror->size() * wordSize;
}
return JVMTI_ERROR_NONE;
} /* end GetObjectSize */
This, however, is incorrect: the instance size of the java.lang.Class instance itself does not depend on Klass::size().
JOL actually detects overlaps because of that:
$ java -jar jol-class.jar
WARNING: Unable to attach Serviceability Agent. You can try again with super-user privileges. Use -Djol.tryWithSudo=true to try with sudo.
WARNING: VM details, e.g. object alignment, reference size, compressed references info will be guessed.
Running 64-bit HotSpot VM.
Using compressed oop with 3-bit shift.
Using compressed klass with 3-bit shift.
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]
java.lang.Class object externals:
ADDRESS SIZE TYPE PATH VALUE
7192c0728 504 java.lang.Class (object)
Take 1
Main$A object externals:
ADDRESS SIZE TYPE PATH VALUE
5c9e17308 504 java.lang.Class .c (object)
5c9e17500 -400 **** OVERLAP **** **** OVERLAP **** **** OVERLAP ****
5c9e17370 496 java.lang.Class .b (object)
5c9e17560 5616077472 (something else) (somewhere else) (something else)
718a00000 24 Main$A (object)
Notice how the instance of B.class "overlaps" with the instance of C.class -- that's because Instrumentation.getObjectSize lied the instance size.
Notice also the instance size for B.class and C.class is different: 496 and 504 bytes, because class C has additional method.