SHOW ME YOUR ID CARD

As I mentioned at my previous article, in Java, every object has a header that holds some metadata about object itself and type.

Every object (except array) in memory has 2 machine word header. The first one is called mark word. Mark word stores identity hashcode, age, bits used for garbage collection, bits used for locking. To find out more check out the source from OpenJDK.

Lets have a look at the details of object header to refresh our memory since previous article.

 

32 bit JVM:

|----------------------------------------------------------------------------------------|--------------------|
|                                    Object Header (64 bits)                             |        State       |
|-------------------------------------------------------|--------------------------------|--------------------|
|                  Mark Word (32 bits)                  |      Klass Word (32 bits)      |                    |
|-------------------------------------------------------|--------------------------------|--------------------|
| identity_hashcode:25 | age:4 | biased_lock:1 | lock:2 |      OOP to metadata object    |       Normal       |
|-------------------------------------------------------|--------------------------------|--------------------|
|  thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:2 |      OOP to metadata object    |       Biased       |
|-------------------------------------------------------|--------------------------------|--------------------|
|               ptr_to_lock_record:30          | lock:2 |      OOP to metadata object    | Lightweight Locked |
|-------------------------------------------------------|--------------------------------|--------------------|
|               ptr_to_heavyweight_monitor:30  | lock:2 |      OOP to metadata object    | Heavyweight Locked |
|-------------------------------------------------------|--------------------------------|--------------------|
|                                              | lock:2 |      OOP to metadata object    |    Marked for GC   |
|-------------------------------------------------------|--------------------------------|--------------------|

64 bit JVM:

|------------------------------------------------------------------------------------------------------------|--------------------|
|                                            Object Header (128 bits)                                        |        State       |
|------------------------------------------------------------------------------|-----------------------------|--------------------|
|                                  Mark Word (64 bits)                         |    Klass Word (64 bits)     |                    |
|------------------------------------------------------------------------------|-----------------------------|--------------------|
| unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 |    OOP to metadata object   |       Normal       |
|------------------------------------------------------------------------------|-----------------------------|--------------------|
| thread:54 |       epoch:2        | unused:1 | age:4 | biased_lock:1 | lock:2 |    OOP to metadata object   |       Biased       |
|------------------------------------------------------------------------------|-----------------------------|--------------------|
|                       ptr_to_lock_record:62                         | lock:2 |    OOP to metadata object   | Lightweight Locked |
|------------------------------------------------------------------------------|-----------------------------|--------------------|
|                     ptr_to_heavyweight_monitor:62                   | lock:2 |    OOP to metadata object   | Heavyweight Locked |
|------------------------------------------------------------------------------|-----------------------------|--------------------|
|                                                                     | lock:2 |    OOP to metadata object   |    Marked for GC   |
|------------------------------------------------------------------------------|-----------------------------|--------------------|

 

Identity hashcode of the object which is initialized (written into the object header) when it is firstly accessed on demand. As seen from here, if identity hashcode is set, it just returns, otherwise calculation is forwarded toObjectSynchonizer and calculated by considering object’s locking state (biased locked, monitor locked, etc …) as implemented here: http://hg.openjdk.java.net/jdk8/jdk8/hotspot/file/87ee5ee27509/src/share/vm/runtime/synchronizer.cpp#l601

By default Object.hashCode() returns this value if it is not overriden. For getting the generated hashcode of object, System.identityHashCode(obj) is the best way since it directly gets this value from object header via native call so this means that it works also in case of overriden hashCode method. However, when object is locked, the identity hashcode value is moved into the monitor object.

In JVM, there are some different strategies (for production, for testing, etc …) for calculating identity hash code of an object as seen here. In Java 8, the default one is Marsaglia’s xor-shift scheme with thread-specific state as defined here. This approach on Java 8 is based on JVM native threads have thread-local variables (_hashStateX, _hashStateY, _hashStateZ, _hashStateW) to be used for hashcode generation as seen here. So this means that on Java 8, default identity hash code is independent from objects memory location.

You can access the code from here. Here is a code snippet for accessing object’s identity hash code from its header with direct memory access:

// -XX:-UseCompressedOops
public class ObjectIdentityHashCodeDemo {

	public static void main(String[] args) {
		Foo foo = new Foo();
		
		// "hashCode" field is empty and it is initialized when it is firstly accessed on demand
		foo.hashCode();
		
		if (JvmUtil.getReferenceSize() == 4) { // 32 bit JVM
			// | identity_hashcode:25 | age:4 | biased_lock:1 | lock:2 |
			long header = UNSAFE.getInt(foo, 0L);
			int identityHashCode = (int) (header >> 7);
			System.out.println(identityHashCode);
			
		} else { // 64 bit JVM
			// | unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 |
			long header = UNSAFE.getLong(foo, 0L);
			int identityHashCode = (int) ((header >> 8) & 0x7FFFFFFF);
			System.out.println(identityHashCode);
		}

		System.out.println(System.identityHashCode(foo));
	}
	
}

 

