Fri, 06 Jul 2018 18:50:13 +0000
8146115: Improve docker container detection and resource configuration usage
Reviewed-by: bobv, dbuck
1.1 --- a/src/os/aix/vm/os_aix.cpp Fri Jul 06 07:33:25 2018 -0700 1.2 +++ b/src/os/aix/vm/os_aix.cpp Fri Jul 06 18:50:13 2018 +0000 1.3 @@ -4008,6 +4008,16 @@ 1.4 }; 1.5 1.6 int os::active_processor_count() { 1.7 + // User has overridden the number of active processors 1.8 + if (ActiveProcessorCount > 0) { 1.9 + if (PrintActiveCpus) { 1.10 + tty->print_cr("active_processor_count: " 1.11 + "active processor count set by user : %d", 1.12 + ActiveProcessorCount); 1.13 + } 1.14 + return ActiveProcessorCount; 1.15 + } 1.16 + 1.17 int online_cpus = ::sysconf(_SC_NPROCESSORS_ONLN); 1.18 assert(online_cpus > 0 && online_cpus <= processor_count(), "sanity check"); 1.19 return online_cpus;
2.1 --- a/src/os/bsd/vm/os_bsd.cpp Fri Jul 06 07:33:25 2018 -0700 2.2 +++ b/src/os/bsd/vm/os_bsd.cpp Fri Jul 06 18:50:13 2018 +0000 2.3 @@ -3765,6 +3765,16 @@ 2.4 }; 2.5 2.6 int os::active_processor_count() { 2.7 + // User has overridden the number of active processors 2.8 + if (ActiveProcessorCount > 0) { 2.9 + if (PrintActiveCpus) { 2.10 + tty->print_cr("active_processor_count: " 2.11 + "active processor count set by user : %d", 2.12 + ActiveProcessorCount); 2.13 + } 2.14 + return ActiveProcessorCount; 2.15 + } 2.16 + 2.17 return _processor_count; 2.18 } 2.19
3.1 --- a/src/os/linux/vm/globals_linux.hpp Fri Jul 06 07:33:25 2018 -0700 3.2 +++ b/src/os/linux/vm/globals_linux.hpp Fri Jul 06 18:50:13 2018 +0000 3.3 @@ -1,5 +1,5 @@ 3.4 /* 3.5 - * Copyright (c) 2005, 2016, Oracle and/or its affiliates. All rights reserved. 3.6 + * Copyright (c) 2005, 2018, Oracle and/or its affiliates. All rights reserved. 3.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3.8 * 3.9 * This code is free software; you can redistribute it and/or modify it 3.10 @@ -49,8 +49,13 @@ 3.11 product(bool, UseSHM, false, \ 3.12 "Use SYSV shared memory for large pages") \ 3.13 \ 3.14 - diagnostic(bool, PrintActiveCpus, false, \ 3.15 - "Print the number of CPUs detected in os::active_processor_count") 3.16 + product(bool, UseContainerSupport, true, \ 3.17 + "Enable detection and runtime container configuration support") \ 3.18 + \ 3.19 + product(bool, PreferContainerQuotaForCPUCount, true, \ 3.20 + "Calculate the container CPU availability based on the value" \ 3.21 + " of quotas (if set), when true. Otherwise, use the CPU" \ 3.22 + " shares value, provided it is less than quota.") 3.23 3.24 // 3.25 // Defines Linux-specific default values. The flags are available on all
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 4.2 +++ b/src/os/linux/vm/osContainer_linux.cpp Fri Jul 06 18:50:13 2018 +0000 4.3 @@ -0,0 +1,680 @@ 4.4 +/* 4.5 + * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. 4.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4.7 + * 4.8 + * This code is free software; you can redistribute it and/or modify it 4.9 + * under the terms of the GNU General Public License version 2 only, as 4.10 + * published by the Free Software Foundation. 4.11 + * 4.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 4.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 4.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 4.15 + * version 2 for more details (a copy is included in the LICENSE file that 4.16 + * accompanied this code). 4.17 + * 4.18 + * You should have received a copy of the GNU General Public License version 4.19 + * 2 along with this work; if not, write to the Free Software Foundation, 4.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 4.21 + * 4.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 4.23 + * or visit www.oracle.com if you need additional information or have any 4.24 + * questions. 4.25 + * 4.26 + */ 4.27 + 4.28 +#include <string.h> 4.29 +#include <math.h> 4.30 +#include <errno.h> 4.31 +#include "utilities/globalDefinitions.hpp" 4.32 +#include "memory/allocation.hpp" 4.33 +#include "runtime/os.hpp" 4.34 +#include "osContainer_linux.hpp" 4.35 + 4.36 +#define PER_CPU_SHARES 1024 4.37 + 4.38 +bool OSContainer::_is_initialized = false; 4.39 +bool OSContainer::_is_containerized = false; 4.40 +julong _unlimited_memory; 4.41 + 4.42 +class CgroupSubsystem: CHeapObj<mtInternal> { 4.43 + friend class OSContainer; 4.44 + 4.45 + private: 4.46 + /* mountinfo contents */ 4.47 + char *_root; 4.48 + char *_mount_point; 4.49 + 4.50 + /* Constructed subsystem directory */ 4.51 + char *_path; 4.52 + 4.53 + public: 4.54 + CgroupSubsystem(char *root, char *mountpoint) { 4.55 + _root = os::strdup(root); 4.56 + _mount_point = os::strdup(mountpoint); 4.57 + _path = NULL; 4.58 + } 4.59 + 4.60 + /* 4.61 + * Set directory to subsystem specific files based 4.62 + * on the contents of the mountinfo and cgroup files. 4.63 + */ 4.64 + void set_subsystem_path(char *cgroup_path) { 4.65 + char buf[MAXPATHLEN+1]; 4.66 + if (_root != NULL && cgroup_path != NULL) { 4.67 + if (strcmp(_root, "/") == 0) { 4.68 + int buflen; 4.69 + strncpy(buf, _mount_point, MAXPATHLEN); 4.70 + buf[MAXPATHLEN-1] = '\0'; 4.71 + if (strcmp(cgroup_path,"/") != 0) { 4.72 + buflen = strlen(buf); 4.73 + if ((buflen + strlen(cgroup_path)) > (MAXPATHLEN-1)) { 4.74 + return; 4.75 + } 4.76 + strncat(buf, cgroup_path, MAXPATHLEN-buflen); 4.77 + buf[MAXPATHLEN-1] = '\0'; 4.78 + } 4.79 + _path = os::strdup(buf); 4.80 + } else { 4.81 + if (strcmp(_root, cgroup_path) == 0) { 4.82 + strncpy(buf, _mount_point, MAXPATHLEN); 4.83 + buf[MAXPATHLEN-1] = '\0'; 4.84 + _path = os::strdup(buf); 4.85 + } else { 4.86 + char *p = strstr(_root, cgroup_path); 4.87 + if (p != NULL && p == _root) { 4.88 + if (strlen(cgroup_path) > strlen(_root)) { 4.89 + int buflen; 4.90 + strncpy(buf, _mount_point, MAXPATHLEN); 4.91 + buf[MAXPATHLEN-1] = '\0'; 4.92 + buflen = strlen(buf); 4.93 + if ((buflen + strlen(cgroup_path)) > (MAXPATHLEN-1)) { 4.94 + return; 4.95 + } 4.96 + strncat(buf, cgroup_path + strlen(_root), MAXPATHLEN-buflen); 4.97 + buf[MAXPATHLEN-1] = '\0'; 4.98 + _path = os::strdup(buf); 4.99 + } 4.100 + } 4.101 + } 4.102 + } 4.103 + } 4.104 + } 4.105 + 4.106 + char *subsystem_path() { return _path; } 4.107 +}; 4.108 + 4.109 +CgroupSubsystem* memory = NULL; 4.110 +CgroupSubsystem* cpuset = NULL; 4.111 +CgroupSubsystem* cpu = NULL; 4.112 +CgroupSubsystem* cpuacct = NULL; 4.113 + 4.114 +typedef char * cptr; 4.115 + 4.116 +PRAGMA_DIAG_PUSH 4.117 +PRAGMA_FORMAT_NONLITERAL_IGNORED 4.118 +template <typename T> int subsystem_file_contents(CgroupSubsystem* c, 4.119 + const char *filename, 4.120 + const char *scan_fmt, 4.121 + T returnval) { 4.122 + FILE *fp = NULL; 4.123 + char *p; 4.124 + char file[MAXPATHLEN+1]; 4.125 + char buf[MAXPATHLEN+1]; 4.126 + 4.127 + if (c == NULL) { 4.128 + if (PrintContainerInfo) { 4.129 + tty->print_cr("subsystem_file_contents: CgroupSubsytem* is NULL"); 4.130 + } 4.131 + return OSCONTAINER_ERROR; 4.132 + } 4.133 + if (c->subsystem_path() == NULL) { 4.134 + if (PrintContainerInfo) { 4.135 + tty->print_cr("subsystem_file_contents: subsystem path is NULL"); 4.136 + } 4.137 + return OSCONTAINER_ERROR; 4.138 + } 4.139 + 4.140 + strncpy(file, c->subsystem_path(), MAXPATHLEN); 4.141 + file[MAXPATHLEN-1] = '\0'; 4.142 + int filelen = strlen(file); 4.143 + if ((filelen + strlen(filename)) > (MAXPATHLEN-1)) { 4.144 + if (PrintContainerInfo) { 4.145 + tty->print_cr("File path too long %s, %s", file, filename); 4.146 + } 4.147 + return OSCONTAINER_ERROR; 4.148 + } 4.149 + strncat(file, filename, MAXPATHLEN-filelen); 4.150 + if (PrintContainerInfo) { 4.151 + tty->print_cr("Path to %s is %s", filename, file); 4.152 + } 4.153 + fp = fopen(file, "r"); 4.154 + if (fp != NULL) { 4.155 + p = fgets(buf, MAXPATHLEN, fp); 4.156 + if (p != NULL) { 4.157 + int matched = sscanf(p, scan_fmt, returnval); 4.158 + if (matched == 1) { 4.159 + fclose(fp); 4.160 + return 0; 4.161 + } else { 4.162 + if (PrintContainerInfo) { 4.163 + tty->print_cr("Type %s not found in file %s", scan_fmt, file); 4.164 + } 4.165 + } 4.166 + } else { 4.167 + if (PrintContainerInfo) { 4.168 + tty->print_cr("Empty file %s", file); 4.169 + } 4.170 + } 4.171 + } else { 4.172 + if (PrintContainerInfo) { 4.173 + tty->print_cr("Open of file %s failed, %s", file, strerror(errno)); 4.174 + } 4.175 + } 4.176 + if (fp != NULL) 4.177 + fclose(fp); 4.178 + return OSCONTAINER_ERROR; 4.179 +} 4.180 +PRAGMA_DIAG_POP 4.181 + 4.182 +#define GET_CONTAINER_INFO(return_type, subsystem, filename, \ 4.183 + logstring, scan_fmt, variable) \ 4.184 + return_type variable; \ 4.185 +{ \ 4.186 + int err; \ 4.187 + err = subsystem_file_contents(subsystem, \ 4.188 + filename, \ 4.189 + scan_fmt, \ 4.190 + &variable); \ 4.191 + if (err != 0) \ 4.192 + return (return_type) OSCONTAINER_ERROR; \ 4.193 + \ 4.194 + if (PrintContainerInfo) \ 4.195 + tty->print_cr(logstring, variable); \ 4.196 +} 4.197 + 4.198 +#define GET_CONTAINER_INFO_CPTR(return_type, subsystem, filename, \ 4.199 + logstring, scan_fmt, variable, bufsize) \ 4.200 + char variable[bufsize]; \ 4.201 +{ \ 4.202 + int err; \ 4.203 + err = subsystem_file_contents(subsystem, \ 4.204 + filename, \ 4.205 + scan_fmt, \ 4.206 + variable); \ 4.207 + if (err != 0) \ 4.208 + return (return_type) NULL; \ 4.209 + \ 4.210 + if (PrintContainerInfo) \ 4.211 + tty->print_cr(logstring, variable); \ 4.212 +} 4.213 + 4.214 +/* init 4.215 + * 4.216 + * Initialize the container support and determine if 4.217 + * we are running under cgroup control. 4.218 + */ 4.219 +void OSContainer::init() { 4.220 + int mountid; 4.221 + int parentid; 4.222 + int major; 4.223 + int minor; 4.224 + FILE *mntinfo = NULL; 4.225 + FILE *cgroup = NULL; 4.226 + char buf[MAXPATHLEN+1]; 4.227 + char tmproot[MAXPATHLEN+1]; 4.228 + char tmpmount[MAXPATHLEN+1]; 4.229 + char tmpbase[MAXPATHLEN+1]; 4.230 + char *p; 4.231 + jlong mem_limit; 4.232 + 4.233 + assert(!_is_initialized, "Initializing OSContainer more than once"); 4.234 + 4.235 + _is_initialized = true; 4.236 + _is_containerized = false; 4.237 + 4.238 + _unlimited_memory = (LONG_MAX / os::vm_page_size()) * os::vm_page_size(); 4.239 + 4.240 + if (PrintContainerInfo) { 4.241 + tty->print_cr("OSContainer::init: Initializing Container Support"); 4.242 + } 4.243 + if (!UseContainerSupport) { 4.244 + if (PrintContainerInfo) { 4.245 + tty->print_cr("Container Support not enabled"); 4.246 + } 4.247 + return; 4.248 + } 4.249 + 4.250 + /* 4.251 + * Find the cgroup mount point for memory and cpuset 4.252 + * by reading /proc/self/mountinfo 4.253 + * 4.254 + * Example for docker: 4.255 + * 219 214 0:29 /docker/7208cebd00fa5f2e342b1094f7bed87fa25661471a4637118e65f1c995be8a34 /sys/fs/cgroup/memory ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory 4.256 + * 4.257 + * Example for host: 4.258 + * 34 28 0:29 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,memory 4.259 + */ 4.260 + mntinfo = fopen("/proc/self/mountinfo", "r"); 4.261 + if (mntinfo == NULL) { 4.262 + if (PrintContainerInfo) { 4.263 + tty->print_cr("Can't open /proc/self/mountinfo, %s", 4.264 + strerror(errno)); 4.265 + } 4.266 + return; 4.267 + } 4.268 + 4.269 + while ( (p = fgets(buf, MAXPATHLEN, mntinfo)) != NULL) { 4.270 + // Look for the filesystem type and see if it's cgroup 4.271 + char fstype[MAXPATHLEN+1]; 4.272 + fstype[0] = '\0'; 4.273 + char *s = strstr(p, " - "); 4.274 + if (s != NULL && 4.275 + sscanf(s, " - %s", fstype) == 1 && 4.276 + strcmp(fstype, "cgroup") == 0) { 4.277 + 4.278 + if (strstr(p, "memory") != NULL) { 4.279 + int matched = sscanf(p, "%d %d %d:%d %s %s", 4.280 + &mountid, 4.281 + &parentid, 4.282 + &major, 4.283 + &minor, 4.284 + tmproot, 4.285 + tmpmount); 4.286 + if (matched == 6) { 4.287 + memory = new CgroupSubsystem(tmproot, tmpmount); 4.288 + } 4.289 + else 4.290 + if (PrintContainerInfo) { 4.291 + tty->print_cr("Incompatible str containing cgroup and memory: %s", p); 4.292 + } 4.293 + } else if (strstr(p, "cpuset") != NULL) { 4.294 + int matched = sscanf(p, "%d %d %d:%d %s %s", 4.295 + &mountid, 4.296 + &parentid, 4.297 + &major, 4.298 + &minor, 4.299 + tmproot, 4.300 + tmpmount); 4.301 + if (matched == 6) { 4.302 + cpuset = new CgroupSubsystem(tmproot, tmpmount); 4.303 + } 4.304 + else { 4.305 + if (PrintContainerInfo) { 4.306 + tty->print_cr("Incompatible str containing cgroup and cpuset: %s", p); 4.307 + } 4.308 + } 4.309 + } else if (strstr(p, "cpu,cpuacct") != NULL || strstr(p, "cpuacct,cpu") != NULL) { 4.310 + int matched = sscanf(p, "%d %d %d:%d %s %s", 4.311 + &mountid, 4.312 + &parentid, 4.313 + &major, 4.314 + &minor, 4.315 + tmproot, 4.316 + tmpmount); 4.317 + if (matched == 6) { 4.318 + cpu = new CgroupSubsystem(tmproot, tmpmount); 4.319 + cpuacct = new CgroupSubsystem(tmproot, tmpmount); 4.320 + } 4.321 + else { 4.322 + if (PrintContainerInfo) { 4.323 + tty->print_cr("Incompatible str containing cgroup and cpu,cpuacct: %s", p); 4.324 + } 4.325 + } 4.326 + } else if (strstr(p, "cpuacct") != NULL) { 4.327 + int matched = sscanf(p, "%d %d %d:%d %s %s", 4.328 + &mountid, 4.329 + &parentid, 4.330 + &major, 4.331 + &minor, 4.332 + tmproot, 4.333 + tmpmount); 4.334 + if (matched == 6) { 4.335 + cpuacct = new CgroupSubsystem(tmproot, tmpmount); 4.336 + } 4.337 + else { 4.338 + if (PrintContainerInfo) { 4.339 + tty->print_cr("Incompatible str containing cgroup and cpuacct: %s", p); 4.340 + } 4.341 + } 4.342 + } else if (strstr(p, "cpu") != NULL) { 4.343 + int matched = sscanf(p, "%d %d %d:%d %s %s", 4.344 + &mountid, 4.345 + &parentid, 4.346 + &major, 4.347 + &minor, 4.348 + tmproot, 4.349 + tmpmount); 4.350 + if (matched == 6) { 4.351 + cpu = new CgroupSubsystem(tmproot, tmpmount); 4.352 + } 4.353 + else { 4.354 + if (PrintContainerInfo) { 4.355 + tty->print_cr("Incompatible str containing cgroup and cpu: %s", p); 4.356 + } 4.357 + } 4.358 + } 4.359 + } 4.360 + } 4.361 + 4.362 + fclose(mntinfo); 4.363 + 4.364 + if (memory == NULL) { 4.365 + if (PrintContainerInfo) { 4.366 + tty->print_cr("Required cgroup memory subsystem not found"); 4.367 + } 4.368 + return; 4.369 + } 4.370 + if (cpuset == NULL) { 4.371 + if (PrintContainerInfo) { 4.372 + tty->print_cr("Required cgroup cpuset subsystem not found"); 4.373 + } 4.374 + return; 4.375 + } 4.376 + if (cpu == NULL) { 4.377 + if (PrintContainerInfo) { 4.378 + tty->print_cr("Required cgroup cpu subsystem not found"); 4.379 + } 4.380 + return; 4.381 + } 4.382 + if (cpuacct == NULL) { 4.383 + if (PrintContainerInfo) { 4.384 + tty->print_cr("Required cgroup cpuacct subsystem not found"); 4.385 + } 4.386 + return; 4.387 + } 4.388 + 4.389 + /* 4.390 + * Read /proc/self/cgroup and map host mount point to 4.391 + * local one via /proc/self/mountinfo content above 4.392 + * 4.393 + * Docker example: 4.394 + * 5:memory:/docker/6558aed8fc662b194323ceab5b964f69cf36b3e8af877a14b80256e93aecb044 4.395 + * 4.396 + * Host example: 4.397 + * 5:memory:/user.slice 4.398 + * 4.399 + * Construct a path to the process specific memory and cpuset 4.400 + * cgroup directory. 4.401 + * 4.402 + * For a container running under Docker from memory example above 4.403 + * the paths would be: 4.404 + * 4.405 + * /sys/fs/cgroup/memory 4.406 + * 4.407 + * For a Host from memory example above the path would be: 4.408 + * 4.409 + * /sys/fs/cgroup/memory/user.slice 4.410 + * 4.411 + */ 4.412 + cgroup = fopen("/proc/self/cgroup", "r"); 4.413 + if (cgroup == NULL) { 4.414 + if (PrintContainerInfo) { 4.415 + tty->print_cr("Can't open /proc/self/cgroup, %s", 4.416 + strerror(errno)); 4.417 + } 4.418 + return; 4.419 + } 4.420 + 4.421 + while ( (p = fgets(buf, MAXPATHLEN, cgroup)) != NULL) { 4.422 + int cgno; 4.423 + int matched; 4.424 + char *controller; 4.425 + char *base; 4.426 + 4.427 + /* Skip cgroup number */ 4.428 + strsep(&p, ":"); 4.429 + /* Get controller and base */ 4.430 + controller = strsep(&p, ":"); 4.431 + base = strsep(&p, "\n"); 4.432 + 4.433 + if (controller != NULL) { 4.434 + if (strstr(controller, "memory") != NULL) { 4.435 + memory->set_subsystem_path(base); 4.436 + } else if (strstr(controller, "cpuset") != NULL) { 4.437 + cpuset->set_subsystem_path(base); 4.438 + } else if (strstr(controller, "cpu,cpuacct") != NULL || strstr(controller, "cpuacct,cpu") != NULL) { 4.439 + cpu->set_subsystem_path(base); 4.440 + cpuacct->set_subsystem_path(base); 4.441 + } else if (strstr(controller, "cpuacct") != NULL) { 4.442 + cpuacct->set_subsystem_path(base); 4.443 + } else if (strstr(controller, "cpu") != NULL) { 4.444 + cpu->set_subsystem_path(base); 4.445 + } 4.446 + } 4.447 + } 4.448 + 4.449 + fclose(cgroup); 4.450 + 4.451 + // We need to update the amount of physical memory now that 4.452 + // command line arguments have been processed. 4.453 + if ((mem_limit = memory_limit_in_bytes()) > 0) { 4.454 + os::Linux::set_physical_memory(mem_limit); 4.455 + } 4.456 + 4.457 + _is_containerized = true; 4.458 + 4.459 +} 4.460 + 4.461 +const char * OSContainer::container_type() { 4.462 + if (is_containerized()) { 4.463 + return "cgroupv1"; 4.464 + } else { 4.465 + return NULL; 4.466 + } 4.467 +} 4.468 + 4.469 + 4.470 +/* memory_limit_in_bytes 4.471 + * 4.472 + * Return the limit of available memory for this process. 4.473 + * 4.474 + * return: 4.475 + * memory limit in bytes or 4.476 + * -1 for unlimited 4.477 + * OSCONTAINER_ERROR for not supported 4.478 + */ 4.479 +jlong OSContainer::memory_limit_in_bytes() { 4.480 + GET_CONTAINER_INFO(julong, memory, "/memory.limit_in_bytes", 4.481 + "Memory Limit is: " JULONG_FORMAT, JULONG_FORMAT, memlimit); 4.482 + 4.483 + if (memlimit >= _unlimited_memory) { 4.484 + if (PrintContainerInfo) { 4.485 + tty->print_cr("Memory Limit is: Unlimited"); 4.486 + } 4.487 + return (jlong)-1; 4.488 + } 4.489 + else { 4.490 + return (jlong)memlimit; 4.491 + } 4.492 +} 4.493 + 4.494 +jlong OSContainer::memory_and_swap_limit_in_bytes() { 4.495 + GET_CONTAINER_INFO(julong, memory, "/memory.memsw.limit_in_bytes", 4.496 + "Memory and Swap Limit is: " JULONG_FORMAT, JULONG_FORMAT, memswlimit); 4.497 + if (memswlimit >= _unlimited_memory) { 4.498 + if (PrintContainerInfo) { 4.499 + tty->print_cr("Memory and Swap Limit is: Unlimited"); 4.500 + } 4.501 + return (jlong)-1; 4.502 + } else { 4.503 + return (jlong)memswlimit; 4.504 + } 4.505 +} 4.506 + 4.507 +jlong OSContainer::memory_soft_limit_in_bytes() { 4.508 + GET_CONTAINER_INFO(julong, memory, "/memory.soft_limit_in_bytes", 4.509 + "Memory Soft Limit is: " JULONG_FORMAT, JULONG_FORMAT, memsoftlimit); 4.510 + if (memsoftlimit >= _unlimited_memory) { 4.511 + if (PrintContainerInfo) { 4.512 + tty->print_cr("Memory Soft Limit is: Unlimited"); 4.513 + } 4.514 + return (jlong)-1; 4.515 + } else { 4.516 + return (jlong)memsoftlimit; 4.517 + } 4.518 +} 4.519 + 4.520 +/* memory_usage_in_bytes 4.521 + * 4.522 + * Return the amount of used memory for this process. 4.523 + * 4.524 + * return: 4.525 + * memory usage in bytes or 4.526 + * -1 for unlimited 4.527 + * OSCONTAINER_ERROR for not supported 4.528 + */ 4.529 +jlong OSContainer::memory_usage_in_bytes() { 4.530 + GET_CONTAINER_INFO(jlong, memory, "/memory.usage_in_bytes", 4.531 + "Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memusage); 4.532 + return memusage; 4.533 +} 4.534 + 4.535 +/* memory_max_usage_in_bytes 4.536 + * 4.537 + * Return the maximum amount of used memory for this process. 4.538 + * 4.539 + * return: 4.540 + * max memory usage in bytes or 4.541 + * OSCONTAINER_ERROR for not supported 4.542 + */ 4.543 +jlong OSContainer::memory_max_usage_in_bytes() { 4.544 + GET_CONTAINER_INFO(jlong, memory, "/memory.max_usage_in_bytes", 4.545 + "Maximum Memory Usage is: " JLONG_FORMAT, JLONG_FORMAT, memmaxusage); 4.546 + return memmaxusage; 4.547 +} 4.548 + 4.549 +/* active_processor_count 4.550 + * 4.551 + * Calculate an appropriate number of active processors for the 4.552 + * VM to use based on these three inputs. 4.553 + * 4.554 + * cpu affinity 4.555 + * cgroup cpu quota & cpu period 4.556 + * cgroup cpu shares 4.557 + * 4.558 + * Algorithm: 4.559 + * 4.560 + * Determine the number of available CPUs from sched_getaffinity 4.561 + * 4.562 + * If user specified a quota (quota != -1), calculate the number of 4.563 + * required CPUs by dividing quota by period. 4.564 + * 4.565 + * If shares are in effect (shares != -1), calculate the number 4.566 + * of CPUs required for the shares by dividing the share value 4.567 + * by PER_CPU_SHARES. 4.568 + * 4.569 + * All results of division are rounded up to the next whole number. 4.570 + * 4.571 + * If neither shares or quotas have been specified, return the 4.572 + * number of active processors in the system. 4.573 + * 4.574 + * If both shares and quotas have been specified, the results are 4.575 + * based on the flag PreferContainerQuotaForCPUCount. If true, 4.576 + * return the quota value. If false return the smallest value 4.577 + * between shares or quotas. 4.578 + * 4.579 + * If shares and/or quotas have been specified, the resulting number 4.580 + * returned will never exceed the number of active processors. 4.581 + * 4.582 + * return: 4.583 + * number of CPUs 4.584 + */ 4.585 +int OSContainer::active_processor_count() { 4.586 + int quota_count = 0, share_count = 0; 4.587 + int cpu_count, limit_count; 4.588 + int result; 4.589 + 4.590 + cpu_count = limit_count = os::Linux::active_processor_count(); 4.591 + int quota = cpu_quota(); 4.592 + int period = cpu_period(); 4.593 + int share = cpu_shares(); 4.594 + 4.595 + if (quota > -1 && period > 0) { 4.596 + quota_count = ceilf((float)quota / (float)period); 4.597 + if (PrintContainerInfo) { 4.598 + tty->print_cr("CPU Quota count based on quota/period: %d", quota_count); 4.599 + } 4.600 + } 4.601 + if (share > -1) { 4.602 + share_count = ceilf((float)share / (float)PER_CPU_SHARES); 4.603 + if (PrintContainerInfo) { 4.604 + tty->print_cr("CPU Share count based on shares: %d", share_count); 4.605 + } 4.606 + } 4.607 + 4.608 + // If both shares and quotas are setup results depend 4.609 + // on flag PreferContainerQuotaForCPUCount. 4.610 + // If true, limit CPU count to quota 4.611 + // If false, use minimum of shares and quotas 4.612 + if (quota_count !=0 && share_count != 0) { 4.613 + if (PreferContainerQuotaForCPUCount) { 4.614 + limit_count = quota_count; 4.615 + } else { 4.616 + limit_count = MIN2(quota_count, share_count); 4.617 + } 4.618 + } else if (quota_count != 0) { 4.619 + limit_count = quota_count; 4.620 + } else if (share_count != 0) { 4.621 + limit_count = share_count; 4.622 + } 4.623 + 4.624 + result = MIN2(cpu_count, limit_count); 4.625 + if (PrintContainerInfo) { 4.626 + tty->print_cr("OSContainer::active_processor_count: %d", result); 4.627 + } 4.628 + return result; 4.629 +} 4.630 + 4.631 +char * OSContainer::cpu_cpuset_cpus() { 4.632 + GET_CONTAINER_INFO_CPTR(cptr, cpuset, "/cpuset.cpus", 4.633 + "cpuset.cpus is: %s", "%1023s", cpus, 1024); 4.634 + return os::strdup(cpus); 4.635 +} 4.636 + 4.637 +char * OSContainer::cpu_cpuset_memory_nodes() { 4.638 + GET_CONTAINER_INFO_CPTR(cptr, cpuset, "/cpuset.mems", 4.639 + "cpuset.mems is: %s", "%1023s", mems, 1024); 4.640 + return os::strdup(mems); 4.641 +} 4.642 + 4.643 +/* cpu_quota 4.644 + * 4.645 + * Return the number of milliseconds per period 4.646 + * process is guaranteed to run. 4.647 + * 4.648 + * return: 4.649 + * quota time in milliseconds 4.650 + * -1 for no quota 4.651 + * OSCONTAINER_ERROR for not supported 4.652 + */ 4.653 +int OSContainer::cpu_quota() { 4.654 + GET_CONTAINER_INFO(int, cpu, "/cpu.cfs_quota_us", 4.655 + "CPU Quota is: %d", "%d", quota); 4.656 + return quota; 4.657 +} 4.658 + 4.659 +int OSContainer::cpu_period() { 4.660 + GET_CONTAINER_INFO(int, cpu, "/cpu.cfs_period_us", 4.661 + "CPU Period is: %d", "%d", period); 4.662 + return period; 4.663 +} 4.664 + 4.665 +/* cpu_shares 4.666 + * 4.667 + * Return the amount of cpu shares available to the process 4.668 + * 4.669 + * return: 4.670 + * Share number (typically a number relative to 1024) 4.671 + * (2048 typically expresses 2 CPUs worth of processing) 4.672 + * -1 for no share setup 4.673 + * OSCONTAINER_ERROR for not supported 4.674 + */ 4.675 +int OSContainer::cpu_shares() { 4.676 + GET_CONTAINER_INFO(int, cpu, "/cpu.shares", 4.677 + "CPU Shares is: %d", "%d", shares); 4.678 + // Convert 1024 to no shares setup 4.679 + if (shares == 1024) return -1; 4.680 + 4.681 + return shares; 4.682 +} 4.683 +
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 5.2 +++ b/src/os/linux/vm/osContainer_linux.hpp Fri Jul 06 18:50:13 2018 +0000 5.3 @@ -0,0 +1,68 @@ 5.4 +/* 5.5 + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 5.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5.7 + * 5.8 + * This code is free software; you can redistribute it and/or modify it 5.9 + * under the terms of the GNU General Public License version 2 only, as 5.10 + * published by the Free Software Foundation. 5.11 + * 5.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 5.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 5.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 5.15 + * version 2 for more details (a copy is included in the LICENSE file that 5.16 + * accompanied this code). 5.17 + * 5.18 + * You should have received a copy of the GNU General Public License version 5.19 + * 2 along with this work; if not, write to the Free Software Foundation, 5.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 5.21 + * 5.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 5.23 + * or visit www.oracle.com if you need additional information or have any 5.24 + * questions. 5.25 + * 5.26 + */ 5.27 + 5.28 +#ifndef OS_LINUX_VM_OSCONTAINER_LINUX_HPP 5.29 +#define OS_LINUX_VM_OSCONTAINER_LINUX_HPP 5.30 + 5.31 +#include "utilities/globalDefinitions.hpp" 5.32 +#include "utilities/macros.hpp" 5.33 +#include "memory/allocation.hpp" 5.34 + 5.35 +#define OSCONTAINER_ERROR (-2) 5.36 + 5.37 +class OSContainer: AllStatic { 5.38 + 5.39 + private: 5.40 + static bool _is_initialized; 5.41 + static bool _is_containerized; 5.42 + 5.43 + public: 5.44 + static void init(); 5.45 + static inline bool is_containerized(); 5.46 + static const char * container_type(); 5.47 + 5.48 + static jlong memory_limit_in_bytes(); 5.49 + static jlong memory_and_swap_limit_in_bytes(); 5.50 + static jlong memory_soft_limit_in_bytes(); 5.51 + static jlong memory_usage_in_bytes(); 5.52 + static jlong memory_max_usage_in_bytes(); 5.53 + 5.54 + static int active_processor_count(); 5.55 + 5.56 + static char * cpu_cpuset_cpus(); 5.57 + static char * cpu_cpuset_memory_nodes(); 5.58 + 5.59 + static int cpu_quota(); 5.60 + static int cpu_period(); 5.61 + 5.62 + static int cpu_shares(); 5.63 + 5.64 +}; 5.65 + 5.66 +inline bool OSContainer::is_containerized() { 5.67 + assert(_is_initialized, "OSContainer not initialized"); 5.68 + return _is_containerized; 5.69 +} 5.70 + 5.71 +#endif // OS_LINUX_VM_OSCONTAINER_LINUX_HPP
6.1 --- a/src/os/linux/vm/os_linux.cpp Fri Jul 06 07:33:25 2018 -0700 6.2 +++ b/src/os/linux/vm/os_linux.cpp Fri Jul 06 18:50:13 2018 +0000 6.3 @@ -37,6 +37,7 @@ 6.4 #include "mutex_linux.inline.hpp" 6.5 #include "oops/oop.inline.hpp" 6.6 #include "os_share_linux.hpp" 6.7 +#include "osContainer_linux.hpp" 6.8 #include "prims/jniFastGetField.hpp" 6.9 #include "prims/jvm.h" 6.10 #include "prims/jvm_misc.hpp" 6.11 @@ -179,13 +180,62 @@ 6.12 julong os::Linux::available_memory() { 6.13 // values in struct sysinfo are "unsigned long" 6.14 struct sysinfo si; 6.15 + julong avail_mem; 6.16 + 6.17 + if (OSContainer::is_containerized()) { 6.18 + jlong mem_limit, mem_usage; 6.19 + if ((mem_limit = OSContainer::memory_limit_in_bytes()) < 1) { 6.20 + if (PrintContainerInfo) { 6.21 + tty->print_cr("container memory limit %s: " JLONG_FORMAT ", using host value", 6.22 + mem_limit == OSCONTAINER_ERROR ? "failed" : "unlimited", mem_limit); 6.23 + } 6.24 + } 6.25 + 6.26 + if (mem_limit > 0 && (mem_usage = OSContainer::memory_usage_in_bytes()) < 1) { 6.27 + if (PrintContainerInfo) { 6.28 + tty->print_cr("container memory usage failed: " JLONG_FORMAT ", using host value", mem_usage); 6.29 + } 6.30 + } 6.31 + 6.32 + if (mem_limit > 0 && mem_usage > 0 ) { 6.33 + avail_mem = mem_limit > mem_usage ? (julong)mem_limit - (julong)mem_usage : 0; 6.34 + if (PrintContainerInfo) { 6.35 + tty->print_cr("available container memory: " JULONG_FORMAT, avail_mem); 6.36 + } 6.37 + return avail_mem; 6.38 + } 6.39 + } 6.40 + 6.41 sysinfo(&si); 6.42 - 6.43 - return (julong)si.freeram * si.mem_unit; 6.44 + avail_mem = (julong)si.freeram * si.mem_unit; 6.45 + if (Verbose) { 6.46 + tty->print_cr("available memory: " JULONG_FORMAT, avail_mem); 6.47 + } 6.48 + return avail_mem; 6.49 } 6.50 6.51 julong os::physical_memory() { 6.52 - return Linux::physical_memory(); 6.53 + jlong phys_mem = 0; 6.54 + if (OSContainer::is_containerized()) { 6.55 + jlong mem_limit; 6.56 + if ((mem_limit = OSContainer::memory_limit_in_bytes()) > 0) { 6.57 + if (PrintContainerInfo) { 6.58 + tty->print_cr("total container memory: " JLONG_FORMAT, mem_limit); 6.59 + } 6.60 + return mem_limit; 6.61 + } 6.62 + 6.63 + if (PrintContainerInfo) { 6.64 + tty->print_cr("container memory limit %s: " JLONG_FORMAT ", using host value", 6.65 + mem_limit == OSCONTAINER_ERROR ? "failed" : "unlimited", mem_limit); 6.66 + } 6.67 + } 6.68 + 6.69 + phys_mem = Linux::physical_memory(); 6.70 + if (Verbose) { 6.71 + tty->print_cr("total system memory: " JLONG_FORMAT, phys_mem); 6.72 + } 6.73 + return phys_mem; 6.74 } 6.75 6.76 //////////////////////////////////////////////////////////////////////////////// 6.77 @@ -2120,6 +2170,8 @@ 6.78 os::Posix::print_load_average(st); 6.79 6.80 os::Linux::print_full_memory_info(st); 6.81 + 6.82 + os::Linux::print_container_info(st); 6.83 } 6.84 6.85 // Try to identify popular distros. 6.86 @@ -2185,6 +2237,57 @@ 6.87 st->cr(); 6.88 } 6.89 6.90 +void os::Linux::print_container_info(outputStream* st) { 6.91 +if (!OSContainer::is_containerized()) { 6.92 + return; 6.93 + } 6.94 + 6.95 + st->print("container (cgroup) information:\n"); 6.96 + 6.97 + const char *p_ct = OSContainer::container_type(); 6.98 + st->print("container_type: %s\n", p_ct != NULL ? p_ct : "failed"); 6.99 + 6.100 + char *p = OSContainer::cpu_cpuset_cpus(); 6.101 + st->print("cpu_cpuset_cpus: %s\n", p != NULL ? p : "failed"); 6.102 + free(p); 6.103 + 6.104 + p = OSContainer::cpu_cpuset_memory_nodes(); 6.105 + st->print("cpu_memory_nodes: %s\n", p != NULL ? p : "failed"); 6.106 + free(p); 6.107 + 6.108 + int i = OSContainer::active_processor_count(); 6.109 + if (i > 0) { 6.110 + st->print("active_processor_count: %d\n", i); 6.111 + } else { 6.112 + st->print("active_processor_count: failed\n"); 6.113 + } 6.114 + 6.115 + i = OSContainer::cpu_quota(); 6.116 + st->print("cpu_quota: %d\n", i); 6.117 + 6.118 + i = OSContainer::cpu_period(); 6.119 + st->print("cpu_period: %d\n", i); 6.120 + 6.121 + i = OSContainer::cpu_shares(); 6.122 + st->print("cpu_shares: %d\n", i); 6.123 + 6.124 + jlong j = OSContainer::memory_limit_in_bytes(); 6.125 + st->print("memory_limit_in_bytes: " JLONG_FORMAT "\n", j); 6.126 + 6.127 + j = OSContainer::memory_and_swap_limit_in_bytes(); 6.128 + st->print("memory_and_swap_limit_in_bytes: " JLONG_FORMAT "\n", j); 6.129 + 6.130 + j = OSContainer::memory_soft_limit_in_bytes(); 6.131 + st->print("memory_soft_limit_in_bytes: " JLONG_FORMAT "\n", j); 6.132 + 6.133 + j = OSContainer::OSContainer::memory_usage_in_bytes(); 6.134 + st->print("memory_usage_in_bytes: " JLONG_FORMAT "\n", j); 6.135 + 6.136 + j = OSContainer::OSContainer::memory_max_usage_in_bytes(); 6.137 + st->print("memory_max_usage_in_bytes: " JLONG_FORMAT "\n", j); 6.138 + st->cr(); 6.139 +} 6.140 + 6.141 void os::print_memory_info(outputStream* st) { 6.142 6.143 st->print("Memory:"); 6.144 @@ -4956,6 +5059,10 @@ 6.145 } 6.146 } 6.147 6.148 +void os::pd_init_container_support() { 6.149 + OSContainer::init(); 6.150 +} 6.151 + 6.152 // this is called _after_ the global arguments have been parsed 6.153 jint os::init_2(void) 6.154 { 6.155 @@ -5136,7 +5243,7 @@ 6.156 // sched_getaffinity gives an accurate answer as it accounts for cpusets. 6.157 // If anything goes wrong we fallback to returning the number of online 6.158 // processors - which can be greater than the number available to the process. 6.159 -int os::active_processor_count() { 6.160 +int os::Linux::active_processor_count() { 6.161 cpu_set_t cpus; // can represent at most 1024 (CPU_SETSIZE) processors 6.162 int cpus_size = sizeof(cpu_set_t); 6.163 int cpu_count = 0; 6.164 @@ -5154,10 +5261,48 @@ 6.165 "which may exceed available processors", strerror(errno), cpu_count); 6.166 } 6.167 6.168 - assert(cpu_count > 0 && cpu_count <= processor_count(), "sanity check"); 6.169 + assert(cpu_count > 0 && cpu_count <= os::processor_count(), "sanity check"); 6.170 return cpu_count; 6.171 } 6.172 6.173 +// Determine the active processor count from one of 6.174 +// three different sources: 6.175 +// 6.176 +// 1. User option -XX:ActiveProcessorCount 6.177 +// 2. kernel os calls (sched_getaffinity or sysconf(_SC_NPROCESSORS_ONLN) 6.178 +// 3. extracted from cgroup cpu subsystem (shares and quotas) 6.179 +// 6.180 +// Option 1, if specified, will always override. 6.181 +// If the cgroup subsystem is active and configured, we 6.182 +// will return the min of the cgroup and option 2 results. 6.183 +// This is required since tools, such as numactl, that 6.184 +// alter cpu affinity do not update cgroup subsystem 6.185 +// cpuset configuration files. 6.186 +int os::active_processor_count() { 6.187 + // User has overridden the number of active processors 6.188 + if (ActiveProcessorCount > 0) { 6.189 + if (PrintActiveCpus) { 6.190 + tty->print_cr("active_processor_count: " 6.191 + "active processor count set by user : %d", 6.192 + ActiveProcessorCount); 6.193 + } 6.194 + return ActiveProcessorCount; 6.195 + } 6.196 + 6.197 + int active_cpus; 6.198 + if (OSContainer::is_containerized()) { 6.199 + active_cpus = OSContainer::active_processor_count(); 6.200 + if (PrintActiveCpus) { 6.201 + tty->print_cr("active_processor_count: determined by OSContainer: %d", 6.202 + active_cpus); 6.203 + } 6.204 + } else { 6.205 + active_cpus = os::Linux::active_processor_count(); 6.206 + } 6.207 + 6.208 + return active_cpus; 6.209 +} 6.210 + 6.211 void os::set_native_thread_name(const char *name) { 6.212 // Not yet implemented. 6.213 return;
7.1 --- a/src/os/linux/vm/os_linux.hpp Fri Jul 06 07:33:25 2018 -0700 7.2 +++ b/src/os/linux/vm/os_linux.hpp Fri Jul 06 18:50:13 2018 +0000 7.3 @@ -35,6 +35,7 @@ 7.4 7.5 class Linux { 7.6 friend class os; 7.7 + friend class OSContainer; 7.8 friend class TestReserveMemorySpecial; 7.9 7.10 // For signal-chaining 7.11 @@ -79,6 +80,9 @@ 7.12 7.13 static julong available_memory(); 7.14 static julong physical_memory() { return _physical_memory; } 7.15 + static void set_physical_memory(julong phys_mem) { _physical_memory = phys_mem; } 7.16 + static int active_processor_count(); 7.17 + 7.18 static void initialize_system_info(); 7.19 7.20 static int commit_memory_impl(char* addr, size_t bytes, bool exec); 7.21 @@ -116,6 +120,7 @@ 7.22 static bool release_memory_special_huge_tlbfs(char* base, size_t bytes); 7.23 7.24 static void print_full_memory_info(outputStream* st); 7.25 + static void print_container_info(outputStream* st); 7.26 static void print_distro_info(outputStream* st); 7.27 static void print_libversion_info(outputStream* st); 7.28
8.1 --- a/src/os/solaris/vm/os_solaris.cpp Fri Jul 06 07:33:25 2018 -0700 8.2 +++ b/src/os/solaris/vm/os_solaris.cpp Fri Jul 06 18:50:13 2018 +0000 8.3 @@ -359,6 +359,16 @@ 8.4 } 8.5 8.6 int os::active_processor_count() { 8.7 + // User has overridden the number of active processors 8.8 + if (ActiveProcessorCount > 0) { 8.9 + if (Verbose) { 8.10 + tty->print_cr("active_processor_count: " 8.11 + "active processor count set by user : %d", 8.12 + ActiveProcessorCount); 8.13 + } 8.14 + return ActiveProcessorCount; 8.15 + } 8.16 + 8.17 int online_cpus = sysconf(_SC_NPROCESSORS_ONLN); 8.18 pid_t pid = getpid(); 8.19 psetid_t pset = PS_NONE;
9.1 --- a/src/os/windows/vm/os_windows.cpp Fri Jul 06 07:33:25 2018 -0700 9.2 +++ b/src/os/windows/vm/os_windows.cpp Fri Jul 06 18:50:13 2018 +0000 9.3 @@ -716,6 +716,16 @@ 9.4 #endif 9.5 9.6 int os::active_processor_count() { 9.7 + // User has overridden the number of active processors 9.8 + if (ActiveProcessorCount > 0) { 9.9 + if (PrintActiveCpus) { 9.10 + tty->print_cr("active_processor_count: " 9.11 + "active processor count set by user : %d", 9.12 + ActiveProcessorCount); 9.13 + } 9.14 + return ActiveProcessorCount; 9.15 + } 9.16 + 9.17 DWORD_PTR lpProcessAffinityMask = 0; 9.18 DWORD_PTR lpSystemAffinityMask = 0; 9.19 int proc_count = processor_count();
10.1 --- a/src/share/vm/runtime/arguments.cpp Fri Jul 06 07:33:25 2018 -0700 10.2 +++ b/src/share/vm/runtime/arguments.cpp Fri Jul 06 18:50:13 2018 +0000 10.3 @@ -1,5 +1,5 @@ 10.4 /* 10.5 - * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. 10.6 + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. 10.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 10.8 * 10.9 * This code is free software; you can redistribute it and/or modify it 10.10 @@ -1801,20 +1801,34 @@ 10.11 } 10.12 } 10.13 10.14 + // Convert Fraction to Precentage values 10.15 + if (FLAG_IS_DEFAULT(MaxRAMPercentage) && 10.16 + !FLAG_IS_DEFAULT(MaxRAMFraction)) 10.17 + MaxRAMPercentage = 100.0 / MaxRAMFraction; 10.18 + 10.19 + if (FLAG_IS_DEFAULT(MinRAMPercentage) && 10.20 + !FLAG_IS_DEFAULT(MinRAMFraction)) 10.21 + MinRAMPercentage = 100.0 / MinRAMFraction; 10.22 + 10.23 + if (FLAG_IS_DEFAULT(InitialRAMPercentage) && 10.24 + !FLAG_IS_DEFAULT(InitialRAMFraction)) 10.25 + InitialRAMPercentage = 100.0 / InitialRAMFraction; 10.26 + 10.27 // If the maximum heap size has not been set with -Xmx, 10.28 // then set it as fraction of the size of physical memory, 10.29 // respecting the maximum and minimum sizes of the heap. 10.30 if (FLAG_IS_DEFAULT(MaxHeapSize)) { 10.31 - julong reasonable_max = phys_mem / MaxRAMFraction; 10.32 - 10.33 - if (phys_mem <= MaxHeapSize * MinRAMFraction) { 10.34 + julong reasonable_max = (julong)((phys_mem * MaxRAMPercentage) / 100); 10.35 + const julong reasonable_min = (julong)((phys_mem * MinRAMPercentage) / 100); 10.36 + if (reasonable_min < MaxHeapSize) { 10.37 // Small physical memory, so use a minimum fraction of it for the heap 10.38 - reasonable_max = phys_mem / MinRAMFraction; 10.39 + reasonable_max = reasonable_min; 10.40 } else { 10.41 // Not-small physical memory, so require a heap at least 10.42 // as large as MaxHeapSize 10.43 reasonable_max = MAX2(reasonable_max, (julong)MaxHeapSize); 10.44 } 10.45 + 10.46 if (!FLAG_IS_DEFAULT(ErgoHeapSizeLimit) && ErgoHeapSizeLimit != 0) { 10.47 // Limit the heap size to ErgoHeapSizeLimit 10.48 reasonable_max = MIN2(reasonable_max, (julong)ErgoHeapSizeLimit); 10.49 @@ -1856,7 +1870,7 @@ 10.50 reasonable_minimum = limit_by_allocatable_memory(reasonable_minimum); 10.51 10.52 if (InitialHeapSize == 0) { 10.53 - julong reasonable_initial = phys_mem / InitialRAMFraction; 10.54 + julong reasonable_initial = (julong)((phys_mem * InitialRAMPercentage) / 100); 10.55 10.56 reasonable_initial = MAX3(reasonable_initial, reasonable_minimum, (julong)min_heap_size()); 10.57 reasonable_initial = MIN2(reasonable_initial, (julong)MaxHeapSize); 10.58 @@ -1881,6 +1895,94 @@ 10.59 } 10.60 } 10.61 10.62 +// This option inspects the machine and attempts to set various 10.63 +// parameters to be optimal for long-running, memory allocation 10.64 +// intensive jobs. It is intended for machines with large 10.65 +// amounts of cpu and memory. 10.66 +jint Arguments::set_aggressive_heap_flags() { 10.67 + // initHeapSize is needed since _initial_heap_size is 4 bytes on a 32 bit 10.68 + // VM, but we may not be able to represent the total physical memory 10.69 + // available (like having 8gb of memory on a box but using a 32bit VM). 10.70 + // Thus, we need to make sure we're using a julong for intermediate 10.71 + // calculations. 10.72 + julong initHeapSize; 10.73 + julong total_memory = os::physical_memory(); 10.74 + 10.75 + if (total_memory < (julong) 256 * M) { 10.76 + jio_fprintf(defaultStream::error_stream(), 10.77 + "You need at least 256mb of memory to use -XX:+AggressiveHeap\n"); 10.78 + vm_exit(1); 10.79 + } 10.80 + 10.81 + // The heap size is half of available memory, or (at most) 10.82 + // all of possible memory less 160mb (leaving room for the OS 10.83 + // when using ISM). This is the maximum; because adaptive sizing 10.84 + // is turned on below, the actual space used may be smaller. 10.85 + 10.86 + initHeapSize = MIN2(total_memory / (julong) 2, 10.87 + total_memory - (julong) 160 * M); 10.88 + 10.89 + initHeapSize = limit_by_allocatable_memory(initHeapSize); 10.90 + 10.91 + if (FLAG_IS_DEFAULT(MaxHeapSize)) { 10.92 + FLAG_SET_CMDLINE(uintx, MaxHeapSize, initHeapSize); 10.93 + FLAG_SET_CMDLINE(uintx, InitialHeapSize, initHeapSize); 10.94 + // Currently the minimum size and the initial heap sizes are the same. 10.95 + set_min_heap_size(initHeapSize); 10.96 + } 10.97 + if (FLAG_IS_DEFAULT(NewSize)) { 10.98 + // Make the young generation 3/8ths of the total heap. 10.99 + FLAG_SET_CMDLINE(uintx, NewSize, 10.100 + ((julong) MaxHeapSize / (julong) 8) * (julong) 3); 10.101 + FLAG_SET_CMDLINE(uintx, MaxNewSize, NewSize); 10.102 + } 10.103 + 10.104 +#ifndef _ALLBSD_SOURCE // UseLargePages is not yet supported on BSD. 10.105 + FLAG_SET_DEFAULT(UseLargePages, true); 10.106 +#endif 10.107 + 10.108 + // Increase some data structure sizes for efficiency 10.109 + FLAG_SET_CMDLINE(uintx, BaseFootPrintEstimate, MaxHeapSize); 10.110 + FLAG_SET_CMDLINE(bool, ResizeTLAB, false); 10.111 + FLAG_SET_CMDLINE(uintx, TLABSize, 256 * K); 10.112 + 10.113 + // See the OldPLABSize comment below, but replace 'after promotion' 10.114 + // with 'after copying'. YoungPLABSize is the size of the survivor 10.115 + // space per-gc-thread buffers. The default is 4kw. 10.116 + FLAG_SET_CMDLINE(uintx, YoungPLABSize, 256 * K); // Note: this is in words 10.117 + 10.118 + // OldPLABSize is the size of the buffers in the old gen that 10.119 + // UseParallelGC uses to promote live data that doesn't fit in the 10.120 + // survivor spaces. At any given time, there's one for each gc thread. 10.121 + // The default size is 1kw. These buffers are rarely used, since the 10.122 + // survivor spaces are usually big enough. For specjbb, however, there 10.123 + // are occasions when there's lots of live data in the young gen 10.124 + // and we end up promoting some of it. We don't have a definite 10.125 + // explanation for why bumping OldPLABSize helps, but the theory 10.126 + // is that a bigger PLAB results in retaining something like the 10.127 + // original allocation order after promotion, which improves mutator 10.128 + // locality. A minor effect may be that larger PLABs reduce the 10.129 + // number of PLAB allocation events during gc. The value of 8kw 10.130 + // was arrived at by experimenting with specjbb. 10.131 + FLAG_SET_CMDLINE(uintx, OldPLABSize, 8 * K); // Note: this is in words 10.132 + 10.133 + // Enable parallel GC and adaptive generation sizing 10.134 + FLAG_SET_CMDLINE(bool, UseParallelGC, true); 10.135 + 10.136 + // Encourage steady state memory management 10.137 + FLAG_SET_CMDLINE(uintx, ThresholdTolerance, 100); 10.138 + 10.139 + // This appears to improve mutator locality 10.140 + FLAG_SET_CMDLINE(bool, ScavengeBeforeFullGC, false); 10.141 + 10.142 + // Get around early Solaris scheduling bug 10.143 + // (affinity vs other jobs on system) 10.144 + // but disallow DR and offlining (5008695). 10.145 + FLAG_SET_CMDLINE(bool, BindGCTaskThreadsToCPUs, true); 10.146 + 10.147 + return JNI_OK; 10.148 +} 10.149 + 10.150 // This must be called after ergonomics because we want bytecode rewriting 10.151 // if the server compiler is used, or if UseSharedSpaces is disabled. 10.152 void Arguments::set_bytecode_flags() { 10.153 @@ -2644,6 +2746,14 @@ 10.154 return result; 10.155 } 10.156 10.157 + // We need to ensure processor and memory resources have been properly 10.158 + // configured - which may rely on arguments we just processed - before 10.159 + // doing the final argument processing. Any argument processing that 10.160 + // needs to know about processor and memory resources must occur after 10.161 + // this point. 10.162 + 10.163 + os::init_container_support(); 10.164 + 10.165 // Do final processing now that all arguments have been parsed 10.166 result = finalize_vm_init_args(&scp, scp_assembly_required); 10.167 if (result != JNI_OK) { 10.168 @@ -3117,94 +3227,6 @@ 10.169 _exit_hook = CAST_TO_FN_PTR(exit_hook_t, option->extraInfo); 10.170 } else if (match_option(option, "abort", &tail)) { 10.171 _abort_hook = CAST_TO_FN_PTR(abort_hook_t, option->extraInfo); 10.172 - // -XX:+AggressiveHeap 10.173 - } else if (match_option(option, "-XX:+AggressiveHeap", &tail)) { 10.174 - 10.175 - // This option inspects the machine and attempts to set various 10.176 - // parameters to be optimal for long-running, memory allocation 10.177 - // intensive jobs. It is intended for machines with large 10.178 - // amounts of cpu and memory. 10.179 - 10.180 - // initHeapSize is needed since _initial_heap_size is 4 bytes on a 32 bit 10.181 - // VM, but we may not be able to represent the total physical memory 10.182 - // available (like having 8gb of memory on a box but using a 32bit VM). 10.183 - // Thus, we need to make sure we're using a julong for intermediate 10.184 - // calculations. 10.185 - julong initHeapSize; 10.186 - julong total_memory = os::physical_memory(); 10.187 - 10.188 - if (total_memory < (julong)256*M) { 10.189 - jio_fprintf(defaultStream::error_stream(), 10.190 - "You need at least 256mb of memory to use -XX:+AggressiveHeap\n"); 10.191 - vm_exit(1); 10.192 - } 10.193 - 10.194 - // The heap size is half of available memory, or (at most) 10.195 - // all of possible memory less 160mb (leaving room for the OS 10.196 - // when using ISM). This is the maximum; because adaptive sizing 10.197 - // is turned on below, the actual space used may be smaller. 10.198 - 10.199 - initHeapSize = MIN2(total_memory / (julong)2, 10.200 - total_memory - (julong)160*M); 10.201 - 10.202 - initHeapSize = limit_by_allocatable_memory(initHeapSize); 10.203 - 10.204 - if (FLAG_IS_DEFAULT(MaxHeapSize)) { 10.205 - FLAG_SET_CMDLINE(uintx, MaxHeapSize, initHeapSize); 10.206 - FLAG_SET_CMDLINE(uintx, InitialHeapSize, initHeapSize); 10.207 - // Currently the minimum size and the initial heap sizes are the same. 10.208 - set_min_heap_size(initHeapSize); 10.209 - } 10.210 - if (FLAG_IS_DEFAULT(NewSize)) { 10.211 - // Make the young generation 3/8ths of the total heap. 10.212 - FLAG_SET_CMDLINE(uintx, NewSize, 10.213 - ((julong)MaxHeapSize / (julong)8) * (julong)3); 10.214 - FLAG_SET_CMDLINE(uintx, MaxNewSize, NewSize); 10.215 - } 10.216 - 10.217 -#ifndef _ALLBSD_SOURCE // UseLargePages is not yet supported on BSD. 10.218 - FLAG_SET_DEFAULT(UseLargePages, true); 10.219 -#endif 10.220 - 10.221 - // Increase some data structure sizes for efficiency 10.222 - FLAG_SET_CMDLINE(uintx, BaseFootPrintEstimate, MaxHeapSize); 10.223 - FLAG_SET_CMDLINE(bool, ResizeTLAB, false); 10.224 - FLAG_SET_CMDLINE(uintx, TLABSize, 256*K); 10.225 - 10.226 - // See the OldPLABSize comment below, but replace 'after promotion' 10.227 - // with 'after copying'. YoungPLABSize is the size of the survivor 10.228 - // space per-gc-thread buffers. The default is 4kw. 10.229 - FLAG_SET_CMDLINE(uintx, YoungPLABSize, 256*K); // Note: this is in words 10.230 - 10.231 - // OldPLABSize is the size of the buffers in the old gen that 10.232 - // UseParallelGC uses to promote live data that doesn't fit in the 10.233 - // survivor spaces. At any given time, there's one for each gc thread. 10.234 - // The default size is 1kw. These buffers are rarely used, since the 10.235 - // survivor spaces are usually big enough. For specjbb, however, there 10.236 - // are occasions when there's lots of live data in the young gen 10.237 - // and we end up promoting some of it. We don't have a definite 10.238 - // explanation for why bumping OldPLABSize helps, but the theory 10.239 - // is that a bigger PLAB results in retaining something like the 10.240 - // original allocation order after promotion, which improves mutator 10.241 - // locality. A minor effect may be that larger PLABs reduce the 10.242 - // number of PLAB allocation events during gc. The value of 8kw 10.243 - // was arrived at by experimenting with specjbb. 10.244 - FLAG_SET_CMDLINE(uintx, OldPLABSize, 8*K); // Note: this is in words 10.245 - 10.246 - // Enable parallel GC and adaptive generation sizing 10.247 - FLAG_SET_CMDLINE(bool, UseParallelGC, true); 10.248 - 10.249 - // Encourage steady state memory management 10.250 - FLAG_SET_CMDLINE(uintx, ThresholdTolerance, 100); 10.251 - 10.252 - // This appears to improve mutator locality 10.253 - FLAG_SET_CMDLINE(bool, ScavengeBeforeFullGC, false); 10.254 - 10.255 - // Get around early Solaris scheduling bug 10.256 - // (affinity vs other jobs on system) 10.257 - // but disallow DR and offlining (5008695). 10.258 - FLAG_SET_CMDLINE(bool, BindGCTaskThreadsToCPUs, true); 10.259 - 10.260 } else if (match_option(option, "-XX:+NeverTenure", &tail)) { 10.261 // The last option must always win. 10.262 FLAG_SET_CMDLINE(bool, AlwaysTenure, false); 10.263 @@ -3605,6 +3627,15 @@ 10.264 return JNI_ERR; 10.265 } 10.266 10.267 + // This must be done after all arguments have been processed 10.268 + // and the container support has been initialized since AggressiveHeap 10.269 + // relies on the amount of total memory available. 10.270 + if (AggressiveHeap) { 10.271 + jint result = set_aggressive_heap_flags(); 10.272 + if (result != JNI_OK) { 10.273 + return result; 10.274 + } 10.275 + } 10.276 // This must be done after all arguments have been processed. 10.277 // java_compiler() true means set to "NONE" or empty. 10.278 if (java_compiler() && !xdebug_mode()) {
11.1 --- a/src/share/vm/runtime/arguments.hpp Fri Jul 06 07:33:25 2018 -0700 11.2 +++ b/src/share/vm/runtime/arguments.hpp Fri Jul 06 18:50:13 2018 +0000 11.3 @@ -1,5 +1,5 @@ 11.4 /* 11.5 - * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. 11.6 + * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved. 11.7 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 11.8 * 11.9 * This code is free software; you can redistribute it and/or modify it 11.10 @@ -365,6 +365,8 @@ 11.11 // Aggressive optimization flags. 11.12 static void set_aggressive_opts_flags(); 11.13 11.14 + static jint set_aggressive_heap_flags(); 11.15 + 11.16 // Argument parsing 11.17 static void do_pd_flag_adjustments(); 11.18 static bool parse_argument(const char* arg, Flag::Flags origin);
12.1 --- a/src/share/vm/runtime/globals.hpp Fri Jul 06 07:33:25 2018 -0700 12.2 +++ b/src/share/vm/runtime/globals.hpp Fri Jul 06 18:50:13 2018 +0000 12.3 @@ -2068,13 +2068,23 @@ 12.4 product_pd(uint64_t, MaxRAM, \ 12.5 "Real memory size (in bytes) used to set maximum heap size") \ 12.6 \ 12.7 + product(bool, AggressiveHeap, false, \ 12.8 + "Optimize heap options for long-running memory intensive apps") \ 12.9 + \ 12.10 product(uintx, ErgoHeapSizeLimit, 0, \ 12.11 "Maximum ergonomically set heap size (in bytes); zero means use " \ 12.12 - "MaxRAM / MaxRAMFraction") \ 12.13 + "MaxRAM * MaxRAMPercentage / 100") \ 12.14 \ 12.15 experimental(bool, UseCGroupMemoryLimitForHeap, false, \ 12.16 "Use CGroup memory limit as physical memory limit for heap " \ 12.17 - "sizing") \ 12.18 + "sizing" \ 12.19 + "Deprecated, replaced by container support") \ 12.20 + \ 12.21 + diagnostic(bool, PrintContainerInfo, false, \ 12.22 + "Print container related information") \ 12.23 + \ 12.24 + diagnostic(bool, PrintActiveCpus, false, \ 12.25 + "Print the number of CPUs detected in os::active_processor_count") \ 12.26 \ 12.27 product(uintx, MaxRAMFraction, 4, \ 12.28 "Maximum fraction (1/n) of real memory used for maximum heap " \ 12.29 @@ -2091,6 +2101,19 @@ 12.30 product(uintx, InitialRAMFraction, 64, \ 12.31 "Fraction (1/n) of real memory used for initial heap size") \ 12.32 \ 12.33 + product(double, MaxRAMPercentage, 25.0, \ 12.34 + "Maximum percentage of real memory used for maximum heap size") \ 12.35 + \ 12.36 + product(double, MinRAMPercentage, 50.0, \ 12.37 + "Minimum percentage of real memory used for maximum heap" \ 12.38 + "size on systems with small physical memory size") \ 12.39 + \ 12.40 + product(double, InitialRAMPercentage, 1.5625, \ 12.41 + "Percentage of real memory used for initial heap size") \ 12.42 + \ 12.43 + product(intx, ActiveProcessorCount, -1, \ 12.44 + "Specify the CPU count the VM should use and report as active") \ 12.45 + \ 12.46 develop(uintx, MaxVirtMemFraction, 2, \ 12.47 "Maximum fraction (1/n) of virtual memory used for ergonomically "\ 12.48 "determining maximum heap size") \
13.1 --- a/src/share/vm/runtime/os.hpp Fri Jul 06 07:33:25 2018 -0700 13.2 +++ b/src/share/vm/runtime/os.hpp Fri Jul 06 18:50:13 2018 +0000 13.3 @@ -152,8 +152,16 @@ 13.4 static size_t page_size_for_region(size_t region_size, size_t min_pages, bool must_be_aligned); 13.5 13.6 static void initialize_initial_active_processor_count(); 13.7 + 13.8 + LINUX_ONLY(static void pd_init_container_support();) 13.9 + 13.10 public: 13.11 static void init(void); // Called before command line parsing 13.12 + 13.13 + static void init_container_support() { // Called during command line parsing. 13.14 + LINUX_ONLY(pd_init_container_support();) 13.15 + } 13.16 + 13.17 static void init_before_ergo(void); // Called after command line parsing 13.18 // before VM ergonomics processing. 13.19 static jint init_2(void); // Called after command line parsing
14.1 --- a/src/share/vm/runtime/thread.cpp Fri Jul 06 07:33:25 2018 -0700 14.2 +++ b/src/share/vm/runtime/thread.cpp Fri Jul 06 18:50:13 2018 +0000 14.3 @@ -3343,6 +3343,7 @@ 14.4 Arguments::init_version_specific_system_properties(); 14.5 14.6 // Parse arguments 14.7 + // Note: this internally calls os::init_container_support() 14.8 jint parse_result = Arguments::parse(args); 14.9 if (parse_result != JNI_OK) return parse_result; 14.10