RFC: TLAB size flapping
Aleksey Shipilev
shade at redhat.com
Tue Dec 6 17:17:11 UTC 2016
Hi,
So, if you run allocation tests under -Xlog:gc+tlab, then a funny story unfolds.
The interesting piece of code is below, it is polled by TLAB allocation
machinery to figure what is the max TLAB allocatable without hassle:
size_t ShenandoahHeap::unsafe_max_tlab_alloc(Thread *thread) const {
size_t idx = _free_regions->current_index();
ShenandoahHeapRegion* current = _free_regions->get(idx);
if (current == NULL) {
return 0;
} else if (current->free() > MinTLABSize) {
return current->free();
} else {
return MinTLABSize;
}
}
This what happens next:
// Step 1: TLAB request for allocating, polling Shenandoah about the next free
// region. Shenandoah replies there is a current free region with 256 words
// busy (hm!). Okay, we claim the rest of the region for a TLAB then.
[2.328s][trace][gc,tlab] TLAB: fill thread: 0x00007ffb54594800 ...
[2.328s][trace][gc,tlab] ShenandoahHeap::unsafe_max_tlab_alloc: region = 1019,
capacity = 524288, used = 256, free = 524032
[2.328s][trace][gc,tlab] ThreadLocalAllocBuffer::compute_size(3) returns 524032
[2.328s][trace][gc,tlab] allocating new tlab of size 524032 at addr
0x00000006bec00800
// Step 2: Another TLAB request. No more space in current region. But yeah, we
// return MinTLABSize (those 256 words!), and shared infra moves on, asking us
// to allocate a new TLAB of 256 words. Now, the current region is depleted, so
// we allocate those 256 words in the *next* region.
[2.328s][trace][gc,tlab] TLAB: fill thread: 0x00007ffb54594800 ...
[2.329s][trace][gc,tlab] ShenandoahHeap::unsafe_max_tlab_alloc: (failing) region
= 1019, capacity = 524288, used = 524288, free = 0
[2.329s][trace][gc,tlab] ThreadLocalAllocBuffer::compute_size(3) returns 256
[2.329s][trace][gc,tlab] allocating new tlab of size 256 at addr 0x00000006bf000000
// Step 1 again. The cycle continues. Another TLAB request, current region has
// 256 words used, claim the rest... goes on and on.
[2.329s][trace][gc,tlab] TLAB: fill thread: 0x00007ffb54594800 ...
[2.329s][trace][gc,tlab] ShenandoahHeap::unsafe_max_tlab_alloc: region = 1020,
capacity = 524288, used = 256, free = 524032
[2.329s][trace][gc,tlab] ThreadLocalAllocBuffer::compute_size(3) returns 524032
[2.329s][trace][gc,tlab] allocating new tlab of size 524032 at addr
0x00000006bf000800
So, this flaps TLAB allocations between the region size and MinTLABSize. Oops!
We enter the slow path *twice* per region, instead of doing it once. I think
returning MinTLABSize is wrong in the code above, and we have two options:
a) Return 0 on MinTLABSize branch. If I read the code right, this will bail us
from TLAB allocation path, which is undesireable;
b) Advance to the next free region, and try to poll its free().
G1 is susceptible to the same problem, as far as I can see.
Thanks,
-Aleksey
More information about the shenandoah-dev
mailing list