-
Bug
-
Resolution: Fixed
-
P4
-
16
-
b11
When we archive a class mirror we force computation of its identity hashcode, then copy the original oop over the freshly allocated archived_oop, then reset the markWord age bits (in case they had been set). This assumes that the oop markWord was in a neutral state: unlocked and not inflated. Being unlocked is not an issue as we can't be holding any locks when we do the dump. But it is possible that the oop has an inflated monitor associated with it. If we archive the oop with that markWord, when we use it from the archive and try to synchronize on it, the synchronization system will see the inflated value and interpret it as an ObjectMonitor address, when for the current VM it is a random memory address (as monitors are not archived).
This is not observed in practice for two reasons:
1. In a normal VM it is exceedingly unlikely that synchronization on a class mirror during a CDS dump will result in inflation. (I have a project JVM where I always inflate and so encountered this problem.)
2. Even if inflation does occur it is almost guaranteed that deflation will also occur. This seemed guaranteed with synchronous safepoint based monitor deflation, but with async monitor deflation that is no longer the case.
The fix is quite simple: we overwrite the markWord of the archived oop with a value computed from the "prototype" markWord and the original oop identity hashcode (which has to match the archived oop as we have archived hashtables).
! // we need to preserve the identity hashcode
! int hash_original = obj->identity_hash();
oop archived_oop = (oop)G1CollectedHeap::heap()->archive_mem_allocate(len);
if (archived_oop != NULL) {
Copy::aligned_disjoint_words(cast_from_oop<HeapWord*>(obj), cast_from_oop<HeapWord*>(archived_oop), len);
MetaspaceShared::relocate_klass_ptr(archived_oop);
+ // Reinitialize markword
+ archived_oop->set_mark_raw(markWord::prototype().copy_set_hash(hash_original));
+ assert(archived_oop->mark().is_unlocked(), "sanity");
+ int hash_archived = archived_oop->identity_hash();
+ assert(hash_original == hash_archived, "Different hash codes: original %x, archived %x", hash_original, hash_archived);
This is not observed in practice for two reasons:
1. In a normal VM it is exceedingly unlikely that synchronization on a class mirror during a CDS dump will result in inflation. (I have a project JVM where I always inflate and so encountered this problem.)
2. Even if inflation does occur it is almost guaranteed that deflation will also occur. This seemed guaranteed with synchronous safepoint based monitor deflation, but with async monitor deflation that is no longer the case.
The fix is quite simple: we overwrite the markWord of the archived oop with a value computed from the "prototype" markWord and the original oop identity hashcode (which has to match the archived oop as we have archived hashtables).
! // we need to preserve the identity hashcode
! int hash_original = obj->identity_hash();
oop archived_oop = (oop)G1CollectedHeap::heap()->archive_mem_allocate(len);
if (archived_oop != NULL) {
Copy::aligned_disjoint_words(cast_from_oop<HeapWord*>(obj), cast_from_oop<HeapWord*>(archived_oop), len);
MetaspaceShared::relocate_klass_ptr(archived_oop);
+ // Reinitialize markword
+ archived_oop->set_mark_raw(markWord::prototype().copy_set_hash(hash_original));
+ assert(archived_oop->mark().is_unlocked(), "sanity");
+ int hash_archived = archived_oop->identity_hash();
+ assert(hash_original == hash_archived, "Different hash codes: original %x, archived %x", hash_original, hash_archived);