JDK-6858243 : CMS: Fencing needed for hw with weak memory model
Type:Bug
Component:hotspot
Sub-Component:gc
Affected Version:hs16,1.4.2,1.4.2_12
Priority:P4
Status:Closed
Resolution:Duplicate
OS:generic
CPU:generic
Submitted:2009-07-08
Updated:2016-04-25
Resolved:2010-08-19
The Version table provides details related to the release that this issue/RFE will be addressed.
Unresolved : Release in which this issue/RFE will be addressed. Resolved: Release in which this issue/RFE has been resolved. Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.
SUGGESTED FIX
hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp:
1305: | assert(promoInfo->has_spooling_space(), "Control point invariant");
| HeapWord* obj_ptr = ps->lab.alloc(word_sz);
| if (obj_ptr == NULL) {
| obj_ptr = expand_and_par_lab_allocate(ps, word_sz);
| if (obj_ptr == NULL) {
1310: | return NULL;
| }
| }
+ | OrderAccess::release();
| oop obj = oop(obj_ptr);
| assert(obj->klass() == NULL, "Object should be uninitialized here.");
1315: | // Otherwise, copy the object. Here we must be careful to insert the
| // klass pointer last, since this marks the block as an allocated object.
| // Except with compressed oops it's the mark word.
| HeapWord* old_ptr = (HeapWord*)old;
| if (word_sz > (size_t)oopDesc::header_size()) {
1320: | Copy::aligned_disjoint_words(old_ptr + oopDesc::header_size(),
| obj_ptr + oopDesc::header_size(),
| word_sz - oopDesc::header_size());
+ | OrderAccess::release();
| }
|
1325: | if (UseCompressedOops) {
| // Copy gap missed by (aligned) header size calculation above
|
obj->set_klass_gap(old->klass_gap());
|
}
|
1330: | // Restore the mark word copied above.
| obj->set_mark(m);
|
| // Now we can track the promoted object, if necessary. We take care
| // To delay the transition from uninitialized to full object
1335: | // (i.e., insertion of klass pointer) until after, so that it
| // atomically becomes a promoted object.
| if (promoInfo->tracking()) {
| promoInfo->track((PromotedObject*)obj, old->klass());
| }
hotspot/src/share/vm/gc_implementation/concurrentMarkSweep/compactibleFreeListSpace.cpp:
|size_t CompactibleFreeListSpace::block_size(const HeapWord* p) const {
| NOT_PRODUCT(verify_objects_initialized());
| assert(MemRegion(bottom(), end()).contains(p), "p not in space");
805: | // This must be volatile, or else there is a danger that the compiler
|
// will compile the code below into a sometimes-infinite loop, by keeping
| // the value read the first time in a register.
| while (true) {
| // We must do this until we get a consistent view of the object.
810: |
if (FreeChunk::indicatesFreeChunk(p)) {
|
volatile FreeChunk* fc = (volatile FreeChunk*)p;
|
size_t res = fc->size();
+ | OrderAccess::acquire();
| // If the object is still a free chunk, return the size, else it
|
// has been allocated so try again.
815: |
if (FreeChunk::indicatesFreeChunk(p)) {
| assert(res != 0, "Block size should not be 0");
|
return res;
|
}
|
} else {
820: |
// must read from what 'p' points to in each loop.
|
klassOop k = ((volatile oopDesc*)p)->klass_or_null();
|
if (k != NULL) {
|
assert(k->is_oop(true /* ignore mark word */), "Should really be klass oop.");
|
oop o = (oop)p;
825: | assert(o->is_parsable(), "Should be parsable");
|
assert(o->is_oop(true /* ignore mark word */), "Should be an oop.");
+ | OrderAccess::acquire();
|
size_t res = o->size_given_klass(k->klass_part());
|
res = adjustObjectSize(res);
|
assert(res != 0, "Block size should not be 0");
830: |
return res;
|
}
| }
|
}
|
}
...
| if (res != NULL) {
| splitBirth(size);
| repairLinearAllocBlock(blk);
| } else if (blk->_ptr != NULL) {
1300: | res = blk->_ptr;
| size_t blk_size = blk->_word_size;
| blk->_word_size -= size;
| blk->_ptr += size;
| splitBirth(size);
1305: | repairLinearAllocBlock(blk);
+ | OrderAccess::release();
| // Update BOT last so that other (parallel) GC threads see a consistent
| // view of the BOT and free blocks.
| // Above must occur before BOT is updated below.
| _bt.split_block(res, blk_size, size); // adjust block offset table
1310: | }
| return res;
|}
|
|HeapWord* CompactibleFreeListSpace::getChunkFromLinearAllocBlockRemainder(
1315: | LinearAllocBlock* blk,
| size_t size) {
| assert_locked();
| assert(size >= MinChunkSize, "too small");
|
1320: | HeapWord* res = NULL;
| // This is the common case. Keep it simple.
| if (blk->_word_size >= size + MinChunkSize) {
| assert(blk->_ptr != NULL, "consistency check");
| res = blk->_ptr;
1325: | // Note that the BOT is up-to-date for the linAB before allocation. It
| // indicates the start of the linAB. The split_block() updates the
| // BOT for the linAB after the allocation (indicates the start of the
| // next chunk to be allocated).
| size_t blk_size = blk->_word_size;
1330: | blk->_word_size -= size;
| blk->_ptr += size;
| splitBirth(size);
| repairLinearAllocBlock(blk);
+ | OrderAccess::release();
| // Update BOT last so that other (parallel) GC threads see a consistent
1335: | // view of the BOT and free blocks.
| // Above must occur before BOT is updated below.
| _bt.split_block(res, blk_size, size); // adjust block offset table
| _bt.allocated(res, size);
| }
1340: | return res;
|}
...
1675: |FreeChunk*
|
CompactibleFreeListSpace::splitChunkAndReturnRemainder(FreeChunk* chunk,
|
size_t new_size) {
|
assert_locked();
|
size_t size = chunk->size();
1680: |
assert(size > new_size, "Split from a smaller block?");
| assert(is_aligned(chunk), "alignment problem");
| assert(size == adjustObjectSize(size), "alignment problem");
| size_t rem_size = size - new_size;
| assert(rem_size == adjustObjectSize(rem_size), "alignment problem");
1685: | assert(rem_size >= MinChunkSize, "Free chunk smaller than minimum");
| FreeChunk* ffc = (FreeChunk*)((HeapWord*)chunk + new_size);
| assert(is_aligned(ffc), "alignment problem");
| ffc->setSize(rem_size);
| ffc->linkNext(NULL);
1690: | ffc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads.
+ | OrderAccess::release();
| // Above must occur before BOT is updated below.
| // adjust block offset table
| _bt.split_block((HeapWord*)chunk, chunk->size(), new_size);
| if (rem_size < SmallForDictionary) {
1695: | bool is_par = (SharedHeap::heap()->n_par_threads() > 0);
| if (is_par) _indexedFreeListParLocks[rem_size]->lock();
| returnChunkToFreeList(ffc);
| split(size, rem_size);
| if (is_par) _indexedFreeListParLocks[rem_size]->unlock();
1700: | } else {
| returnChunkToDictionary(ffc);
|
split(size ,rem_size);
| }
|
chunk->setSize(new_size);
1705: |
return chunk;
|}
...
2650: | while ((fc = fl_for_cur_sz.getChunkAtHead()) != NULL) {
| // Must do this in reverse order, so that anybody attempting to
| // access the main chunk sees it as a single free block until we
| // change it.
| size_t fc_size = fc->size();
2655: | for (int i = k-1; i >= 0; i--) {
| FreeChunk* ffc = (FreeChunk*)((HeapWord*)fc + i * word_sz);
| ffc->setSize(word_sz);
| ffc->linkNext(NULL);
| ffc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads.
+ | OrderAccess::release();
2660: | // Above must occur before BOT is updated below.
| // splitting from the right, fc_size == (k - i + 1) * wordsize
| _bt.mark_block((HeapWord*)ffc, word_sz);
| fc_size -= word_sz;
| _bt.verify_not_unallocated((HeapWord*)ffc, ffc->size());
2665: | _bt.verify_single_block((HeapWord*)fc, fc_size);
| _bt.verify_single_block((HeapWord*)ffc, ffc->size());
| // Push this on "fl".
| fl->returnChunkAtHead(ffc);
| }
...
| // Note that we hold the lock until we decide if we're going to give
2710: | // back the remainder to the dictionary, since a contending allocator
| // may otherwise see the heap as empty. (We're willing to take that
| // hit if the block is a small block.)
| if (rem > 0) {
| size_t prefix_size = n * word_sz;
2715: | rem_fc = (FreeChunk*)((HeapWord*)fc + prefix_size);
| rem_fc->setSize(rem);
| rem_fc->linkNext(NULL);
| rem_fc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads.
+ | OrderAccess::release();
| // Above must occur before BOT is updated below.
2720: | _bt.split_block((HeapWord*)fc, fc->size(), prefix_size);
| if (rem >= IndexSetSize) {
| returnChunkToDictionary(rem_fc);
| dictionary()->dictCensusUpdate(fc->size(),
| true /*split*/,
2725: | true /*birth*/);
| rem_fc = NULL;
| }
...
2740: | // Now do the splitting up.
| // Must do this in reverse order, so that anybody attempting to
| // access the main chunk sees it as a single free block until we
| // change it.
| size_t fc_size = n * word_sz;
2745: | // All but first chunk in this loop
| for (ssize_t i = n-1; i > 0; i--) {
| FreeChunk* ffc = (FreeChunk*)((HeapWord*)fc + i * word_sz);
| ffc->setSize(word_sz);
| ffc->linkNext(NULL);
2750: | ffc->linkPrev(NULL); // Mark as a free block for other (parallel) GC threads.
+ | OrderAccess::release();
| // Above must occur before BOT is updated below.
| // splitting from the right, fc_size == (n - i + 1) * wordsize
| _bt.mark_block((HeapWord*)ffc, word_sz);
| fc_size -= word_sz;
2755: | _bt.verify_not_unallocated((HeapWord*)ffc, ffc->size());
| _bt.verify_single_block((HeapWord*)ffc, ffc->size());
| _bt.verify_single_block((HeapWord*)fc, fc_size);
| // Push this on "fl".
| fl->returnChunkAtHead(ffc);
2760: | }