FreeBlocks is a wrapper around two allocators:
- a binlist for small-block O(1) allocation
- a block tree for larger blocks (O(log n)).
If caller wants a block of size N, FreeBlocks asks either its BinList or its BlockTree, depending on size N.
However, if we have large blocks, but no small blocks, asking for small blocks will come back empty. What should happen instead: FreeBlocks should break a piece off one of its large blocks.
This is an old bug (I think it even predates elastic Metaspace). This is very easy to fix, but such a change needs to be thoroughly tested since changing this code in the manner described above will put more pressure on the blocktree, and may shake loose hidden bugs or performance problems.
Repro gtest (note: atop of Lilliput, uses MetaBlock):
```
TEST_VM(metaspace, freeblocks_repro_8340455) {
FreeBlocks fbl;
CHECK_CONTENT(fbl, 0, 0);
MetaWord tmp[K];
MetaBlock b(tmp, K);
add_one_block_and_test(fbl, b); // add one block of 1024 words
MetaBlock b2 = remove_one_block_and_test(fbl, 1); // now ask for 1 word
ASSERT_FALSE(b2.is_empty()); // here it fails
ASSERT_EQ(b2, b);
}
```
- a binlist for small-block O(1) allocation
- a block tree for larger blocks (O(log n)).
If caller wants a block of size N, FreeBlocks asks either its BinList or its BlockTree, depending on size N.
However, if we have large blocks, but no small blocks, asking for small blocks will come back empty. What should happen instead: FreeBlocks should break a piece off one of its large blocks.
This is an old bug (I think it even predates elastic Metaspace). This is very easy to fix, but such a change needs to be thoroughly tested since changing this code in the manner described above will put more pressure on the blocktree, and may shake loose hidden bugs or performance problems.
Repro gtest (note: atop of Lilliput, uses MetaBlock):
```
TEST_VM(metaspace, freeblocks_repro_8340455) {
FreeBlocks fbl;
CHECK_CONTENT(fbl, 0, 0);
MetaWord tmp[K];
MetaBlock b(tmp, K);
add_one_block_and_test(fbl, b); // add one block of 1024 words
MetaBlock b2 = remove_one_block_and_test(fbl, 1); // now ask for 1 word
ASSERT_FALSE(b2.is_empty()); // here it fails
ASSERT_EQ(b2, b);
}
```