During fixing of related problems I noticed that it is possible that when trying to allocate a new PLAB for a given allocation, the allocator algorithm may return a buffer that is too small for the object, resulting in premature evacuation failure.
The problem occurs because when allocating a new PLAB, not all memory is actually usable for allocation within the PLAB, but only the buffer size minus an alignment reserve.
This situation is asserted, so it seems to occur rarely (and is ignored in product mode).
Here is some commented code.
HeapWord* G1ParGCAllocator::allocate_slow(GCAllocPurpose purpose, size_t word_sz, AllocationContext_t context) {
HeapWord* obj = NULL;
size_t gclab_word_size = _g1h->desired_plab_sz(purpose);
if (word_sz * 100 < gclab_word_size * ParallelGCBufferWastePct) {
G1ParGCAllocBuffer* alloc_buf = alloc_buffer(purpose, context);
add_to_alloc_buffer_waste(alloc_buf->words_remaining());
alloc_buf->retire(false /* end_of_gc */, false /* retain */);
HeapWord* buf = _g1h->par_allocate_during_gc(purpose, gclab_word_size, context);
^^---- allocate a buffer of gclab_word_size
if (buf == NULL) {
return NULL; // Let caller handle allocation failure.
}
// Otherwise.
alloc_buf->set_word_size(gclab_word_size);
alloc_buf->set_buf(buf);
obj = alloc_buf->allocate(word_sz);
^^---- try to allocate word_sz in the PLAB. Since there actually is only plab_word_sz - ParGCAllocBuffer::AlignmentReserve space for allocation, allocation may actually fail.
assert(obj != NULL, "buffer was definitely big enough...");
} else {
obj = _g1h->par_allocate_during_gc(purpose, word_sz, context);
}
return obj;
}