ZGC与传统GC不同,标记阶段标记的是指针(colored pointer),而非传统GC算法中的标记对象。ZGC借助内存映射,将多个地址映射到同一个内存文件描述符上,使得ZGC回收周期各阶段能够使用不同的地址访问同一对象。
本文将详细介绍ZGC的内存管理。
目前ZGC仅支持Linux/x64,指针是64位的,ZGC重新定义了指针结构。
zGlobals_linux_x86.hpp
// Address Space & Pointer Layout
// ------------------------------
//
// +--------------------------------+ 0x00007FFFFFFFFFFF (127TB)
// . .
// . .
// . .
// +--------------------------------+ 0x0000140000000000 (20TB)
// | Remapped View |
// +--------------------------------+ 0x0000100000000000 (16TB)
// | (Reserved, but unused) |
// +--------------------------------+ 0x00000c0000000000 (12TB)
// | Marked1 View |
// +--------------------------------+ 0x0000080000000000 (8TB)
// | Marked0 View |
// +--------------------------------+ 0x0000040000000000 (4TB)
// . .
// +--------------------------------+ 0x0000000000000000
//
//
// 6 4 4 4 4 4 0
// 3 7 6 5 2 1 0
// +-------------------+-+----+-----------------------------------------------+
// |00000000 00000000 0|0|1111|11 11111111 11111111 11111111 11111111 11111111|
// +-------------------+-+----+-----------------------------------------------+
// | | | |
// | | | * 41-0 Object Offset (42-bits, 4TB address space)
// | | |
// | | * 45-42 Metadata Bits (4-bits) 0001 = Marked0 (Address view 4-8TB)
// | | 0010 = Marked1 (Address view 8-12TB)
// | | 0100 = Remapped (Address view 16-20TB)
// | | 1000 = Finalizable (Address view N/A)
// | |
// | * 46-46 Unused (1-bit, always zero)
// |
// * 63-47 Fixed (17-bits, always zero)
//
const size_t ZPlatformAddressOffsetBits = 42; // 4TB
zBackingFile_linux_x86.cpp
// Java heap filename
#define ZFILENAME_HEAP "java_heap"
ZBackingFile::ZBackingFile() :
_fd(-1),
_filesystem(0),
_available(0),
_initialized(false) {
// Create backing file
_fd = create_fd(ZFILENAME_HEAP);
if (_fd == -1) {
return;
}
}
int ZBackingFile::create_fd(const char* name) const {
if (ZPath == NULL) {
// If the path is not explicitly specified, then we first try to create a memfd file
// instead of looking for a tmpfd/hugetlbfs mount point. Note that memfd_create() might
// not be supported at all (requires kernel >= 3.17), or it might not support large
// pages (requires kernel >= 4.14). If memfd_create() fails, then we try to create a
// file on an accessible tmpfs or hugetlbfs mount point.
const int fd = create_mem_fd(name);
if (fd != -1) {
return fd;
}
log_debug(gc, init)("Falling back to searching for an accessible mount point");
}
return create_file_fd(name);
}
int ZBackingFile::create_mem_fd(const char* name) const {
// Create file name
char filename[PATH_MAX];
snprintf(filename, sizeof(filename), "%s%s", name, ZLargePages::is_explicit() ? ".hugetlb" : "");
// Create file
const int extra_flags = ZLargePages::is_explicit() ? MFD_HUGETLB : 0;
const int fd = z_memfd_create(filename, MFD_CLOEXEC | extra_flags);
if (fd == -1) {
ZErrno err;
log_debug(gc, init)("Failed to create memfd file (%s)",
((UseLargePages && err == EINVAL) ? "Hugepages not supported" : err.to_string()));
return -1;
}
log_info(gc, init)("Heap backed by file: /memfd:%s", filename);
return fd;
}
与操作系统类型,为了便于管理,ZGC也定义了两级结构:虚拟内存和物理内存,分别由ZVirtualMemoryManager和ZPhysicalMemoryManager管理。
ZGC的物理内存并不是通常意义上的物理内存,而仅表示mutator使用的Java heap内存。因此仍然是用户空间虚拟内存,仅为了与Mark0、Mark1、Remapped视图区分。本文中的物理内存,如无特殊说明,特指ZGC中的物理内存。
zPhysicalMemory.cpp
ZPhysicalMemory ZPhysicalMemoryManager::alloc(size_t size) {
if (unused_capacity() < size) {
// Not enough memory available
return ZPhysicalMemory();
}
_used += size;
return _backing.alloc(size);
}
zPhysicalMemoryBacking_linux_x86.cpp
ZPhysicalMemory ZPhysicalMemoryBacking::alloc(size_t size) {
assert(is_aligned(size, _granule_size), "Invalid size");
ZPhysicalMemory pmem;
// Allocate segments
for (size_t allocated = 0; allocated < size; allocated += _granule_size) {
const uintptr_t start = _manager.alloc_from_front(_granule_size);
assert(start != UINTPTR_MAX, "Allocation should never fail");
pmem.add_segment(ZPhysicalMemorySegment(start, _granule_size));
}
return pmem;
}
zMemory.cpp
uintptr_t ZMemoryManager::alloc_from_front(size_t size) {
ZListIterator iter(&_freelist);
for (ZMemory* area; iter.next(&area);) {
if (area->size() >= size) {
if (area->size() == size) {
// Exact match, remove area
const uintptr_t start = area->start();
_freelist.remove(area);
delete area;
return start;
} else {
// Larger than requested, shrink area
const uintptr_t start = area->start();
area->shrink_from_front(size);
return start;
}
}
}
// Out of memory
return UINTPTR_MAX;
}
zPhysicalMemory.cpp
void ZPhysicalMemoryManager::free(ZPhysicalMemory pmem) {
_backing.free(pmem);
_used -= pmem.size();
}
zPhysicalMemoryBacking_linux_x86.cpp
void ZPhysicalMemoryBacking::free(ZPhysicalMemory pmem) {
const size_t nsegments = pmem.nsegments();
// Free segments
for (size_t i = 0; i < nsegments; i++) {
const ZPhysicalMemorySegment segment = pmem.segment(i);
_manager.free(segment.start(), segment.size());
}
}
zMemory.cpp
void ZMemoryManager::free(uintptr_t start, size_t size) {
assert(start != UINTPTR_MAX, "Invalid address");
const uintptr_t end = start + size;
ZListIterator iter(&_freelist);
for (ZMemory* area; iter.next(&area);) {
if (start < area->start()) {
ZMemory* const prev = _freelist.prev(area);
if (prev != NULL && start == prev->end()) {
if (end == area->start()) {
// Merge with prev and current area
prev->grow_from_back(size + area->size());
_freelist.remove(area);
delete area;
} else {
// Merge with prev area
prev->grow_from_back(size);
}
} else if (end == area->start()) {
// Merge with current area
area->grow_from_front(size);
} else {
// Insert new area before current area
assert(end < area->start(), "Areas must not overlap");
ZMemory* new_area = new ZMemory(start, size);
_freelist.insert_before(area, new_area);
}
// Done
return;
}
}
// Insert last
ZMemory* const last = _freelist.last();
if (last != NULL && start == last->end()) {
// Merge with last area
last->grow_from_back(size);
} else {
// Insert new area last
ZMemory* new_area = new ZMemory(start, size);
_freelist.insert_last(new_area);
}
}
ZPageAllocator在分配页时,会调用ZPhysicalMemoryManager的map函数,进而调用到ZPhysicalMemoryBacking的map函数。
zPhysicalMemoryBacking_linux_x86.cpp
void ZPhysicalMemoryBacking::map(ZPhysicalMemory pmem, uintptr_t offset) const {
if (ZUnmapBadViews) {
// Only map the good view, for debugging only
map_view(pmem, ZAddress::good(offset), AlwaysPreTouch);
} else {
// Map all views
map_view(pmem, ZAddress::marked0(offset), AlwaysPreTouch);
map_view(pmem, ZAddress::marked1(offset), AlwaysPreTouch);
map_view(pmem, ZAddress::remapped(offset), AlwaysPreTouch);
}
}
OpenJDK 12 源代码