< prev index next >
src/hotspot/os/linux/gc/z/zBackingFile_linux.cpp
Print this page
@@ -34,10 +34,11 @@
#include "runtime/os.hpp"
#include "utilities/align.hpp"
#include "utilities/debug.hpp"
#include <fcntl.h>
+#include <stdio.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/types.h>
#include <unistd.h>
@@ -80,10 +81,13 @@
// Filesystem names
#define ZFILESYSTEM_TMPFS "tmpfs"
#define ZFILESYSTEM_HUGETLBFS "hugetlbfs"
+// Proc file entry for max map mount
+#define ZFILENAME_PROC_MAX_MAP_COUNT "/proc/sys/vm/max_map_count"
+
// Sysfs file for transparent huge page on tmpfs
#define ZFILENAME_SHMEM_ENABLED "/sys/kernel/mm/transparent_hugepage/shmem_enabled"
// Java heap filename
#define ZFILENAME_HEAP "java_heap"
@@ -276,20 +280,80 @@
bool ZBackingFile::is_initialized() const {
return _initialized;
}
-int ZBackingFile::fd() const {
- return _fd;
+void ZBackingFile::warn_available_space(size_t max) const {
+ // Note that the available space on a tmpfs or a hugetlbfs filesystem
+ // will be zero if no size limit was specified when it was mounted.
+ if (_available == 0) {
+ // No size limit set, skip check
+ log_info(gc, init)("Available space on backing filesystem: N/A");
+ return;
+ }
+
+ log_info(gc, init)("Available space on backing filesystem: " SIZE_FORMAT "M", _available / M);
+
+ // Warn if the filesystem doesn't currently have enough space available to hold
+ // the max heap size. The max heap size will be capped if we later hit this limit
+ // when trying to expand the heap.
+ if (_available < max) {
+ log_warning(gc)("***** WARNING! INCORRECT SYSTEM CONFIGURATION DETECTED! *****");
+ log_warning(gc)("Not enough space available on the backing filesystem to hold the current max Java heap");
+ log_warning(gc)("size (" SIZE_FORMAT "M). Please adjust the size of the backing filesystem accordingly "
+ "(available", max / M);
+ log_warning(gc)("space is currently " SIZE_FORMAT "M). Continuing execution with the current filesystem "
+ "size could", _available / M);
+ log_warning(gc)("lead to a premature OutOfMemoryError being thrown, due to failure to map memory.");
+ }
}
-size_t ZBackingFile::size() const {
- return _size;
+void ZBackingFile::warn_max_map_count(size_t max) const {
+ const char* const filename = ZFILENAME_PROC_MAX_MAP_COUNT;
+ FILE* const file = fopen(filename, "r");
+ if (file == NULL) {
+ // Failed to open file, skip check
+ log_debug(gc, init)("Failed to open %s", filename);
+ return;
+ }
+
+ size_t actual_max_map_count = 0;
+ const int result = fscanf(file, SIZE_FORMAT, &actual_max_map_count);
+ fclose(file);
+ if (result != 1) {
+ // Failed to read file, skip check
+ log_debug(gc, init)("Failed to read %s", filename);
+ return;
+ }
+
+ // The required max map count is impossible to calculate exactly since subsystems
+ // other than ZGC are also creating memory mappings, and we have no control over that.
+ // However, ZGC tends to create the most mappings and dominate the total count.
+ // In the worst cases, ZGC will map each granule three times, i.e. once per heap view.
+ // We speculate that we need another 20% to allow for non-ZGC subsystems to map memory.
+ const size_t required_max_map_count = (max / ZGranuleSize) * 3 * 1.2;
+ if (actual_max_map_count < required_max_map_count) {
+ log_warning(gc)("***** WARNING! INCORRECT SYSTEM CONFIGURATION DETECTED! *****");
+ log_warning(gc)("The system limit on number of memory mappings per process might be too low for the given");
+ log_warning(gc)("max Java heap size (" SIZE_FORMAT "M). Please adjust %s to allow for at",
+ max / M, filename);
+ log_warning(gc)("least " SIZE_FORMAT " mappings (current limit is " SIZE_FORMAT "). Continuing execution "
+ "with the current", required_max_map_count, actual_max_map_count);
+ log_warning(gc)("limit could lead to a fatal error, due to failure to map memory.");
+ }
}
-size_t ZBackingFile::available() const {
- return _available;
+void ZBackingFile::warn_commit_limits(size_t max) const {
+ // Warn if available space is too low
+ warn_available_space(max);
+
+ // Warn if max map count is too low
+ warn_max_map_count(max);
+}
+
+size_t ZBackingFile::size() const {
+ return _size;
}
bool ZBackingFile::is_tmpfs() const {
return _filesystem == TMPFS_MAGIC;
}
@@ -570,5 +634,24 @@
return 0;
}
return length;
}
+
+void ZBackingFile::map(uintptr_t addr, size_t size, uintptr_t offset) const {
+ const void* const res = mmap((void*)addr, size, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_SHARED, _fd, offset);
+ if (res == MAP_FAILED) {
+ ZErrno err;
+ fatal("Failed to map memory (%s)", err.to_string());
+ }
+}
+
+void ZBackingFile::unmap(uintptr_t addr, size_t size) const {
+ // Note that we must keep the address space reservation intact and just detach
+ // the backing memory. For this reason we map a new anonymous, non-accessible
+ // and non-reserved page over the mapping instead of actually unmapping.
+ const void* const res = mmap((void*)addr, size, PROT_NONE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE, -1, 0);
+ if (res == MAP_FAILED) {
+ ZErrno err;
+ fatal("Failed to map memory (%s)", err.to_string());
+ }
+}
< prev index next >