We have seen that object’s identity hashcode is generated by JVM if it is not initialized yet. So, what will happen if we clear object header and recall hashCode? Does it return old identity hashcode or generate a new one? Yes, it is initialized with new generated value by JVM since identity hashcode is only store at object header and when we clear object header, there is no way for getting old value by JVM and so it generates new one. So, we can use this behaviour for generating random numbers just for fun 🙂

You can access the code from here. Here is a code sample for showing how to use object header via identity hashcode for generating random numbers:

 

public class RandomNumberGeneratorWithObjectIdentityHashCodeDemo {

	private static final Unsafe UNSAFE = JvmUtil.getUnsafe();
	private static final int WARM_UP_RANDOM_NUMBER_COUNT = 1_000_000;
	private static final int RANDOM_NUMBER_COUNT = WARM_UP_RANDOM_NUMBER_COUNT * 100;
	private static final Random RANDOM = new Random();
	
	public static void main(String[] args) {
		generateNumbersWithRandom(WARM_UP_RANDOM_NUMBER_COUNT);
		generateNumbersWithObjectIdentityHashCode(WARM_UP_RANDOM_NUMBER_COUNT);
		
		long start = System.currentTimeMillis();
		generateNumbersWithRandom(RANDOM_NUMBER_COUNT);
		System.out.println("Generating random numbers with 'Random' finished in " + 
				(System.currentTimeMillis() - start) + " milliseconds ...");
		
		start = System.currentTimeMillis();
		generateNumbersWithObjectIdentityHashCode(RANDOM_NUMBER_COUNT);
		System.out.println("Generating random numbers by identity hashcode finished in " + 
				(System.currentTimeMillis() - start) + " milliseconds ...");
	}
	
	private static void generateNumbersWithRandom(int numberCount) {
		for (int i = 0; i < numberCount; i++) {
			RANDOM.nextInt();
		}
	}
	
	private static void generateNumbersWithObjectIdentityHashCode(int numberCount) {
		Foo foo = new Foo();
		if (JvmUtil.getReferenceSize() == 4) { // 32 bit JVM
			// | identity_hashcode:25 | age:4 | biased_lock:1 | lock:2 |
			for (int i = 0; i < numberCount; i++) {
				foo.hashCode();
				// Not set to 0x00. Because if the last 2 bits is set to zero, it means 
				// object is "Lightweight Locked", so the second call will wait forever.
				UNSAFE.putInt(foo, 0L, 0x01);
			}
		} else { // 64 bit JVM
			// | unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2 |
			for (int i = 0; i < numberCount; i++) {
				foo.hashCode();
				// Not set to 0x00. Because if the last 2 bits is set to zero, it means 
				// object is "Lightweight Locked", so the second call will wait forever.
				UNSAFE.putLong(foo, 0L, 0x01);
			}
		}
	}
}

 

As stated in demo code as comment, object header should not set to zero. Because, the last 2 bits of object header is used for representing locking state of object.

  • 0x00: Lightweight locked
  • 0x01: Unlocked or Biased (In this case, 3rd LSB is used to determine about if it Biased locked or not locked. If this bit is set to 1, this means that this object is Biased locked to specified thread at header. Otherwise, means that there is no lock with this object
  • 0x02: Heavyweight locked
  • 0x03: Marked for Garbage collection

So, if object header is set to zero, it means that this object is “Lightweight locked” (because the last 2 bit is zero) and the next call on this object forUNSAFE.putLong(foo, …) will wait forever since object header says that object is “Lightweight locked”, but in the header, there is no pointer to lock record. For more details about this subject, please look at this post.

By the way, I will look into details of these locking states at next article and but not diving into them at this post.

And this is the output:

 

Generating random numbers with 'Random' finished in 1394 milliseconds ...
Generating random numbers by resetting identity hashcode finished in 4915 milliseconds ...

 

As shown above, “java.util.Random has 4x performance than resetting and retrieving object’s identity hashcode as random number. However, there maybe some contention based performance issues at multithreaded environments for java.util.Randomusage. And as I stated before, in Java 8, default strategy of identity hashcode generation is independent from object’s memory location and specific to caller thread like java.util.concurrent.ThreadLocalRandom which comes with Java 7. Besides of this blog post’s subject, here is a nice article for comparing java.util.Random and java.util.concurrent.ThreadLocalRandom: http://java-performance.info/java-util-random-java-util-concurrent-threadlocalrandom-multithreaded-environments/

 

REFERENCES

Java Object Header: http://arturmkrtchyan.com/java-object-header

Java Objects Memory Structure: http://www.codeinstructions.com/2008/12/java-objects-memory-structure.html

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s