8340426: ZGC: Move defragment out of the allocation path
Reviewed-by: aboldtch, jsikstro, eosterlund
This commit is contained in:
parent
a63ac5a699
commit
ec020f3fc9
@ -241,10 +241,10 @@ void ZHeap::undo_alloc_page(ZPage* page) {
|
|||||||
log_trace(gc)("Undo page allocation, thread: " PTR_FORMAT " (%s), page: " PTR_FORMAT ", size: " SIZE_FORMAT,
|
log_trace(gc)("Undo page allocation, thread: " PTR_FORMAT " (%s), page: " PTR_FORMAT ", size: " SIZE_FORMAT,
|
||||||
p2i(Thread::current()), ZUtils::thread_name(), p2i(page), page->size());
|
p2i(Thread::current()), ZUtils::thread_name(), p2i(page), page->size());
|
||||||
|
|
||||||
free_page(page);
|
free_page(page, false /* allow_defragment */);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZHeap::free_page(ZPage* page) {
|
void ZHeap::free_page(ZPage* page, bool allow_defragment) {
|
||||||
// Remove page table entry
|
// Remove page table entry
|
||||||
_page_table.remove(page);
|
_page_table.remove(page);
|
||||||
|
|
||||||
@ -253,7 +253,7 @@ void ZHeap::free_page(ZPage* page) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Free page
|
// Free page
|
||||||
_page_allocator.free_page(page);
|
_page_allocator.free_page(page, allow_defragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ZHeap::free_empty_pages(const ZArray<ZPage*>* pages) {
|
size_t ZHeap::free_empty_pages(const ZArray<ZPage*>* pages) {
|
||||||
|
@ -104,7 +104,7 @@ public:
|
|||||||
// Page allocation
|
// Page allocation
|
||||||
ZPage* alloc_page(ZPageType type, size_t size, ZAllocationFlags flags, ZPageAge age);
|
ZPage* alloc_page(ZPageType type, size_t size, ZAllocationFlags flags, ZPageAge age);
|
||||||
void undo_alloc_page(ZPage* page);
|
void undo_alloc_page(ZPage* page);
|
||||||
void free_page(ZPage* page);
|
void free_page(ZPage* page, bool allow_defragment);
|
||||||
size_t free_empty_pages(const ZArray<ZPage*>* pages);
|
size_t free_empty_pages(const ZArray<ZPage*>* pages);
|
||||||
|
|
||||||
// Object allocation
|
// Object allocation
|
||||||
|
@ -275,7 +275,7 @@ bool ZPageAllocator::prime_cache(ZWorkers* workers, size_t size) {
|
|||||||
workers->run_all(&task);
|
workers->run_all(&task);
|
||||||
}
|
}
|
||||||
|
|
||||||
free_page(page);
|
free_page(page, false /* allow_defragment */);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -462,6 +462,38 @@ void ZPageAllocator::destroy_page(ZPage* page) {
|
|||||||
safe_destroy_page(page);
|
safe_destroy_page(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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() == ZPageType::small &&
|
||||||
|
page->start() >= to_zoffset(_virtual.reserved() / 2) &&
|
||||||
|
page->start() > _virtual.lowest_available_address();
|
||||||
|
}
|
||||||
|
|
||||||
|
ZPage* ZPageAllocator::defragment_page(ZPage* page) {
|
||||||
|
// Harvest the physical memory (which is committed)
|
||||||
|
ZPhysicalMemory pmem;
|
||||||
|
ZPhysicalMemory& old_pmem = page->physical_memory();
|
||||||
|
pmem.add_segments(old_pmem);
|
||||||
|
old_pmem.remove_segments();
|
||||||
|
|
||||||
|
_unmapper->unmap_and_destroy_page(page);
|
||||||
|
|
||||||
|
// Allocate new virtual memory at a low address
|
||||||
|
const ZVirtualMemory vmem = _virtual.alloc(pmem.size(), true /* force_low_address */);
|
||||||
|
|
||||||
|
// Create the new page and map it
|
||||||
|
ZPage* new_page = new ZPage(ZPageType::small, vmem, pmem);
|
||||||
|
map_page(new_page);
|
||||||
|
|
||||||
|
// Update statistics
|
||||||
|
ZStatInc(ZCounterDefragment);
|
||||||
|
|
||||||
|
return new_page;
|
||||||
|
}
|
||||||
|
|
||||||
bool ZPageAllocator::is_alloc_allowed(size_t size) const {
|
bool ZPageAllocator::is_alloc_allowed(size_t size) const {
|
||||||
const size_t available = _current_max_capacity - _used - _claimed;
|
const size_t available = _current_max_capacity - _used - _claimed;
|
||||||
return available >= size;
|
return available >= size;
|
||||||
@ -623,16 +655,6 @@ ZPage* ZPageAllocator::alloc_page_create(ZPageAllocation* allocation) {
|
|||||||
return new ZPage(allocation->type(), vmem, pmem);
|
return new ZPage(allocation->type(), vmem, pmem);
|
||||||
}
|
}
|
||||||
|
|
||||||
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() == ZPageType::small &&
|
|
||||||
page->start() >= to_zoffset(_virtual.reserved() / 2) &&
|
|
||||||
page->start() > _virtual.lowest_available_address();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ZPageAllocator::is_alloc_satisfied(ZPageAllocation* allocation) const {
|
bool ZPageAllocator::is_alloc_satisfied(ZPageAllocation* allocation) const {
|
||||||
// The allocation is immediately satisfied if the list of pages contains
|
// The allocation is immediately satisfied if the list of pages contains
|
||||||
// exactly one page, with the type and size that was requested. However,
|
// exactly one page, with the type and size that was requested. However,
|
||||||
@ -652,12 +674,6 @@ bool ZPageAllocator::is_alloc_satisfied(ZPageAllocation* allocation) const {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (should_defragment(page)) {
|
|
||||||
// Defragment address space
|
|
||||||
ZStatInc(ZCounterDefragment);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocation immediately satisfied
|
// Allocation immediately satisfied
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -773,6 +789,18 @@ void ZPageAllocator::satisfy_stalled() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ZPage* ZPageAllocator::prepare_to_recycle(ZPage* page, bool allow_defragment) {
|
||||||
|
// Make sure we have a page that is safe to recycle
|
||||||
|
ZPage* const to_recycle = _safe_recycle.register_and_clone_if_activated(page);
|
||||||
|
|
||||||
|
// Defragment the page before recycle if allowed and needed
|
||||||
|
if (allow_defragment && should_defragment(to_recycle)) {
|
||||||
|
return defragment_page(to_recycle);
|
||||||
|
}
|
||||||
|
|
||||||
|
return to_recycle;
|
||||||
|
}
|
||||||
|
|
||||||
void ZPageAllocator::recycle_page(ZPage* page) {
|
void ZPageAllocator::recycle_page(ZPage* page) {
|
||||||
// Set time when last used
|
// Set time when last used
|
||||||
page->set_last_used();
|
page->set_last_used();
|
||||||
@ -781,9 +809,11 @@ void ZPageAllocator::recycle_page(ZPage* page) {
|
|||||||
_cache.free_page(page);
|
_cache.free_page(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ZPageAllocator::free_page(ZPage* page) {
|
void ZPageAllocator::free_page(ZPage* page, bool allow_defragment) {
|
||||||
const ZGenerationId generation_id = page->generation_id();
|
const ZGenerationId generation_id = page->generation_id();
|
||||||
ZPage* const to_recycle = _safe_recycle.register_and_clone_if_activated(page);
|
|
||||||
|
// Prepare page for recycling before taking the lock
|
||||||
|
ZPage* const to_recycle = prepare_to_recycle(page, allow_defragment);
|
||||||
|
|
||||||
ZLocker<ZLock> locker(&_lock);
|
ZLocker<ZLock> locker(&_lock);
|
||||||
|
|
||||||
@ -800,11 +830,12 @@ void ZPageAllocator::free_page(ZPage* page) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ZPageAllocator::free_pages(const ZArray<ZPage*>* pages) {
|
void ZPageAllocator::free_pages(const ZArray<ZPage*>* pages) {
|
||||||
ZArray<ZPage*> to_recycle;
|
ZArray<ZPage*> to_recycle_pages;
|
||||||
|
|
||||||
size_t young_size = 0;
|
size_t young_size = 0;
|
||||||
size_t old_size = 0;
|
size_t old_size = 0;
|
||||||
|
|
||||||
|
// Prepare pages for recycling before taking the lock
|
||||||
ZArrayIterator<ZPage*> pages_iter(pages);
|
ZArrayIterator<ZPage*> pages_iter(pages);
|
||||||
for (ZPage* page; pages_iter.next(&page);) {
|
for (ZPage* page; pages_iter.next(&page);) {
|
||||||
if (page->is_young()) {
|
if (page->is_young()) {
|
||||||
@ -812,7 +843,12 @@ void ZPageAllocator::free_pages(const ZArray<ZPage*>* pages) {
|
|||||||
} else {
|
} else {
|
||||||
old_size += page->size();
|
old_size += page->size();
|
||||||
}
|
}
|
||||||
to_recycle.push(_safe_recycle.register_and_clone_if_activated(page));
|
|
||||||
|
// Prepare to recycle
|
||||||
|
ZPage* const to_recycle = prepare_to_recycle(page, true /* allow_defragment */);
|
||||||
|
|
||||||
|
// Register for recycling
|
||||||
|
to_recycle_pages.push(to_recycle);
|
||||||
}
|
}
|
||||||
|
|
||||||
ZLocker<ZLock> locker(&_lock);
|
ZLocker<ZLock> locker(&_lock);
|
||||||
@ -823,7 +859,7 @@ void ZPageAllocator::free_pages(const ZArray<ZPage*>* pages) {
|
|||||||
decrease_used_generation(ZGenerationId::old, old_size);
|
decrease_used_generation(ZGenerationId::old, old_size);
|
||||||
|
|
||||||
// Free pages
|
// Free pages
|
||||||
ZArrayIterator<ZPage*> iter(&to_recycle);
|
ZArrayIterator<ZPage*> iter(&to_recycle_pages);
|
||||||
for (ZPage* page; iter.next(&page);) {
|
for (ZPage* page; iter.next(&page);) {
|
||||||
recycle_page(page);
|
recycle_page(page);
|
||||||
}
|
}
|
||||||
@ -833,11 +869,16 @@ void ZPageAllocator::free_pages(const ZArray<ZPage*>* pages) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ZPageAllocator::free_pages_alloc_failed(ZPageAllocation* allocation) {
|
void ZPageAllocator::free_pages_alloc_failed(ZPageAllocation* allocation) {
|
||||||
ZArray<ZPage*> to_recycle;
|
ZArray<ZPage*> to_recycle_pages;
|
||||||
|
|
||||||
|
// Prepare pages for recycling before taking the lock
|
||||||
ZListRemoveIterator<ZPage> allocation_pages_iter(allocation->pages());
|
ZListRemoveIterator<ZPage> allocation_pages_iter(allocation->pages());
|
||||||
for (ZPage* page; allocation_pages_iter.next(&page);) {
|
for (ZPage* page; allocation_pages_iter.next(&page);) {
|
||||||
to_recycle.push(_safe_recycle.register_and_clone_if_activated(page));
|
// Prepare to recycle
|
||||||
|
ZPage* const to_recycle = prepare_to_recycle(page, false /* allow_defragment */);
|
||||||
|
|
||||||
|
// Register for recycling
|
||||||
|
to_recycle_pages.push(to_recycle);
|
||||||
}
|
}
|
||||||
|
|
||||||
ZLocker<ZLock> locker(&_lock);
|
ZLocker<ZLock> locker(&_lock);
|
||||||
@ -849,7 +890,7 @@ void ZPageAllocator::free_pages_alloc_failed(ZPageAllocation* allocation) {
|
|||||||
size_t freed = 0;
|
size_t freed = 0;
|
||||||
|
|
||||||
// Free any allocated/flushed pages
|
// Free any allocated/flushed pages
|
||||||
ZArrayIterator<ZPage*> iter(&to_recycle);
|
ZArrayIterator<ZPage*> iter(&to_recycle_pages);
|
||||||
for (ZPage* page; iter.next(&page);) {
|
for (ZPage* page; iter.next(&page);) {
|
||||||
freed += page->size();
|
freed += page->size();
|
||||||
recycle_page(page);
|
recycle_page(page);
|
||||||
|
@ -104,13 +104,15 @@ private:
|
|||||||
|
|
||||||
void destroy_page(ZPage* page);
|
void destroy_page(ZPage* page);
|
||||||
|
|
||||||
|
bool should_defragment(const ZPage* page) const;
|
||||||
|
ZPage* defragment_page(ZPage* page);
|
||||||
|
|
||||||
bool is_alloc_allowed(size_t size) const;
|
bool is_alloc_allowed(size_t size) const;
|
||||||
|
|
||||||
bool alloc_page_common_inner(ZPageType type, size_t size, ZList<ZPage>* pages);
|
bool alloc_page_common_inner(ZPageType type, size_t size, ZList<ZPage>* pages);
|
||||||
bool alloc_page_common(ZPageAllocation* allocation);
|
bool alloc_page_common(ZPageAllocation* allocation);
|
||||||
bool alloc_page_stall(ZPageAllocation* allocation);
|
bool alloc_page_stall(ZPageAllocation* allocation);
|
||||||
bool alloc_page_or_stall(ZPageAllocation* allocation);
|
bool alloc_page_or_stall(ZPageAllocation* allocation);
|
||||||
bool should_defragment(const ZPage* page) const;
|
|
||||||
bool is_alloc_satisfied(ZPageAllocation* allocation) const;
|
bool is_alloc_satisfied(ZPageAllocation* allocation) const;
|
||||||
ZPage* alloc_page_create(ZPageAllocation* allocation);
|
ZPage* alloc_page_create(ZPageAllocation* allocation);
|
||||||
ZPage* alloc_page_finalize(ZPageAllocation* allocation);
|
ZPage* alloc_page_finalize(ZPageAllocation* allocation);
|
||||||
@ -149,9 +151,10 @@ public:
|
|||||||
void reset_statistics(ZGenerationId id);
|
void reset_statistics(ZGenerationId id);
|
||||||
|
|
||||||
ZPage* alloc_page(ZPageType type, size_t size, ZAllocationFlags flags, ZPageAge age);
|
ZPage* alloc_page(ZPageType type, size_t size, ZAllocationFlags flags, ZPageAge age);
|
||||||
|
ZPage* prepare_to_recycle(ZPage* page, bool allow_defragment);
|
||||||
void recycle_page(ZPage* page);
|
void recycle_page(ZPage* page);
|
||||||
void safe_destroy_page(ZPage* page);
|
void safe_destroy_page(ZPage* page);
|
||||||
void free_page(ZPage* page);
|
void free_page(ZPage* page, bool allow_defragment);
|
||||||
void free_pages(const ZArray<ZPage*>* pages);
|
void free_pages(const ZArray<ZPage*>* pages);
|
||||||
|
|
||||||
void enable_safe_destroy() const;
|
void enable_safe_destroy() const;
|
||||||
|
@ -411,7 +411,7 @@ static void retire_target_page(ZGeneration* generation, ZPage* page) {
|
|||||||
// relocate the remaining objects, leaving the target page empty when
|
// relocate the remaining objects, leaving the target page empty when
|
||||||
// relocation completed.
|
// relocation completed.
|
||||||
if (page->used() == 0) {
|
if (page->used() == 0) {
|
||||||
ZHeap::heap()->free_page(page);
|
ZHeap::heap()->free_page(page, true /* allow_defragment */);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1012,7 +1012,7 @@ public:
|
|||||||
page->log_msg(" (relocate page done normal)");
|
page->log_msg(" (relocate page done normal)");
|
||||||
|
|
||||||
// Free page
|
// Free page
|
||||||
ZHeap::heap()->free_page(page);
|
ZHeap::heap()->free_page(page, true /* allow_defragment */);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user