8276055: ZGC: Defragment address space

Reviewed-by: eosterlund, stefank
This commit is contained in:
Per Liden 2021-10-28 05:44:32 +00:00
parent d9b0138d7d
commit 1750a6e2c0
8 changed files with 80 additions and 17 deletions

View File

@ -85,7 +85,19 @@ void ZMemoryManager::register_callbacks(const Callbacks& callbacks) {
_callbacks = callbacks;
}
uintptr_t ZMemoryManager::alloc_from_front(size_t size) {
uintptr_t ZMemoryManager::peek_low_address() const {
ZLocker<ZLock> locker(&_lock);
const ZMemory* const area = _freelist.first();
if (area != NULL) {
return area->start();
}
// Out of memory
return UINTPTR_MAX;
}
uintptr_t ZMemoryManager::alloc_low_address(size_t size) {
ZLocker<ZLock> locker(&_lock);
ZListIterator<ZMemory> iter(&_freelist);
@ -110,7 +122,7 @@ uintptr_t ZMemoryManager::alloc_from_front(size_t size) {
return UINTPTR_MAX;
}
uintptr_t ZMemoryManager::alloc_from_front_at_most(size_t size, size_t* allocated) {
uintptr_t ZMemoryManager::alloc_low_address_at_most(size_t size, size_t* allocated) {
ZLocker<ZLock> locker(&_lock);
ZMemory* area = _freelist.first();
@ -136,7 +148,7 @@ uintptr_t ZMemoryManager::alloc_from_front_at_most(size_t size, size_t* allocate
return UINTPTR_MAX;
}
uintptr_t ZMemoryManager::alloc_from_back(size_t size) {
uintptr_t ZMemoryManager::alloc_high_address(size_t size) {
ZLocker<ZLock> locker(&_lock);
ZListReverseIterator<ZMemory> iter(&_freelist);
@ -160,7 +172,7 @@ uintptr_t ZMemoryManager::alloc_from_back(size_t size) {
return UINTPTR_MAX;
}
uintptr_t ZMemoryManager::alloc_from_back_at_most(size_t size, size_t* allocated) {
uintptr_t ZMemoryManager::alloc_high_address_at_most(size_t size, size_t* allocated) {
ZLocker<ZLock> locker(&_lock);
ZMemory* area = _freelist.last();

View File

@ -66,7 +66,7 @@ public:
};
private:
ZLock _lock;
mutable ZLock _lock;
ZList<ZMemory> _freelist;
Callbacks _callbacks;
@ -82,10 +82,11 @@ public:
void register_callbacks(const Callbacks& callbacks);
uintptr_t alloc_from_front(size_t size);
uintptr_t alloc_from_front_at_most(size_t size, size_t* allocated);
uintptr_t alloc_from_back(size_t size);
uintptr_t alloc_from_back_at_most(size_t size, size_t* allocated);
uintptr_t peek_low_address() const;
uintptr_t alloc_low_address(size_t size);
uintptr_t alloc_low_address_at_most(size_t size, size_t* allocated);
uintptr_t alloc_high_address(size_t size);
uintptr_t alloc_high_address_at_most(size_t size, size_t* allocated);
void free(uintptr_t start, size_t size);
};

View File

@ -48,6 +48,7 @@
static const ZStatCounter ZCounterAllocationRate("Memory", "Allocation Rate", ZStatUnitBytesPerSecond);
static const ZStatCounter ZCounterPageCacheFlush("Memory", "Page Cache Flush", ZStatUnitBytesPerSecond);
static const ZStatCounter ZCounterDefragment("Memory", "Defragment", ZStatUnitOpsPerSecond);
static const ZStatCriticalPhase ZCriticalPhaseAllocationStall("Allocation Stall");
enum ZPageAllocationStall {
@ -559,12 +560,43 @@ ZPage* ZPageAllocator::alloc_page_create(ZPageAllocation* allocation) {
return new ZPage(allocation->type(), vmem, pmem);
}
static bool is_alloc_satisfied(ZPageAllocation* allocation) {
bool ZPageAllocator::should_defragment(const ZPage* page) const {
// A small page can end up at a high address (second half of the address space)
// if we've split a larger page or we have a constrained address space. To help
// fight address space fragmentation we remap such pages to a lower address, if
// a lower address is available.
return page->type() == ZPageTypeSmall &&
page->start() >= _virtual.reserved() / 2 &&
page->start() > _virtual.lowest_available_address();
}
bool ZPageAllocator::is_alloc_satisfied(ZPageAllocation* allocation) const {
// The allocation is immediately satisfied if the list of pages contains
// exactly one page, with the type and size that was requested.
return allocation->pages()->size() == 1 &&
allocation->pages()->first()->type() == allocation->type() &&
allocation->pages()->first()->size() == allocation->size();
// exactly one page, with the type and size that was requested. However,
// even if the allocation is immediately satisfied we might still want to
// return false here to force the page to be remapped to fight address
// space fragmentation.
if (allocation->pages()->size() != 1) {
// Not a single page
return false;
}
const ZPage* const page = allocation->pages()->first();
if (page->type() != allocation->type() ||
page->size() != allocation->size()) {
// Wrong type or size
return false;
}
if (should_defragment(page)) {
// Defragment address space
ZStatInc(ZCounterDefragment);
return false;
}
// Allocation immediately satisfied
return true;
}
ZPage* ZPageAllocator::alloc_page_finalize(ZPageAllocation* allocation) {

View File

@ -89,6 +89,8 @@ private:
bool alloc_page_common(ZPageAllocation* allocation);
bool alloc_page_stall(ZPageAllocation* allocation);
bool alloc_page_or_stall(ZPageAllocation* allocation);
bool should_defragment(const ZPage* page) const;
bool is_alloc_satisfied(ZPageAllocation* allocation) const;
ZPage* alloc_page_create(ZPageAllocation* allocation);
ZPage* alloc_page_finalize(ZPageAllocation* allocation);
void alloc_page_failed(ZPageAllocation* allocation);

View File

@ -295,7 +295,7 @@ void ZPhysicalMemoryManager::alloc(ZPhysicalMemory& pmem, size_t size) {
// Allocate segments
while (size > 0) {
size_t allocated = 0;
const uintptr_t start = _manager.alloc_from_front_at_most(size, &allocated);
const uintptr_t start = _manager.alloc_low_address_at_most(size, &allocated);
assert(start != UINTPTR_MAX, "Allocation should never fail");
pmem.add_segment(ZPhysicalMemorySegment(start, allocated, false /* committed */));
size -= allocated;

View File

@ -33,6 +33,7 @@
ZVirtualMemoryManager::ZVirtualMemoryManager(size_t max_capacity) :
_manager(),
_reserved(0),
_initialized(false) {
// Check max supported heap size
@ -173,6 +174,9 @@ bool ZVirtualMemoryManager::reserve(size_t max_capacity) {
log_info_p(gc, init)("Address Space Size: " SIZE_FORMAT "M x " SIZE_FORMAT " = " SIZE_FORMAT "M",
reserved / M, ZHeapViews, (reserved * ZHeapViews) / M);
// Record reserved
_reserved = reserved;
return reserved >= max_capacity;
}
@ -191,9 +195,9 @@ ZVirtualMemory ZVirtualMemoryManager::alloc(size_t size, bool force_low_address)
// Small pages are allocated at low addresses, while medium/large pages
// are allocated at high addresses (unless forced to be at a low address).
if (force_low_address || size <= ZPageSizeSmall) {
start = _manager.alloc_from_front(size);
start = _manager.alloc_low_address(size);
} else {
start = _manager.alloc_from_back(size);
start = _manager.alloc_high_address(size);
}
return ZVirtualMemory(start, size);

View File

@ -48,6 +48,7 @@ public:
class ZVirtualMemoryManager {
private:
ZMemoryManager _manager;
uintptr_t _reserved;
bool _initialized;
// Platform specific implementation
@ -69,6 +70,9 @@ public:
bool is_initialized() const;
size_t reserved() const;
uintptr_t lowest_available_address() const;
ZVirtualMemory alloc(size_t size, bool force_low_address);
void free(const ZVirtualMemory& vmem);
};

View File

@ -57,4 +57,12 @@ inline ZVirtualMemory ZVirtualMemory::split(size_t size) {
return ZVirtualMemory(_start - size, size);
}
inline size_t ZVirtualMemoryManager::reserved() const {
return _reserved;
}
inline uintptr_t ZVirtualMemoryManager::lowest_available_address() const {
return _manager.peek_low_address();
}
#endif // SHARE_GC_Z_ZVIRTUALMEMORY_INLINE_HPP