Skip to content

Commit 17e40b0

Browse files
fiskjsikstro
authored andcommitted
JEP: Automatic Heap Sizing for ZGC
1 parent 65ddcd2 commit 17e40b0

76 files changed

Lines changed: 4490 additions & 451 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/hotspot/os/bsd/gc/z/zNUMA_bsd.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828

2929
void ZNUMA::pd_initialize() {
3030
_enabled = false;
31+
_node_count = 1;
32+
_bound_node_count = 1;
3133
_count = !FLAG_IS_DEFAULT(ZFakeNUMA)
3234
? ZFakeNUMA
3335
: 1;

src/hotspot/os/bsd/gc/z/zPhysicalMemoryBacking_bsd.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ bool ZPhysicalMemoryBacking::is_initialized() const {
9292
return _initialized;
9393
}
9494

95-
void ZPhysicalMemoryBacking::warn_commit_limits(size_t max_capacity) const {
95+
void ZPhysicalMemoryBacking::warn_commit_limits(size_t expected_capacity, size_t max_capacity) const {
9696
// Does nothing
9797
}
9898

@@ -178,3 +178,7 @@ void ZPhysicalMemoryBacking::unmap(zaddress_unsafe addr, size_t size) const {
178178
fatal("Failed to map memory (%s)", err.to_string());
179179
}
180180
}
181+
182+
void ZPhysicalMemoryBacking::collapse(zaddress_unsafe addr, size_t size) const {
183+
ShouldNotReachHere();
184+
}

src/hotspot/os/bsd/gc/z/zPhysicalMemoryBacking_bsd.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,15 @@ class ZPhysicalMemoryBacking {
3838

3939
bool is_initialized() const;
4040

41-
void warn_commit_limits(size_t max_capacity) const;
41+
void warn_commit_limits(size_t expected_capacity, size_t max_capacity) const;
4242

4343
size_t commit(zbacking_offset offset, size_t length, uint32_t numa_id) const;
4444
size_t uncommit(zbacking_offset offset, size_t length) const;
4545

4646
void map(zaddress_unsafe addr, size_t size, zbacking_offset offset) const;
4747
void unmap(zaddress_unsafe addr, size_t size) const;
48+
49+
void collapse(zaddress_unsafe addr, size_t size) const;
4850
};
4951

5052
#endif // OS_BSD_GC_Z_ZPHYSICALMEMORYBACKING_BSD_HPP

src/hotspot/os/linux/gc/z/zLargePages_linux.cpp

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,59 @@
2121
* questions.
2222
*/
2323

24+
#include "gc/z/zAdaptiveHeap.inline.hpp"
2425
#include "gc/z/zLargePages.hpp"
2526
#include "hugepages.hpp"
2627
#include "os_linux.hpp"
2728
#include "runtime/globals.hpp"
29+
#include "runtime/globals_extension.hpp"
30+
31+
#include <sys/mman.h>
32+
33+
static bool madv_collapse_available() {
34+
const size_t size = 2 * M;
35+
void* const res = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
36+
37+
if (res == MAP_FAILED) {
38+
return false;
39+
}
40+
41+
assert(size >= os::vm_page_size(), "Unexpected page size");
42+
os::pretouch_memory(res, (void*)(((char*)res) + os::vm_page_size()));
43+
44+
bool result = os::Linux::madvise_collapse_transparent_huge_pages(res, size);
45+
46+
munmap(res, size);
47+
48+
return result;
49+
}
2850

2951
void ZLargePages::pd_initialize() {
52+
bool can_collapse = ZMemoryHeating && madv_collapse_available();
53+
3054
if (os::Linux::thp_requested()) {
55+
if (can_collapse) {
56+
_state = Collapse;
57+
return;
58+
}
3159
// Check if the OS config turned off transparent huge pages for shmem.
3260
_os_enforced_transparent_mode = HugePages::shmem_thp_info().is_disabled();
3361
_state = _os_enforced_transparent_mode ? Disabled : Transparent;
3462
return;
3563
}
3664

3765
if (UseLargePages) {
38-
_state = Explicit;
66+
if (!ZAdaptiveHeap::can_adapt() || ZAdaptiveHeap::explicit_max_capacity()) {
67+
_state = Explicit;
68+
return;
69+
}
70+
71+
log_warning(gc, init)("UseLargePages requires a max heap size to be set (-Xmx) when running with Automatic Heap Sizing. "
72+
"Disabling the use of explicit large pages for the heap");
73+
}
74+
75+
if (FLAG_IS_DEFAULT(UseTransparentHugePages) && can_collapse) {
76+
_state = Collapse;
3977
return;
4078
}
4179

src/hotspot/os/linux/gc/z/zNUMA_linux.cpp

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,24 +38,32 @@ static uint32_t* z_node_to_numa_id = nullptr;
3838
void ZNUMA::pd_initialize() {
3939
_enabled = UseNUMA;
4040

41-
size_t configured_nodes = 0;
41+
uint32_t configured_nodes = 1;
42+
const int max_nodes = os::Linux::numa_num_configured_nodes();
4243

43-
if (UseNUMA) {
44-
const size_t max_nodes = os::Linux::numa_num_configured_nodes();
45-
z_numa_id_to_node = NEW_C_HEAP_ARRAY(uint, max_nodes, mtGC);
46-
configured_nodes = os::numa_get_leaf_groups(z_numa_id_to_node, 0);
44+
if (max_nodes != -1) {
45+
assert(max_nodes >= 1, "bad max_nodes value: %d", max_nodes);
4746

48-
z_node_to_numa_id = NEW_C_HEAP_ARRAY(uint32_t, max_nodes, mtGC);
47+
z_numa_id_to_node = NEW_C_HEAP_ARRAY(uint, (size_t)max_nodes, mtGC);
48+
configured_nodes = (uint32_t)os::numa_get_leaf_groups(z_numa_id_to_node, 0);
49+
50+
z_node_to_numa_id = NEW_C_HEAP_ARRAY(uint32_t, (size_t)max_nodes, mtGC);
4951

5052
// Fill the array with invalid NUMA ids
51-
for (uint32_t i = 0; i < max_nodes; i++) {
53+
for (uint32_t i = 0; i < (size_t)max_nodes; i++) {
5254
z_node_to_numa_id[i] = (uint32_t)-1;
5355
}
5456

5557
// Fill the reverse mappings
5658
for (uint32_t i = 0; i < configured_nodes; i++) {
5759
z_node_to_numa_id[z_numa_id_to_node[i]] = i;
5860
}
61+
62+
_node_count = (uint32_t)max_nodes;
63+
_bound_node_count = configured_nodes;
64+
} else {
65+
_node_count = 1;
66+
_bound_node_count = 1;
5967
}
6068

6169
// UseNUMA and is_faked() are mutually excluded in zArguments.cpp.

src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.cpp

Lines changed: 56 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
*/
2323

2424
#include "gc/shared/gcLogPrecious.hpp"
25+
#include "gc/z/zAdaptiveHeap.inline.hpp"
2526
#include "gc/z/zAddress.inline.hpp"
2627
#include "gc/z/zArray.inline.hpp"
2728
#include "gc/z/zErrno.hpp"
@@ -35,6 +36,8 @@
3536
#include "hugepages.hpp"
3637
#include "logging/log.hpp"
3738
#include "os_linux.hpp"
39+
#include "runtime/globals.hpp"
40+
#include "runtime/globals_extension.hpp"
3841
#include "runtime/init.hpp"
3942
#include "runtime/os.hpp"
4043
#include "runtime/safefetch.hpp"
@@ -116,6 +119,8 @@ static const char* ZPreferredHugetlbfsMountpoints[] = {
116119
static int z_fallocate_hugetlbfs_attempts = 3;
117120
static bool z_fallocate_supported = true;
118121

122+
static size_t z_max_map_count = 0;
123+
119124
ZPhysicalMemoryBacking::ZPhysicalMemoryBacking(size_t max_capacity)
120125
: _fd(-1),
121126
_filesystem(0),
@@ -151,6 +156,14 @@ ZPhysicalMemoryBacking::ZPhysicalMemoryBacking(size_t max_capacity)
151156
_block_size = buf.f_bsize;
152157
_available = buf.f_bavail * _block_size;
153158

159+
// Note that the available space on a tmpfs or a hugetlbfs filesystem
160+
// will be zero if no size limit was specified when it was mounted.
161+
if (_available == 0) {
162+
log_info_p(gc, init)("Available space on backing filesystem: N/A");
163+
} else {
164+
log_info_p(gc, init)("Available space on backing filesystem: %zuM", _available / M);
165+
}
166+
154167
log_info_p(gc, init)("Heap Backing Filesystem: %s (" UINT64_FORMAT_X ")",
155168
is_tmpfs() ? ZFILESYSTEM_TMPFS : is_hugetlbfs() ? ZFILESYSTEM_HUGETLBFS : "other", _filesystem);
156169

@@ -308,12 +321,9 @@ void ZPhysicalMemoryBacking::warn_available_space(size_t max_capacity) const {
308321
// will be zero if no size limit was specified when it was mounted.
309322
if (_available == 0) {
310323
// No size limit set, skip check
311-
log_info_p(gc, init)("Available space on backing filesystem: N/A");
312324
return;
313325
}
314326

315-
log_info_p(gc, init)("Available space on backing filesystem: %zuM", _available / M);
316-
317327
// Warn if the filesystem doesn't currently have enough space available to hold
318328
// the max heap size. The max heap size will be capped if we later hit this limit
319329
// when trying to expand the heap.
@@ -328,7 +338,11 @@ void ZPhysicalMemoryBacking::warn_available_space(size_t max_capacity) const {
328338
}
329339
}
330340

331-
void ZPhysicalMemoryBacking::warn_max_map_count(size_t max_capacity) const {
341+
void ZPhysicalMemoryBacking::compute_max_map_count() const {
342+
if (z_max_map_count != 0) {
343+
return;
344+
}
345+
332346
const char* const filename = ZFILENAME_PROC_MAX_MAP_COUNT;
333347
FILE* const file = os::fopen(filename, "r");
334348
if (file == nullptr) {
@@ -337,38 +351,50 @@ void ZPhysicalMemoryBacking::warn_max_map_count(size_t max_capacity) const {
337351
return;
338352
}
339353

340-
size_t actual_max_map_count = 0;
341-
const int result = fscanf(file, "%zu", &actual_max_map_count);
354+
const int result = fscanf(file, "%zu", &z_max_map_count);
342355
fclose(file);
343356
if (result != 1) {
344357
// Failed to read file, skip check
345358
log_debug_p(gc, init)("Failed to read %s", filename);
346359
return;
347360
}
361+
}
362+
363+
void ZPhysicalMemoryBacking::warn_max_map_count(size_t expected_capacity, size_t max_capacity) const {
364+
// In the worst case, ZGC needs 2 mappings per granule. The reason is that if the
365+
// fragmentation is at maximum, the mappings will be interleaved between being
366+
// backed by memory vs not being backed by memory.
367+
const size_t mappings_per_granule = 2;
348368

349369
// The required max map count is impossible to calculate exactly since subsystems
350370
// other than ZGC are also creating memory mappings, and we have no control over that.
351371
// However, ZGC tends to create the most mappings and dominate the total count.
352-
// In the worst cases, ZGC will map each granule three times, i.e. once per heap view.
353-
// We speculate that we need another 20% to allow for non-ZGC subsystems to map memory.
354-
const size_t required_max_map_count = (max_capacity / ZGranuleSize) * 3 * 1.2;
355-
if (actual_max_map_count < required_max_map_count) {
356-
log_warning_p(gc)("***** WARNING! INCORRECT SYSTEM CONFIGURATION DETECTED! *****");
357-
log_warning_p(gc)("The system limit on number of memory mappings per process might be too low for the given");
358-
log_warning_p(gc)("max Java heap size (%zuM). Please adjust %s to allow for at",
359-
max_capacity / M, filename);
360-
log_warning_p(gc)("least %zu mappings (current limit is %zu). Continuing execution "
361-
"with the current", required_max_map_count, actual_max_map_count);
362-
log_warning_p(gc)("limit could lead to a premature OutOfMemoryError being thrown, due to failure to map memory.");
372+
const size_t required_max_map_count = (expected_capacity / ZGranuleSize) * mappings_per_granule * 1.2;
373+
if (z_max_map_count >= required_max_map_count) {
374+
return;
363375
}
376+
377+
const size_t recommended_max_map_count = (max_capacity / ZGranuleSize) * mappings_per_granule * 1.2;
378+
379+
const char* const filename = ZFILENAME_PROC_MAX_MAP_COUNT;
380+
log_warning_p(gc)("***** WARNING! INCORRECT SYSTEM CONFIGURATION DETECTED! *****");
381+
log_warning_p(gc)("The system limit on number of memory mappings per process might be too low for the");
382+
log_warning_p(gc)("Java heap size (%zuM). Please adjust %s to allow for at",
383+
expected_capacity / M, filename);
384+
log_warning_p(gc)("least %zu mappings (current limit is %zu). Continuing execution "
385+
"with the current", recommended_max_map_count, z_max_map_count);
386+
log_warning_p(gc)("limit could lead to a premature OutOfMemoryError being thrown, due to failure to map memory.");
364387
}
365388

366-
void ZPhysicalMemoryBacking::warn_commit_limits(size_t max_capacity) const {
389+
void ZPhysicalMemoryBacking::warn_commit_limits(size_t expected_capacity, size_t max_capacity) const {
367390
// Warn if available space is too low
368-
warn_available_space(max_capacity);
391+
warn_available_space(expected_capacity);
392+
393+
// Compute max map count the first time (during bootstrapping)
394+
compute_max_map_count();
369395

370396
// Warn if max map count is too low
371-
warn_max_map_count(max_capacity);
397+
warn_max_map_count(expected_capacity, max_capacity);
372398
}
373399

374400
bool ZPhysicalMemoryBacking::is_tmpfs() const {
@@ -513,7 +539,7 @@ ZErrno ZPhysicalMemoryBacking::fallocate_fill_hole(zbacking_offset offset, size_
513539
// some point touch these segments, otherwise we can not punch hole in them.
514540
// Also note that we need to use compat mode when using transparent huge pages,
515541
// since we need to use madvise(2) on the mapping before the page is allocated.
516-
if (z_fallocate_supported && !ZLargePages::is_enabled()) {
542+
if (z_fallocate_supported && !ZLargePages::is_explicit()) {
517543
const ZErrno err = fallocate_fill_hole_syscall(offset, length);
518544
if (!err) {
519545
// Success
@@ -615,8 +641,12 @@ bool ZPhysicalMemoryBacking::commit_inner(zbacking_offset offset, size_t length)
615641
goto retry;
616642
}
617643

644+
static Atomic<bool> warned_failed_commit{false};
645+
if (warned_failed_commit.compare_exchange(false, true) == false) {
646+
log_error_p(gc)("Failed to commit memory (%s)", err.to_string());
647+
}
648+
618649
// Failed
619-
log_error_p(gc)("Failed to commit memory (%s)", err.to_string());
620650
return false;
621651
}
622652

@@ -705,3 +735,7 @@ void ZPhysicalMemoryBacking::unmap(zaddress_unsafe addr, size_t size) const {
705735
fatal("Failed to map memory (%s)", err.to_string());
706736
}
707737
}
738+
739+
void ZPhysicalMemoryBacking::collapse(zaddress_unsafe addr, size_t size) const {
740+
os::Linux::madvise_collapse_transparent_huge_pages((void*)untype(addr), size);
741+
}

src/hotspot/os/linux/gc/z/zPhysicalMemoryBacking_linux.hpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@ class ZErrno;
3131
class ZPhysicalMemoryBacking {
3232
private:
3333
int _fd;
34-
size_t _size;
3534
uint64_t _filesystem;
3635
size_t _block_size;
3736
size_t _available;
3837
bool _initialized;
3938

4039
void warn_available_space(size_t max_capacity) const;
41-
void warn_max_map_count(size_t max_capacity) const;
40+
void compute_max_map_count() const;
41+
void warn_max_map_count(size_t warn_capacity, size_t max_capacity) const;
4242

4343
int create_mem_fd(const char* name) const;
4444
int create_file_fd(const char* name) const;
@@ -67,13 +67,15 @@ class ZPhysicalMemoryBacking {
6767

6868
bool is_initialized() const;
6969

70-
void warn_commit_limits(size_t max_capacity) const;
70+
void warn_commit_limits(size_t expected_capacity, size_t max_capacity) const;
7171

7272
size_t commit(zbacking_offset offset, size_t length, uint32_t numa_id) const;
7373
size_t uncommit(zbacking_offset offset, size_t length) const;
7474

7575
void map(zaddress_unsafe addr, size_t size, zbacking_offset offset) const;
7676
void unmap(zaddress_unsafe addr, size_t size) const;
77+
78+
void collapse(zaddress_unsafe addr, size_t size) const;
7779
};
7880

7981
#endif // OS_LINUX_GC_Z_ZPHYSICALMEMORYBACKING_LINUX_HPP

src/hotspot/os/linux/osContainer_linux.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,11 +90,11 @@ void OSContainer::init() {
9090
// 1.) On a physical Linux system without any limit
9191
// 2.) On a physical Linux system with a limit enforced by other means (like systemd slice)
9292
physical_memory_size_type mem_limit_val;
93-
any_mem_cpu_limit_present = any_mem_cpu_limit_present || memory_limit_in_bytes(mem_limit_val);
94-
any_mem_cpu_limit_present = any_mem_cpu_limit_present || memory_throttle_limit_in_bytes(mem_limit_val);
95-
any_mem_cpu_limit_present = any_mem_cpu_limit_present || memory_soft_limit_in_bytes(mem_limit_val);
93+
any_mem_cpu_limit_present = any_mem_cpu_limit_present || (memory_limit_in_bytes(mem_limit_val) && mem_limit_val != value_unlimited);
94+
any_mem_cpu_limit_present = any_mem_cpu_limit_present || (memory_throttle_limit_in_bytes(mem_limit_val) && mem_limit_val != value_unlimited);
95+
any_mem_cpu_limit_present = any_mem_cpu_limit_present || (memory_soft_limit_in_bytes(mem_limit_val) && mem_limit_val != value_unlimited && mem_limit_val != 0);
9696
double cpus;
97-
any_mem_cpu_limit_present = any_mem_cpu_limit_present || active_processor_count(cpus);
97+
any_mem_cpu_limit_present = any_mem_cpu_limit_present || (active_processor_count(cpus) && cpus < double(os::Linux::active_processor_count()));
9898
if (any_mem_cpu_limit_present) {
9999
reason = " because either a cpu or a memory limit is present";
100100
} else {

src/hotspot/os/linux/os_linux.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3265,6 +3265,8 @@ bool os::Linux::libnuma_init() {
32653265
libnuma_dlsym(handle, "numa_num_configured_nodes")));
32663266
set_numa_available(CAST_TO_FN_PTR(numa_available_func_t,
32673267
libnuma_dlsym(handle, "numa_available")));
3268+
set_numa_node_size64(CAST_TO_FN_PTR(numa_node_size64_func_t,
3269+
libnuma_dlsym(handle, "numa_node_size64")));
32683270
set_numa_tonode_memory(CAST_TO_FN_PTR(numa_tonode_memory_func_t,
32693271
libnuma_dlsym(handle, "numa_tonode_memory")));
32703272
set_numa_interleave_memory(CAST_TO_FN_PTR(numa_interleave_memory_func_t,
@@ -3537,6 +3539,7 @@ os::Linux::numa_node_to_cpus_func_t os::Linux::_numa_node_to_cpus;
35373539
os::Linux::numa_node_to_cpus_v2_func_t os::Linux::_numa_node_to_cpus_v2;
35383540
os::Linux::numa_max_node_func_t os::Linux::_numa_max_node;
35393541
os::Linux::numa_num_configured_nodes_func_t os::Linux::_numa_num_configured_nodes;
3542+
os::Linux::numa_node_size64_func_t os::Linux::_numa_node_size64;
35403543
os::Linux::numa_available_func_t os::Linux::_numa_available;
35413544
os::Linux::numa_tonode_memory_func_t os::Linux::_numa_tonode_memory;
35423545
os::Linux::numa_interleave_memory_func_t os::Linux::_numa_interleave_memory;

0 commit comments

Comments
 (0)