RFR: 8205683: Refactor heap allocation to separate concerns

Erik Österlund erik.osterlund at oracle.com
Wed Jun 27 13:23:04 UTC 2018


Hi Per,

Thank you for reviewing.

Incremental:
http://cr.openjdk.java.net/~eosterlund/8205683/webrev.02_03/

Full:
http://cr.openjdk.java.net/~eosterlund/8205683/webrev.03/

On 2018-06-27 12:43, Per Liden wrote:
> Hi Erik,
>
> On 06/26/2018 05:13 PM, Erik Österlund wrote:
>> Hi,
>>
>> After a bunch of stuff was added to the CollectedHeap object 
>> allocation path, it is starting to look quite ugly. It has function 
>> pointers passed around with arguments to be sent to them, mixing up 
>> allocation, initialization, tracing, sampling and exception handling 
>> all over the place.
>>
>> I propose to improve the situation by having an abstract MemAllocator 
>> stack object own the allocation path, with suitable subclasses, 
>> ObjAllocator, ClassAllocator and ObjArrayAllocator for allocating the 
>> 3 types we support allocating on the heap. Variation points now 
>> simply use virtual calls, and most of the tracing/sampling code can 
>> be moved out from the allocation path. This should make it easier to 
>> understand what is going on here, and the different concerns are more 
>> separated.
>>
>> A collector can override the virtual member functions for allocating 
>> objects, arrays, and classes, and inject their own variation points 
>> into this framework. For example for installing a forwarding pointer 
>> in the cell header required by Brooks pointers, if you are into that 
>> kind of stuff.
>>
>> Bug:
>> https://bugs.openjdk.java.net/browse/JDK-8205683
>>
>> Webrev:
>> http://cr.openjdk.java.net/~eosterlund/8205683/webrev.02/
>
> Awesome cleanup Erik! A few comments/questions/suggestions:
>
> src/hotspot/share/gc/shared/collectedHeap.cpp
> ---------------------------------------------
>
> 449 oop CollectedHeap::obj_allocate(Klass* klass, int size, TRAPS) {
> 450   ObjAllocator allocator(klass, size);
> 451   return allocator.allocate();
> 452 }
> 453
> 454 oop CollectedHeap::array_allocate(Klass* klass, int size, int 
> length, bool do_zero, TRAPS) {
> 455   ObjArrayAllocator allocator(klass, size, length, do_zero);
> 456   return allocator.allocate();
> 457 }
> 458
> 459 oop CollectedHeap::class_allocate(Klass* klass, int size, TRAPS) {
> 460   ClassAllocator allocator(klass, size);
> 461   return allocator.allocate();
> 462 }
>
> I doesn't look like the above functions need to take in TRAPS, right?

It is used by callers so they can use the CHECK_NULL macro on the 
callsite. But inside, I don't need it.

> src/hotspot/share/gc/shared/memAllocator.hpp
> --------------------------------------------
>
> 47   HeapWord* allocate_from_tlab(Allocation& allocation) const;
> 48   HeapWord* allocate_from_tlab_slow(Allocation& allocation) const;
> 49   HeapWord* allocate_outside_tlab(Allocation& allocation) const;
>
> Can we call these allocate_inside_tlab/allocate_outside_tlab?

Fixed.

>  73 virtual oop init_obj(HeapWord* mem) const;
>
> Can we call this initialize() instead? To better line up with allocate().

Fixed.

> Further, I'd also like to see initialize() being a pure virtual 
> function and instead do what that function does today in a different 
> function, protected and non-virtual, called finish(). So in the end 
> we'll have:
>
> allocate();
> initialize();
> finish();
>
> I believe such naming/structure would make code like this easier to 
> understand:
> src/hotspot/share/gc/shared/memAllocator.cpp:
> 415   mem_clear(mem);
> 416   java_lang_Class::set_oop_size(mem, (int)_word_size);
> 417   oop obj = finish(mem);
>
> Otherwise, calling init_obj()/initialize() as the last thing here 
> seems a bit odd and suggests the we're re-initializing the object 
> completely.

Fixed.

> src/hotspot/share/gc/shared/memAllocator.cpp
> --------------------------------------------
> ...
> 391   oop obj = MemAllocator::init_obj(mem);
> 392   assert(Universe::is_bootstrapping() || !obj->is_array(), "must 
> not be an array");
> 393   return obj;
> 394 }
> ...
> 405   oop obj = MemAllocator::init_obj(mem);
> 406   assert(obj->is_array(), "must be an array");
> 407   return obj;
> 408 }
> ...
> 417   oop obj = MemAllocator::init_obj(mem);
> 418   assert(Universe::is_bootstrapping() || !obj->is_array(), "must 
> not be an array");
> 419   return obj;
> 420 }
>
> The above asserts look unnecessary. If so, it would be nice to just 
> convert this to:
>
> ...
> 391   return finish(mem);
> 392 }
> ...
> 405   return finish(mem);
> 406 }
> ...
> 417   return finish(mem);
> 418 }

Fixed.

> 200     HeapWord* mem = (HeapWord*)obj;
> 201     size_t size_in_bytes = _allocator._word_size * HeapWordSize;
> 202     ThreadLocalAllocBuffer& tlab = _thread->tlab();
> 203     size_t bytes_since_last = _allocated_outside_tlab ? 0 : 
> tlab.bytes_since_last_sample_point();
> 204     _thread->heap_sampler().check_for_sampling(mem, size_in_bytes, 
> bytes_since_last);
>
> Looks like we can now change the first arg in check_for_sampling() 
> from HeapWord* to oop and avoid the casting above and inside 
> check_for_sampling() itself.

Fixed.

> src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp
> ------------------------------------------------------
>
> 143   HeapWord* allocate_sampled_object(size_t size);
>
> Can we call this function allocate_sampled() to better match with 
> allocate()?

Not sure where you found this function; I have removed it. The logic was 
instead moved into the allocate_inside_tlab_slow function. One 
alternative I considered is to pass in a bool* to 
ThreadLocalAllocBuffer::allocate() that if not NULL also tries to bump 
the end to its actual end, and returns back whether the end was bumped 
by the sampling logic or not inside of allocate, and have that pointer 
point straight into Allocation::_tlab_end_reset_for_sample. If you 
prefer that style, I can change it to do that instead.

Thanks,
/Erik

>
> cheers,
> Per




More information about the hotspot-gc-dev mailing list