neo4j/neo4j — pull request #3726 by chrisvest (2014-12-23 17:45:59+00:00)
Title: Reduce the space overhead of MuninnPage objects

These changes reduce the overhead of the Muninn page cache by about 10% in runtime images where oops are 4 bytes, by reducing the instance size of MuninnPage objects from 72 bytes to 64 bytes, with a 2 byte allignment gap.
As part of these changes, MuninnPage objects no longer know their index into the MuninnPageCache.pages array.
This means that the cachePageIds reported through the PageCacheMonitor APIs are now based in the identityHashCode of the MuninnPage objects.
To ensure that the identityHashCodes have a very high chance of being unique amongst the objects, I have changed the default hashCode implementation by setting -XX:hashCode=5 on the JVM.
This implementation of hashCode (used for both System.identityHashCode and Object.hashCode) uses a per-thread XorShift RNG, seeded from a JVM-global racy Park-Miller RNG.
The default is otherwise to use the JVM-global Park-Miller RNG in an unsynchronised way, which can lead to multiple objects getting the same hashCodes via race.
With the changed -XX:hashCode setting, this race only exist on thread initialization, wherein it can lead to threads producing the same sequences of hashCodes.
This is not a problem in our case, because all the MuninnPage instances are allocated by the same thread, and they have been made to initialise their identityHashCode on allocation.
As such, we should not see two MuninnPage instances in a cache with the same hashCode, as long as there are fewer than 2^31 pages in the cache.
See `get_next_hashcode` in `synchronizer.cpp` in the OpenJDK hotspot source code for more information on the various hashCode implementation variants.

Another important change is that the cachePageSizes are now required to be a power of two.
A number of tests had to be updated to live up to this requirement.

The performance impact of the extra work on page bounds checking appear to be within error on the measurements I've done.
There is a slight performance during startup, because the construction of MuninnPage instances now incur the extra overhead of computing the identityHashCode, which itself includes a LOCK:CMPXCHG instruction for installing the computed has into the object header.
There is also a slight performance overhead added to the MemoryReleaser, as it now has to use an AtomicInteger to thread-safely find an array slot to install a given native pointer into.
These overheads only exist when the cache is new and still warming up, so I don't think it's a problem.
The PageCacheTests are only slowed down by a second or two.