��� Docker�� ����������� JVM���������������������������

Author������������

Docker������������������

  • Namespace

  • UnionFS

  • Cgroups

Cgroups ��������� Linux ������������������������������������������������������������������CPU������������������ I/O������������������������������������������Cgroups ��������������������������� CPU ���������������������������.

���������������������������������������������������������,Docker ���������������������������������������������������������������������������������������������������������������������������.

Namespace ��� Linux ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Namespace ������������������ ID��������������������� ID������������������������������������������������������������.

Cgroups���control groups���������������

  • subsystem���������������

  • hierarchy���������������

  • cgroup���������������

��������������������������������������������������������������������������������������������������� CPU ������������������ CPU ������������������������������������ CPU ���������������������������������������.

��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� c1������������ CPU ������������ 1 ��������������������������������� c2 ������������������ CPU ������ 1 ������������������������������ 2G��������� c2 ��������������������� c1��������������������� CPU ������.

������������������������������������������������������������������������������������������������������������������������������������CPU ��������������������� CPU ���������������.

subsystem

Linux ������������������������������OS���CentOS 7.6�� �� ��kernel��� 4.19.0-9.el7.ucloud.x86_64���

��

������ mount ���������������������������������������������cgroups���������

# sudo mount -t cgroup
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)

  • cpu ���������

��� cpu ������������������ cgroup���

# mkdir /sys/fs/cgroup/cpu/test

������������������������������������������������������ ������������������������ CPU ������������������������cpu.cfs_quota_us ��������������������������������������� CPU ������������.

tasks ������������������������������������ID.

-rw-r--r-- 1 root root 0 Aug  9 00:51 cgroup.clone_children
-rw-r--r-- 1 root root 0 Aug  9 00:51 cgroup.procs
-r--r--r-- 1 root root 0 Aug  9 00:51 cpuacct.stat
-rw-r--r-- 1 root root 0 Aug  9 00:51 cpuacct.usage
-r--r--r-- 1 root root 0 Aug  9 00:51 cpuacct.usage_all
-r--r--r-- 1 root root 0 Aug  9 00:51 cpuacct.usage_percpu
-r--r--r-- 1 root root 0 Aug  9 00:51 cpuacct.usage_percpu_sys
-r--r--r-- 1 root root 0 Aug  9 00:51 cpuacct.usage_percpu_user
-r--r--r-- 1 root root 0 Aug  9 00:51 cpuacct.usage_sys
-r--r--r-- 1 root root 0 Aug  9 00:51 cpuacct.usage_user
-rw-r--r-- 1 root root 0 Aug  9 00:51 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 Aug  9 00:51 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 Aug  9 00:51 cpu.rt_period_us
-rw-r--r-- 1 root root 0 Aug  9 00:51 cpu.rt_runtime_us
-rw-r--r-- 1 root root 0 Aug  9 00:51 cpu.shares
-r--r--r-- 1 root root 0 Aug  9 00:51 cpu.stat
-rw-r--r-- 1 root root 0 Aug  9 00:51 notify_on_release
-rw-r--r-- 1 root root 0 Aug  9 00:51 tasks
  • memory ���������

��� memory ������������������ cgroup���

# mkdir /sys/fs/cgroup/memory/test

������������������������������������������������������ ������������������������ Memory ������������������������memory.limit_in_bytes ������������������������������.

tasks ���������������������������������������ID.

-rw-r--r-- 1 root root 0 Aug  9 00:55 cgroup.clone_children
--w--w--w- 1 root root 0 Aug  9 00:55 cgroup.event_control
-rw-r--r-- 1 root root 0 Aug  9 00:55 cgroup.procs
-rw-r--r-- 1 root root 0 Aug  9 00:55 memory.failcnt
--w------- 1 root root 0 Aug  9 00:55 memory.force_empty
-rw-r--r-- 1 root root 0 Aug  9 00:55 memory.kmem.failcnt
-rw-r--r-- 1 root root 0 Aug  9 00:55 memory.kmem.limit_in_bytes
-rw-r--r-- 1 root root 0 Aug  9 00:55 memory.kmem.max_usage_in_bytes
-r--r--r-- 1 root root 0 Aug  9 00:55 memory.kmem.slabinfo
-rw-r--r-- 1 root root 0 Aug  9 00:55 memory.kmem.tcp.failcnt
-rw-r--r-- 1 root root 0 Aug  9 00:55 memory.kmem.tcp.limit_in_bytes
-rw-r--r-- 1 root root 0 Aug  9 00:55 memory.kmem.tcp.max_usage_in_bytes
-r--r--r-- 1 root root 0 Aug  9 00:55 memory.kmem.tcp.usage_in_bytes
-r--r--r-- 1 root root 0 Aug  9 00:55 memory.kmem.usage_in_bytes
-rw-r--r-- 1 root root 0 Aug  9 00:55 memory.limit_in_bytes
-rw-r--r-- 1 root root 0 Aug  9 00:55 memory.max_usage_in_bytes
-rw-r--r-- 1 root root 0 Aug  9 00:55 memory.memsw.failcnt
-rw-r--r-- 1 root root 0 Aug  9 00:55 memory.memsw.limit_in_bytes
-rw-r--r-- 1 root root 0 Aug  9 00:55 memory.memsw.max_usage_in_bytes
-r--r--r-- 1 root root 0 Aug  9 00:55 memory.memsw.usage_in_bytes
-rw-r--r-- 1 root root 0 Aug  9 00:55 memory.move_charge_at_immigrate
-r--r--r-- 1 root root 0 Aug  9 00:55 memory.numa_stat
-rw-r--r-- 1 root root 0 Aug  9 00:55 memory.oom_control
---------- 1 root root 0 Aug  9 00:55 memory.pressure_level
-rw-r--r-- 1 root root 0 Aug  9 00:55 memory.soft_limit_in_bytes
-r--r--r-- 1 root root 0 Aug  9 00:55 memory.stat
-rw-r--r-- 1 root root 0 Aug  9 00:55 memory.swappiness
-r--r--r-- 1 root root 0 Aug  9 00:55 memory.usage_in_bytes
-rw-r--r-- 1 root root 0 Aug  9 00:55 memory.use_hierarchy
-rw-r--r-- 1 root root 0 Aug  9 00:55 notify_on_release
-rw-r--r-- 1 root root 0 Aug  9 00:55 tasks
  • Docker ������������ cgroups

������������������������������������������1G���

Docker ������������������������������������������������������������������ /sys/fs/cgroup/memory ������ /sys/fs/cgroup/cpu

-rw-r--r--  1 root root 0 Aug  9 00:23 cgroup.clone_children
--w--w--w-  1 root root 0 Aug  9 00:23 cgroup.event_control
-rw-r--r--  1 root root 0 Aug  9 00:23 cgroup.procs
-r--r--r--  1 root root 0 Aug  9 00:23 cgroup.sane_behavior
drwxr-xr-x  3 root root 0 Aug  9 00:23 docker
-rw-r--r--  1 root root 0 Aug  9 00:23 memory.failcnt
--w-------  1 root root 0 Aug  9 00:23 memory.force_empty
docker run -it -m=1g xxx
-rw-r--r--  1 root root 0 Aug  9 00:24 cpu.shares
-r--r--r--  1 root root 0 Aug  9 00:24 cpu.stat
drwxr-xr-x  3 root root 0 Aug  9 00:24 docker
-rw-r--r--  1 root root 0 Aug  9 00:24 notify_on_release
-rw-r--r--  1 root root 0 Aug  9 00:24 release_agent

������docker������������

���������������������������������ID���������������������������������������������������

-rw-r--r-- 1 root root 0 Aug  9 01:07 cgroup.clone_children
--w--w--w- 1 root root 0 Aug  9 00:22 cgroup.event_control
-rw-r--r-- 1 root root 0 Aug  9 00:22 cgroup.procs
-rw-r--r-- 1 root root 0 Aug  9 00:22 memory.failcnt
--w------- 1 root root 0 Aug  9 01:07 memory.force_empty
-rw-r--r-- 1 root root 0 Aug  9 01:07 memory.kmem.failcnt
-rw-r--r-- 1 root root 0 Aug  9 01:07 memory.kmem.limit_in_bytes
-rw-r--r-- 1 root root 0 Aug  9 01:07 memory.kmem.max_usage_in_bytes
-r--r--r-- 1 root root 0 Aug  9 01:07 memory.kmem.slabinfo
-rw-r--r-- 1 root root 0 Aug  9 01:07 memory.kmem.tcp.failcnt
-rw-r--r-- 1 root root 0 Aug  9 01:07 memory.kmem.tcp.limit_in_bytes
-rw-r--r-- 1 root root 0 Aug  9 01:07 memory.kmem.tcp.max_usage_in_bytes
-r--r--r-- 1 root root 0 Aug  9 01:07 memory.kmem.tcp.usage_in_bytes
-r--r--r-- 1 root root 0 Aug  9 01:07 memory.kmem.usage_in_bytes
-rw-r--r-- 1 root root 0 Aug  9 00:22 memory.limit_in_bytes
-rw-r--r-- 1 root root 0 Aug  9 00:22 memory.max_usage_in_bytes
-rw-r--r-- 1 root root 0 Aug  9 01:07 memory.memsw.failcnt
-rw-r--r-- 1 root root 0 Aug  9 00:22 memory.memsw.limit_in_bytes
-rw-r--r-- 1 root root 0 Aug  9 01:07 memory.memsw.max_usage_in_bytes
-r--r--r-- 1 root root 0 Aug  9 01:07 memory.memsw.usage_in_bytes
-rw-r--r-- 1 root root 0 Aug  9 01:07 memory.move_charge_at_immigrate
-r--r--r-- 1 root root 0 Aug  9 01:07 memory.numa_stat
-rw-r--r-- 1 root root 0 Aug  9 00:22 memory.oom_control
---------- 1 root root 0 Aug  9 01:07 memory.pressure_level
-rw-r--r-- 1 root root 0 Aug  9 01:07 memory.soft_limit_in_bytes
-r--r--r-- 1 root root 0 Aug  9 01:07 memory.stat
-rw-r--r-- 1 root root 0 Aug  9 01:07 memory.swappiness
-r--r--r-- 1 root root 0 Aug  9 00:22 memory.usage_in_bytes
-rw-r--r-- 1 root root 0 Aug  9 01:07 memory.use_hierarchy
-rw-r--r-- 1 root root 0 Aug  9 01:07 notify_on_release
-rw-r--r-- 1 root root 0 Aug  9 01:07 tasks
drwxr-xr-x 2 root root 0 Aug  9 00:22 3da8ee4dd5c8af2d33b9301c948bb57e9ed56534a6d980084a25d9306572608b
-rw-r--r-- 1 root root 0 Aug  9 01:05 cgroup.clone_children
--w--w--w- 1 root root 0 Aug  9 01:05 cgroup.event_control
-rw-r--r-- 1 root root 0 Aug  9 01:05 cgroup.procs

��������������� memory.limit_in_bytes ������������

# cat memory.limit_in_bytes 
1073741824

���������������������������������.

  • ���������������

������������������������������������������Linux tools���cgroups������������������������������(������:free���top)������������������proc������������������������������������������,���:/proc/meminfo���/proc/vmstat���/proc/PID/smaps���.

��

������ /proc/meminfo���/proc/vmstat���������������������������������������������������������������(������������������)���������������.���������������������������������������������������.

Docker���JVM���������

  • CPUs

���������������������������������������������������JVM������������������������������������������������������������.

������������������������������ -XX:ParallelGCThreads(��GC threads) and -XX:CICompilerCount(��JIT compiler threads)���������JVM���������������������CPU���������������������������������

JVM���ParallelGCThreads���������

������JDK8u120

unsigned int Abstract_VM_Version::nof_parallel_worker_threads(
                                                      unsigned int num,
                                                      unsigned int den,
                                                      unsigned int switch_pt) {
  if (FLAG_IS_DEFAULT(ParallelGCThreads)) {
    assert(ParallelGCThreads == 0, "Default ParallelGCThreads is not 0");
    // For very large machines, there are diminishing returns
    // for large numbers of worker threads.  Instead of
    // hogging the whole system, use a fraction of the workers for every
    // processor after the first 8.  For example, on a 72 cpu machine
    // and a chosen fraction of 5/8
    // use 8 + (72 - 8) * (5/8) == 48 worker threads.
    unsigned int ncpus = (unsigned int) os::active_processor_count();
    return (ncpus <= switch_pt) ?
           ncpus :
          (switch_pt + ((ncpus - switch_pt) * num) / den);
  } else {
    return ParallelGCThreads;
  }
}

������������CPU������������������os::active_processor_count()

������������os_linux������������

int os::active_processor_count() {
  // Linux doesn't yet have a (official) notion of processor sets,
  // so just return the number of online processors.
  int online_cpus = ::sysconf(_SC_NPROCESSORS_ONLN);
  assert(online_cpus > 0 && online_cpus <= processor_count(), "sanity check");
  return online_cpus;
}

���������������������������������������sysconf(_SC_NPROCESSORS_ONLN)������������������CPU������������������������������Linux manual page���https://man7.org/linux/man-pages/man3/sysconf.3.html������

- _SC_NPROCESSORS_ONLN
              The number of processors currently online (available).
              See also get_nprocs_conf(3).

���������������������������������������CPU������������������������������ParallelGCThreads���������������������������������������������CPU���������GC Threads������������������������������������������������������������.

������JIT Threads������������������.

OpenJDK���������������

  • JDK8u131���������

��������������������������������������������� JDK-8140793���https://bugs.openjdk.java.net/browse/JDK-8140793���.

��

���JDK9������������������������������backport������JDK8u131���,

������������������JDK 8u131 Update Release Notes(https://www.oracle.com/java/technologies/javase/8u131-relnotes.html)���

BugFixes(https://www.oracle.com/java/technologies/javase/8u131-bugfixes.html)

���������������������������JDK8u144���

// Get the current number of available processors for this process.
// This value can change at any time during a process's lifetime.
// sched_getaffinity gives an accurate answer as it accounts for cpusets.
// If anything goes wrong we fallback to returning the number of online
// processors - which can be greater than the number available to the process.
int os::active_processor_count() {
  cpu_set_t cpus;  // can represent at most 1024 (CPU_SETSIZE) processors
  int cpus_size = sizeof(cpu_set_t);
  int cpu_count = 0;

  // pid 0 means the current thread - which we have to assume represents the process
  if (sched_getaffinity(0, cpus_size, &cpus) == 0) {
    cpu_count = os_cpu_count(&cpus);
    if (PrintActiveCpus) {
      tty->print_cr("active_processor_count: sched_getaffinity processor count: %d", cpu_count);
    }
  }
  else {
    cpu_count = ::sysconf(_SC_NPROCESSORS_ONLN);
    warning("sched_getaffinity failed (%s)- using online processor count (%d) "
            "which may exceed available processors", strerror(errno), cpu_count);
  }

  assert(cpu_count > 0 && cpu_count <= processor_count(), "sanity check");
  return cpu_count;
}

���������������������CPU���������������������������cpusets���������������������������docker��� --cpuset-cpus ������������������ CPU ������������������������.

  • JDK8u191���������

���JDK8u191������������������������ docker ������������.

��

���JDK10������������������������������������backport������JDK8u191���,������������������JDK 8u191 Update Release Notes(https://www.oracle.com/java/technologies/javase/8u191-relnotes.html#JDK-8146115)���

JDK-8146115(https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8146115)

������������������������

  1. -XX:UseContainerSupport ���������������������������
  2. -XX:ActiveProcessorCount=count�� ���������JVM������������CPU���������������������������JVM������������������������CPU������������������������������������

���������������������������JDK8u192���

// Determine the active processor count from one of
// three different sources:
//
// 1. User option -XX:ActiveProcessorCount
// 2. kernel os calls (sched_getaffinity or sysconf(_SC_NPROCESSORS_ONLN)
// 3. extracted from cgroup cpu subsystem (shares and quotas)
//
// Option 1, if specified, will always override.
// If the cgroup subsystem is active and configured, we
// will return the min of the cgroup and option 2 results.
// This is required since tools, such as numactl, that
// alter cpu affinity do not update cgroup subsystem
// cpuset configuration files.
int os::active_processor_count() {
  // User has overridden the number of active processors
  if (ActiveProcessorCount > 0) {
    if (PrintActiveCpus) {
      tty->print_cr("active_processor_count: "
                    "active processor count set by user : %d",
                    ActiveProcessorCount);
    }
    return ActiveProcessorCount;
  }

  int active_cpus;
  if (OSContainer::is_containerized()) {
    active_cpus = OSContainer::active_processor_count();
    if (PrintActiveCpus) {
      tty->print_cr("active_processor_count: determined by OSContainer: %d",
                     active_cpus);
    }
  } else {
    active_cpus = os::Linux::active_processor_count();
  }

  return active_cpus;
}

���������������������������

��

���������������������������������������CPU������:

  1. ���������������������- xx: ActiveProcessorCount������������ActiveProcessorCount���.
  2. ���������ActiveProcessorCount���������������������������������������������cgroup cpu subsystem (shares and quotas)������������������.
  3. ���������������UseContainerSupport������������������������������������������������������(sched_getaffinity���sysconf(_SC_NPROCESSORS_ONLN))������.

������OSContainer::is_containerized()

inline bool OSContainer::is_containerized() {
  assert(_is_initialized, "OSContainer not initialized");
  return _is_containerized;
}

_is_containerized������Threads::create_vm������OSContainer::init()���������������������������������������������������

/* init
 *
 * Initialize the container support and determine if
 * we are running under cgroup control.
 */
void OSContainer::init() {
  int mountid;
  int parentid;
  int major;
  int minor;
  FILE *mntinfo = NULL;
  FILE *cgroup = NULL;
  char buf[MAXPATHLEN+1];
  char tmproot[MAXPATHLEN+1];
  char tmpmount[MAXPATHLEN+1];
  char tmpbase[MAXPATHLEN+1];
  char *p;
  jlong mem_limit;

  assert(!_is_initialized, "Initializing OSContainer more than once");

  _is_initialized = true;
  _is_containerized = false;

  _unlimited_memory = (LONG_MAX / os::vm_page_size()) * os::vm_page_size();

  if (PrintContainerInfo) {
    tty->print_cr("OSContainer::init: Initializing Container Support");
  }
  if (!UseContainerSupport) {
    if (PrintContainerInfo) {
      tty->print_cr("Container Support not enabled");
    }
    return;
  }

  /*
   * Find the cgroup mount point for memory and cpuset
   * by reading /proc/self/mountinfo
   *
   * Example for docker:
   * 219 214 0:29 /docker/7208cebd00fa5f2e342b1094f7bed87fa25661471a4637118e65f1c995be8a34 /sys/fs/cgroup/memory ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory
   *
   * Example for host:
   * 34 28 0:29 / /sys/fs/cgroup/memory rw,nosuid,nodev,noexec,relatime shared:16 - cgroup cgroup rw,memory
   */
  mntinfo = fopen("/proc/self/mountinfo", "r");
  if (mntinfo == NULL) {
      if (PrintContainerInfo) {
        tty->print_cr("Can't open /proc/self/mountinfo, %s",
                       strerror(errno));
      }
      return;
  }

  while ( (p = fgets(buf, MAXPATHLEN, mntinfo)) != NULL) {
    // Look for the filesystem type and see if it's cgroup
    char fstype[MAXPATHLEN+1];
    fstype[0] = '\0';
    char *s =  strstr(p, " - ");
    if (s != NULL &&
        sscanf(s, " - %s", fstype) == 1 &&
        strcmp(fstype, "cgroup") == 0) {

      if (strstr(p, "memory") != NULL) {
        int matched = sscanf(p, "%d %d %d:%d %s %s",
                             &mountid,
                             &parentid,
                             &major,
                             &minor,
                             tmproot,
                             tmpmount);
        if (matched == 6) {
          memory = new CgroupSubsystem(tmproot, tmpmount);
        }
        else
          if (PrintContainerInfo) {
            tty->print_cr("Incompatible str containing cgroup and memory: %s", p);
          }
      } else if (strstr(p, "cpuset") != NULL) {
        int matched = sscanf(p, "%d %d %d:%d %s %s",
                             &mountid,
                             &parentid,
                             &major,
                             &minor,
                             tmproot,
                             tmpmount);
        if (matched == 6) {
          cpuset = new CgroupSubsystem(tmproot, tmpmount);
        }
        else {
          if (PrintContainerInfo) {
            tty->print_cr("Incompatible str containing cgroup and cpuset: %s", p);
          }
        }
      } else if (strstr(p, "cpu,cpuacct") != NULL || strstr(p, "cpuacct,cpu") != NULL) {
        int matched = sscanf(p, "%d %d %d:%d %s %s",
                             &mountid,
                             &parentid,
                             &major,
                             &minor,
                             tmproot,
                             tmpmount);
        if (matched == 6) {
          cpu = new CgroupSubsystem(tmproot, tmpmount);
          cpuacct = new CgroupSubsystem(tmproot, tmpmount);
        }
        else {
          if (PrintContainerInfo) {
            tty->print_cr("Incompatible str containing cgroup and cpu,cpuacct: %s", p);
          }
        }
      } else if (strstr(p, "cpuacct") != NULL) {
        int matched = sscanf(p, "%d %d %d:%d %s %s",
                             &mountid,
                             &parentid,
                             &major,
                             &minor,
                             tmproot,
                             tmpmount);
        if (matched == 6) {
          cpuacct = new CgroupSubsystem(tmproot, tmpmount);
        }
        else {
          if (PrintContainerInfo) {
            tty->print_cr("Incompatible str containing cgroup and cpuacct: %s", p);
          }
        }
      } else if (strstr(p, "cpu") != NULL) {
        int matched = sscanf(p, "%d %d %d:%d %s %s",
                             &mountid,
                             &parentid,
                             &major,
                             &minor,
                             tmproot,
                             tmpmount);
        if (matched == 6) {
          cpu = new CgroupSubsystem(tmproot, tmpmount);
        }
        else {
          if (PrintContainerInfo) {
            tty->print_cr("Incompatible str containing cgroup and cpu: %s", p);
          }
        }
      }
    }
  }

  fclose(mntinfo);

  if (memory == NULL) {
    if (PrintContainerInfo) {
      tty->print_cr("Required cgroup memory subsystem not found");
    }
    return;
  }
  if (cpuset == NULL) {
    if (PrintContainerInfo) {
      tty->print_cr("Required cgroup cpuset subsystem not found");
    }
    return;
  }
  if (cpu == NULL) {
    if (PrintContainerInfo) {
      tty->print_cr("Required cgroup cpu subsystem not found");
    }
    return;
  }
  if (cpuacct == NULL) {
    if (PrintContainerInfo) {
      tty->print_cr("Required cgroup cpuacct subsystem not found");
    }
    return;
  }

  /*
   * Read /proc/self/cgroup and map host mount point to
   * local one via /proc/self/mountinfo content above
   *
   * Docker example:
   * 5:memory:/docker/6558aed8fc662b194323ceab5b964f69cf36b3e8af877a14b80256e93aecb044
   *
   * Host example:
   * 5:memory:/user.slice
   *
   * Construct a path to the process specific memory and cpuset
   * cgroup directory.
   *
   * For a container running under Docker from memory example above
   * the paths would be:
   *
   * /sys/fs/cgroup/memory
   *
   * For a Host from memory example above the path would be:
   *
   * /sys/fs/cgroup/memory/user.slice
   *
   */
  cgroup = fopen("/proc/self/cgroup", "r");
  if (cgroup == NULL) {
    if (PrintContainerInfo) {
      tty->print_cr("Can't open /proc/self/cgroup, %s",
                     strerror(errno));
      }
    return;
  }

  while ( (p = fgets(buf, MAXPATHLEN, cgroup)) != NULL) {
    int cgno;
    int matched;
    char *controller;
    char *base;

    /* Skip cgroup number */
    strsep(&p, ":");
    /* Get controller and base */
    controller = strsep(&p, ":");
    base = strsep(&p, "\n");

    if (controller != NULL) {
      if (strstr(controller, "memory") != NULL) {
        memory->set_subsystem_path(base);
      } else if (strstr(controller, "cpuset") != NULL) {
        cpuset->set_subsystem_path(base);
      } else if (strstr(controller, "cpu,cpuacct") != NULL || strstr(controller, "cpuacct,cpu") != NULL) {
        cpu->set_subsystem_path(base);
        cpuacct->set_subsystem_path(base);
      } else if (strstr(controller, "cpuacct") != NULL) {
        cpuacct->set_subsystem_path(base);
      } else if (strstr(controller, "cpu") != NULL) {
        cpu->set_subsystem_path(base);
      }
    }
  }

  fclose(cgroup);

  // We need to update the amount of physical memory now that
  // command line arguments have been processed.
  if ((mem_limit = memory_limit_in_bytes()) > 0) {
    os::Linux::set_physical_memory(mem_limit);
  }

  _is_containerized = true;

}

������OSContainer::init(���

������������������������������������������������

  1. ������-XX:UseContainerSupport������������������������������_is_containerized ��� false.
  2. ������������cgroup������cpuset���memory������������mount point : /proc/self/mountinfo�� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� �� ��

    Example for docker:�� /docker/7208cebd00fa5f2e342b1094f7bed87fa25661471a4637118e65f1c995be8a34�� /sys/fs/cgroup/memory ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,memory�� �� �� �� �� �� ���������������������������_is_containerized ��� false.

  3. ������������/proc/self/cgroup���������������������/proc/self/mountinfo�� �������������������������������������������� �� �� �� ��

    Docker example:

    ��5:memory:/docker/6558aed8fc662b194323ceab5b964f69cf36b3e8af877a14b80256e93aecb044�� ��/sys/fs/cgroup/memory

    ���������������������������_is_containerized ��� false.

/* active_processor_count
 *
 * Calculate an appropriate number of active processors for the
 * VM to use based on these three inputs.
 *
 * cpu affinity
 * cgroup cpu quota & cpu period
 * cgroup cpu shares
 *
 * Algorithm:
 *
 * Determine the number of available CPUs from sched_getaffinity
 *
 * If user specified a quota (quota != -1), calculate the number of
 * required CPUs by dividing quota by period.
 *
 * If shares are in effect (shares != -1), calculate the number
 * of CPUs required for the shares by dividing the share value
 * by PER_CPU_SHARES.
 *
 * All results of division are rounded up to the next whole number.
 *
 * If neither shares or quotas have been specified, return the
 * number of active processors in the system.
 *
 * If both shares and quotas have been specified, the results are
 * based on the flag PreferContainerQuotaForCPUCount.  If true,
 * return the quota value.  If false return the smallest value
 * between shares or quotas.
 *
 * If shares and/or quotas have been specified, the resulting number
 * returned will never exceed the number of active processors.
 *
 * return:
 *    number of CPUs
 */
int OSContainer::active_processor_count() {
  int quota_count = 0, share_count = 0;
  int cpu_count, limit_count;
  int result;

  cpu_count = limit_count = os::Linux::active_processor_count();
  int quota  = cpu_quota();
  int period = cpu_period();
  int share  = cpu_shares();

  if (quota > -1 && period > 0) {
    quota_count = ceilf((float)quota / (float)period);
    if (PrintContainerInfo) {
      tty->print_cr("CPU Quota count based on quota/period: %d", quota_count);
    }
  }
  if (share > -1) {
    share_count = ceilf((float)share / (float)PER_CPU_SHARES);
    if (PrintContainerInfo) {
      tty->print_cr("CPU Share count based on shares: %d", share_count);
    }
  }

  // If both shares and quotas are setup results depend
  // on flag PreferContainerQuotaForCPUCount.
  // If true, limit CPU count to quota
  // If false, use minimum of shares and quotas
  if (quota_count !=0 && share_count != 0) {
    if (PreferContainerQuotaForCPUCount) {
      limit_count = quota_count;
    } else {
      limit_count = MIN2(quota_count, share_count);
    }
  } else if (quota_count != 0) {
    limit_count = quota_count;
  } else if (share_count != 0) {
    limit_count = share_count;
  }

  result = MIN2(cpu_count, limit_count);
  if (PrintContainerInfo) {
    tty->print_cr("OSContainer::active_processor_count: %d", result);
  }
  return result;
}

������OSContainer::active_processor_count()

Configure the default CFS scheduler

The CFS is the Linux kernel CPU scheduler for normal Linux processes. Several runtime flags allow you to configure the amount of access to CPU resources your container has. When you use these settings, Docker modifies the settings for the container���s cgroup on the host machine.

Option Description
--cpu-quota=<value> Impose a CPU CFS quota on the container. The number of microseconds per --cpu-period that the container is limited to before throttled. As such acting as the effective ceiling. For most use-cases, --cpus is a more convenient alternative.
--cpu-period=<value> Specify the CPU CFS scheduler period, which is used alongside --cpu-quota. Defaults to 100000 microseconds (100 milliseconds). Most users do not change this from the default. For most use-cases, --cpus is a more convenient alternative.
..... .....

������������

  • ������JVM��������������������������������� -XX:ParallelGCThreads��� -XX:CICompilerCount ���������������������JDK8u191������������������������������ -XX: ActiveProcessorCount ���.
  • ������JDK / ������������������������������
    JDK8u121��������������� -> JDK8u131������������������JDK8u191������������������������������������������������������������������Docker������.
  • ���������JDK ������������������������������������������������������������https://github.com/vipshop/vjtools/tree/master/vjstar/src/main/script/docker-cpus.

������ libsysconfcpus������������������������������������JDK������������������������������������

libsysconfcpus.so������������������JVM������CPU���������������������������sysconf(_SC_NPROCESSORS_CONF)���������������������������LIBSYSCONFCPUS���������������������libsysconfcpus���������������so���������������������������������������������������������������������������������

�� �� 1.������������������LD_PRELOAD������libsysconfcpus.so���������������������������������������

�� �� 2.���������������������������������������������������������������������������"CONTAINER_CORE_LIMIT"���������������CPU������(���������������������������)������������������������libsysconfcpus������������������������

������������JVM������-server������������������������2���������������������������������������

#!/bin/sh
if [ "x$CONTAINER_CORE_REQUEST" != "x" ]; then
   LIBSYSCONFCPUS="$CONTAINER_CORE_REQUEST"  
   if [ ${LIBSYSCONFCPUS} -lt 2 ]; then
      LIBSYSCONFCPUS=2
   fi
   export LIBSYSCONFCPUS      
fi
export LD_PRELOAD="/usr/local/lib/libsysconfcpus.so:$LD_PRELOAD"

Docker���JVM���������

  • Memory

���������������������������������������-Xmx���MaxHeapSize������-Xms���InitialHeapSize���������JVM������������������������������������������������������������������������������������������JDK8u120���

void Arguments::set_heap_size() {
  if (!FLAG_IS_DEFAULT(DefaultMaxRAMFraction)) {
    // Deprecated flag
    FLAG_SET_CMDLINE(uintx, MaxRAMFraction, DefaultMaxRAMFraction);
  }

  const julong phys_mem =
    FLAG_IS_DEFAULT(MaxRAM) ? MIN2(os::physical_memory(), (julong)MaxRAM)
                            : (julong)MaxRAM;

  // If the maximum heap size has not been set with -Xmx,
  // then set it as fraction of the size of physical memory,
  // respecting the maximum and minimum sizes of the heap.
  if (FLAG_IS_DEFAULT(MaxHeapSize)) {
    julong reasonable_max = phys_mem / MaxRAMFraction;

    if (phys_mem <= MaxHeapSize * MinRAMFraction) {
      // Small physical memory, so use a minimum fraction of it for the heap
      reasonable_max = phys_mem / MinRAMFraction;
    } else {
      // Not-small physical memory, so require a heap at least
      // as large as MaxHeapSize
      reasonable_max = MAX2(reasonable_max, (julong)MaxHeapSize);
    }
    if (!FLAG_IS_DEFAULT(ErgoHeapSizeLimit) && ErgoHeapSizeLimit != 0) {
      // Limit the heap size to ErgoHeapSizeLimit
      reasonable_max = MIN2(reasonable_max, (julong)ErgoHeapSizeLimit);
    }
    if (UseCompressedOops) {
      // Limit the heap size to the maximum possible when using compressed oops
      julong max_coop_heap = (julong)max_heap_for_compressed_oops();
      if (HeapBaseMinAddress + MaxHeapSize < max_coop_heap) {
        // Heap should be above HeapBaseMinAddress to get zero based compressed oops
        // but it should be not less than default MaxHeapSize.
        max_coop_heap -= HeapBaseMinAddress;
      }
      reasonable_max = MIN2(reasonable_max, max_coop_heap);
    }
    reasonable_max = limit_by_allocatable_memory(reasonable_max);

    if (!FLAG_IS_DEFAULT(InitialHeapSize)) {
      // An initial heap size was specified on the command line,
      // so be sure that the maximum size is consistent.  Done
      // after call to limit_by_allocatable_memory because that
      // method might reduce the allocation size.
      reasonable_max = MAX2(reasonable_max, (julong)InitialHeapSize);
    }

    if (PrintGCDetails && Verbose) {
      // Cannot use gclog_or_tty yet.
      tty->print_cr("  Maximum heap size " SIZE_FORMAT, reasonable_max);
    }
    FLAG_SET_ERGO(uintx, MaxHeapSize, (uintx)reasonable_max);
  }

  // If the minimum or initial heap_size have not been set or requested to be set
  // ergonomically, set them accordingly.
  if (InitialHeapSize == 0 || min_heap_size() == 0) {
    julong reasonable_minimum = (julong)(OldSize + NewSize);

    reasonable_minimum = MIN2(reasonable_minimum, (julong)MaxHeapSize);

    reasonable_minimum = limit_by_allocatable_memory(reasonable_minimum);

    if (InitialHeapSize == 0) {
      julong reasonable_initial = phys_mem / InitialRAMFraction;

      reasonable_initial = MAX3(reasonable_initial, reasonable_minimum, (julong)min_heap_size());
      reasonable_initial = MIN2(reasonable_initial, (julong)MaxHeapSize);

      reasonable_initial = limit_by_allocatable_memory(reasonable_initial);

      if (PrintGCDetails && Verbose) {
        // Cannot use gclog_or_tty yet.
        tty->print_cr("  Initial heap size " SIZE_FORMAT, (uintx)reasonable_initial);
      }
      FLAG_SET_ERGO(uintx, InitialHeapSize, (uintx)reasonable_initial);
    }
    // If the minimum heap size has not been set (via -Xms),
    // synchronize with InitialHeapSize to avoid errors with the default value.
    if (min_heap_size() == 0) {
      set_min_heap_size(MIN2((uintx)reasonable_minimum, InitialHeapSize));
      if (PrintGCDetails && Verbose) {
        // Cannot use gclog_or_tty yet.
        tty->print_cr("  Minimum heap size " SIZE_FORMAT, min_heap_size());
      }
    }
  }
}

������������������,������������������������������os::physical_memory()

julong os::physical_memory() {
  return Linux::physical_memory();
}

���������������������������������������������������������������������������������������������,���������Docker���������JVM���������������������������������������OOMKiller���������.

���������

���������������������������12G���������

$ java -XX:+PrintFlagsFinal -version|grep -i heapsize|egrep 'Initial|Max'
java version "1.8.0_74"
Java(TM) SE Runtime Environment (build 1.8.0_74-b02)
Java HotSpot(TM) 64-Bit Server VM (build 25.74-b02, mixed mode)
    uintx InitialHeapSize                          := 188743680                           {product}
    uintx MaxHeapSize                              := 2988441600                          {product}

������������������������6G���������

$ docker run --rm java java  -XX:+PrintFlagsFinal -version |grep -i heapsize | egrep 'Initial|Max'
openjdk version "1.8.0_72-internal"
OpenJDK Runtime Environment (build 1.8.0_72-internal-b15)
OpenJDK 64-Bit Server VM (build 25.72-b15, mixed mode)
    uintx InitialHeapSize                          := 188743680                           {product}
    uintx MaxHeapSize                              := 2988441600                          {product}

���������������������256M���������

$ docker run -m 256m --rm java java  -XX:+PrintFlagsFinal -version |grep -i heapsize | egrep 'Initial|Max'

WARNING: Your kernel does not support swap limit capabilities, memory limited without swap.
openjdk version "1.8.0_72-internal"
OpenJDK Runtime Environment (build 1.8.0_72-internal-b15)
OpenJDK 64-Bit Server VM (build 25.72-b15, mixed mode)
    uintx InitialHeapSize                          := 188743680                           {product}
    uintx MaxHeapSize                              := 2988441600                          {product}

������������JVM���������������������������������������.

���������������������������OOMKilled

....
"State": {
    "Running": false,
    "Paused": false,
    "Restarting": false,
    "OOMKilled": true,
    "Dead": false,
    "Pid": 0,
    "ExitCode": 137,
    "Error": "",
    "StartedAt": "2016-03-15T21:21:48.845032635Z",
    "FinishedAt": "2016-03-15T21:21:49.140794192Z"
},
....

OpenJDK���������������

  • JDK8u131���������

��������������������������������������������� JDK-8170888���https://bugs.openjdk.java.net/browse/JDK-8170888���.

��

���JDK9������������������������������backport������JDK8u131���,

������������������JDK 8u131 Update Release Notes(https://www.oracle.com/java/technologies/javase/8u131-relnotes.html)���

BugFixes(https://www.oracle.com/java/technologies/javase/8u131-bugfixes.html)

������������������������������

  1. -XX:+UseCGroupMemoryLimitForHeap

������ -XX:+UseCGroupMemoryLimitForHeap ���������experimental VM option������������������������������������������������������������������-XX:+UnlockExperimentalVMOptions.

���������������������������JDK8u144���

void Arguments::set_heap_size() {
  if (!FLAG_IS_DEFAULT(DefaultMaxRAMFraction)) {
    // Deprecated flag
    FLAG_SET_CMDLINE(uintx, MaxRAMFraction, DefaultMaxRAMFraction);
  }

  julong phys_mem =
    FLAG_IS_DEFAULT(MaxRAM) ? MIN2(os::physical_memory(), (julong)MaxRAM)
                            : (julong)MaxRAM;

  // Experimental support for CGroup memory limits
  if (UseCGroupMemoryLimitForHeap) {
    // This is a rough indicator that a CGroup limit may be in force
    // for this process
    const char* lim_file = "/sys/fs/cgroup/memory/memory.limit_in_bytes";
    FILE *fp = fopen(lim_file, "r");
    if (fp != NULL) {
      julong cgroup_max = 0;
      int ret = fscanf(fp, JULONG_FORMAT, &cgroup_max);
      if (ret == 1 && cgroup_max > 0) {
        // If unlimited, cgroup_max will be a very large, but unspecified
        // value, so use initial phys_mem as a limit
        if (PrintGCDetails && Verbose) {
          // Cannot use gclog_or_tty yet.
          tty->print_cr("Setting phys_mem to the min of cgroup limit ("
                        JULONG_FORMAT "MB) and initial phys_mem ("
                        JULONG_FORMAT "MB)", cgroup_max/M, phys_mem/M);
        }
        phys_mem = MIN2(cgroup_max, phys_mem);
      } else {
        warning("Unable to read/parse cgroup memory limit from %s: %s",
                lim_file, errno != 0 ? strerror(errno) : "unknown error");
      }
      fclose(fp);
    } else {
      warning("Unable to open cgroup memory limit file %s (%s)", lim_file, strerror(errno));
    }
  }

  // If the maximum heap size has not been set with -Xmx,
  // then set it as fraction of the size of physical memory,
  // respecting the maximum and minimum sizes of the heap.
  if (FLAG_IS_DEFAULT(MaxHeapSize)) {
    julong reasonable_max = phys_mem / MaxRAMFraction;

    if (phys_mem <= MaxHeapSize * MinRAMFraction) {
      // Small physical memory, so use a minimum fraction of it for the heap
      reasonable_max = phys_mem / MinRAMFraction;
    } else {
      // Not-small physical memory, so require a heap at least
      // as large as MaxHeapSize
      reasonable_max = MAX2(reasonable_max, (julong)MaxHeapSize);
    }
    if (!FLAG_IS_DEFAULT(ErgoHeapSizeLimit) && ErgoHeapSizeLimit != 0) {
      // Limit the heap size to ErgoHeapSizeLimit
      reasonable_max = MIN2(reasonable_max, (julong)ErgoHeapSizeLimit);
    }
    if (UseCompressedOops) {
      // Limit the heap size to the maximum possible when using compressed oops
      julong max_coop_heap = (julong)max_heap_for_compressed_oops();
      if (HeapBaseMinAddress + MaxHeapSize < max_coop_heap) {
        // Heap should be above HeapBaseMinAddress to get zero based compressed oops
        // but it should be not less than default MaxHeapSize.
        max_coop_heap -= HeapBaseMinAddress;
      }
      reasonable_max = MIN2(reasonable_max, max_coop_heap);
    }
    reasonable_max = limit_by_allocatable_memory(reasonable_max);

    if (!FLAG_IS_DEFAULT(InitialHeapSize)) {
      // An initial heap size was specified on the command line,
      // so be sure that the maximum size is consistent.  Done
      // after call to limit_by_allocatable_memory because that
      // method might reduce the allocation size.
      reasonable_max = MAX2(reasonable_max, (julong)InitialHeapSize);
    }

    if (PrintGCDetails && Verbose) {
      // Cannot use gclog_or_tty yet.
      tty->print_cr("  Maximum heap size " SIZE_FORMAT, (size_t) reasonable_max);
    }
    FLAG_SET_ERGO(uintx, MaxHeapSize, (uintx)reasonable_max);
  }

  // If the minimum or initial heap_size have not been set or requested to be set
  // ergonomically, set them accordingly.
  if (InitialHeapSize == 0 || min_heap_size() == 0) {
    julong reasonable_minimum = (julong)(OldSize + NewSize);

    reasonable_minimum = MIN2(reasonable_minimum, (julong)MaxHeapSize);

    reasonable_minimum = limit_by_allocatable_memory(reasonable_minimum);

    if (InitialHeapSize == 0) {
      julong reasonable_initial = phys_mem / InitialRAMFraction;

      reasonable_initial = MAX3(reasonable_initial, reasonable_minimum, (julong)min_heap_size());
      reasonable_initial = MIN2(reasonable_initial, (julong)MaxHeapSize);

      reasonable_initial = limit_by_allocatable_memory(reasonable_initial);

      if (PrintGCDetails && Verbose) {
        // Cannot use gclog_or_tty yet.
        tty->print_cr("  Initial heap size " SIZE_FORMAT, (uintx)reasonable_initial);
      }
      FLAG_SET_ERGO(uintx, InitialHeapSize, (uintx)reasonable_initial);
    }
    // If the minimum heap size has not been set (via -Xms),
    // synchronize with InitialHeapSize to avoid errors with the default value.
    if (min_heap_size() == 0) {
      set_min_heap_size(MIN2((uintx)reasonable_minimum, InitialHeapSize));
      if (PrintGCDetails && Verbose) {
        // Cannot use gclog_or_tty yet.
        tty->print_cr("  Minimum heap size " SIZE_FORMAT, min_heap_size());
      }
    }
  }
}

���������������������������-XX:+UseCGroupMemoryLimitForHeap ���������������JVM���������������������cgroup/memory���limit_in_bytes���������������������������������������������.

  • JDK8u191���������

���JDK8u191������������������������ docker ������������.

��

���JDK10������������������������������������backport������JDK8u191���,������������������JDK 8u191 Update Release Notes(https://www.oracle.com/java/technologies/javase/8u191-relnotes.html#JDK-8146115).

JDK-8186248(https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8186248)

���������������������������������������������������������������������������������������������������������������������������������

  1. -XX:InitialRAMPercentage
  2. -XX:MaxRAMPercentage
  3. -XX:MinRAMPercentage

���������������-XX:InitialRAMFraction���-XX:MaxRAMFraction������-XX:MinRAMFraction���������������������.

���������������������������JDK8u192������

void Arguments::set_heap_size() {
  if (!FLAG_IS_DEFAULT(DefaultMaxRAMFraction)) {
    // Deprecated flag
    FLAG_SET_CMDLINE(uintx, MaxRAMFraction, DefaultMaxRAMFraction);
  }

  julong phys_mem =
    FLAG_IS_DEFAULT(MaxRAM) ? MIN2(os::physical_memory(), (julong)MaxRAM)
                            : (julong)MaxRAM;

  // Experimental support for CGroup memory limits
  if (UseCGroupMemoryLimitForHeap) {
    // This is a rough indicator that a CGroup limit may be in force
    // for this process
    const char* lim_file = "/sys/fs/cgroup/memory/memory.limit_in_bytes";
    FILE *fp = fopen(lim_file, "r");
    if (fp != NULL) {
      julong cgroup_max = 0;
      int ret = fscanf(fp, JULONG_FORMAT, &cgroup_max);
      if (ret == 1 && cgroup_max > 0) {
        // If unlimited, cgroup_max will be a very large, but unspecified
        // value, so use initial phys_mem as a limit
        if (PrintGCDetails && Verbose) {
          // Cannot use gclog_or_tty yet.
          tty->print_cr("Setting phys_mem to the min of cgroup limit ("
                        JULONG_FORMAT "MB) and initial phys_mem ("
                        JULONG_FORMAT "MB)", cgroup_max/M, phys_mem/M);
        }
        phys_mem = MIN2(cgroup_max, phys_mem);
      } else {
        warning("Unable to read/parse cgroup memory limit from %s: %s",
                lim_file, errno != 0 ? strerror(errno) : "unknown error");
      }
      fclose(fp);
    } else {
      warning("Unable to open cgroup memory limit file %s (%s)", lim_file, strerror(errno));
    }
  }

  // Convert Fraction to Precentage values
  if (FLAG_IS_DEFAULT(MaxRAMPercentage) &&
      !FLAG_IS_DEFAULT(MaxRAMFraction))
    MaxRAMPercentage = 100.0 / MaxRAMFraction;

   if (FLAG_IS_DEFAULT(MinRAMPercentage) &&
       !FLAG_IS_DEFAULT(MinRAMFraction))
     MinRAMPercentage = 100.0 / MinRAMFraction;

   if (FLAG_IS_DEFAULT(InitialRAMPercentage) &&
       !FLAG_IS_DEFAULT(InitialRAMFraction))
     InitialRAMPercentage = 100.0 / InitialRAMFraction;

  // If the maximum heap size has not been set with -Xmx,
  // then set it as fraction of the size of physical memory,
  // respecting the maximum and minimum sizes of the heap.
  if (FLAG_IS_DEFAULT(MaxHeapSize)) {
    julong reasonable_max = (julong)((phys_mem * MaxRAMPercentage) / 100);
    const julong reasonable_min = (julong)((phys_mem * MinRAMPercentage) / 100);
    if (reasonable_min < MaxHeapSize) {
      // Small physical memory, so use a minimum fraction of it for the heap
      reasonable_max = reasonable_min;
    } else {
      // Not-small physical memory, so require a heap at least
      // as large as MaxHeapSize
      reasonable_max = MAX2(reasonable_max, (julong)MaxHeapSize);
    }

    if (!FLAG_IS_DEFAULT(ErgoHeapSizeLimit) && ErgoHeapSizeLimit != 0) {
      // Limit the heap size to ErgoHeapSizeLimit
      reasonable_max = MIN2(reasonable_max, (julong)ErgoHeapSizeLimit);
    }
    if (UseCompressedOops) {
      // Limit the heap size to the maximum possible when using compressed oops
      julong max_coop_heap = (julong)max_heap_for_compressed_oops();
      if (HeapBaseMinAddress + MaxHeapSize < max_coop_heap) {
        // Heap should be above HeapBaseMinAddress to get zero based compressed oops
        // but it should be not less than default MaxHeapSize.
        max_coop_heap -= HeapBaseMinAddress;
      }
      reasonable_max = MIN2(reasonable_max, max_coop_heap);
    }
    reasonable_max = limit_by_allocatable_memory(reasonable_max);

    if (!FLAG_IS_DEFAULT(InitialHeapSize)) {
      // An initial heap size was specified on the command line,
      // so be sure that the maximum size is consistent.  Done
      // after call to limit_by_allocatable_memory because that
      // method might reduce the allocation size.
      reasonable_max = MAX2(reasonable_max, (julong)InitialHeapSize);
    }

    if (PrintGCDetails && Verbose) {
      // Cannot use gclog_or_tty yet.
      tty->print_cr("  Maximum heap size " SIZE_FORMAT, (size_t) reasonable_max);
    }
    FLAG_SET_ERGO(uintx, MaxHeapSize, (uintx)reasonable_max);
  }

  // If the minimum or initial heap_size have not been set or requested to be set
  // ergonomically, set them accordingly.
  if (InitialHeapSize == 0 || min_heap_size() == 0) {
    julong reasonable_minimum = (julong)(OldSize + NewSize);

    reasonable_minimum = MIN2(reasonable_minimum, (julong)MaxHeapSize);

    reasonable_minimum = limit_by_allocatable_memory(reasonable_minimum);

    if (InitialHeapSize == 0) {
      julong reasonable_initial = (julong)((phys_mem * InitialRAMPercentage) / 100);

      reasonable_initial = MAX3(reasonable_initial, reasonable_minimum, (julong)min_heap_size());
      reasonable_initial = MIN2(reasonable_initial, (julong)MaxHeapSize);

      reasonable_initial = limit_by_allocatable_memory(reasonable_initial);

      if (PrintGCDetails && Verbose) {
        // Cannot use gclog_or_tty yet.
        tty->print_cr("  Initial heap size " SIZE_FORMAT, (uintx)reasonable_initial);
      }
      FLAG_SET_ERGO(uintx, InitialHeapSize, (uintx)reasonable_initial);
    }
    // If the minimum heap size has not been set (via -Xms),
    // synchronize with InitialHeapSize to avoid errors with the default value.
    if (min_heap_size() == 0) {
      set_min_heap_size(MIN2((uintx)reasonable_minimum, InitialHeapSize));
      if (PrintGCDetails && Verbose) {
        // Cannot use gclog_or_tty yet.
        tty->print_cr("  Minimum heap size " SIZE_FORMAT, min_heap_size());
      }
    }
  }
}

���������������������������JDK8u192���

julong os::physical_memory() {
  jlong phys_mem = 0;
  if (OSContainer::is_containerized()) {
    jlong mem_limit;
    if ((mem_limit = OSContainer::memory_limit_in_bytes()) > 0) {
      if (PrintContainerInfo) {
        tty->print_cr("total container memory: " JLONG_FORMAT, mem_limit);
      }
      return mem_limit;
    }

    if (PrintContainerInfo) {
      tty->print_cr("container memory limit %s: " JLONG_FORMAT ", using host value",
                     mem_limit == OSCONTAINER_ERROR ? "failed" : "unlimited", mem_limit);
    }
  }

  phys_mem = Linux::physical_memory();
  if (Verbose) {
    tty->print_cr("total system memory: " JLONG_FORMAT, phys_mem);
  }
  return phys_mem;
}

���������������������������

os::physical_memory()���������������������Linux���������������������������������OSContainer::is_containerized()������������������������������������������������������������.

������OSContainer::memory_limit_in_bytes()���

/* memory_limit_in_bytes
 *
 * Return the limit of available memory for this process.
 *
 * return:
 *    memory limit in bytes or
 *    -1 for unlimited
 *    OSCONTAINER_ERROR for not supported
 */
jlong OSContainer::memory_limit_in_bytes() {
  GET_CONTAINER_INFO(julong, memory, "/memory.limit_in_bytes",
                     "Memory Limit is: " JULONG_FORMAT, JULONG_FORMAT, memlimit);

  if (memlimit >= _unlimited_memory) {
    if (PrintContainerInfo) {
      tty->print_cr("Memory Limit is: Unlimited");
    }
    return (jlong)-1;
  }
  else {
    return (jlong)memlimit;
  }
}

������������

  • ������JVM���������������������������������-Xmx���MaxHeapSize������-Xms���InitialHeapSize������������������������JDK8u191������������������������������ -XX:InitialRAMPercentage ���-XX:MaxRAMPercentage ���-XX:MinRAMPercentage ��������� Docker ������������������������ Java ���������������������������������������������������.
  • ������JDK / ������������������������������
    JDK8u121��������������� -> JDK8u131������������������JDK8u191������������������.

������������

  • ������������������������

Docker������Java������������������jmap���jstack���������������������������

���Linux������������

Can't attach to the process: ptrace(PTRACE_ATTACH, ..).

������������

jstack���jmap������������������������������������������������:

  1. Attach������������������������Vitural Machine.attach()������������jmap���������������Socket ���������JVM���Attach Listener���������������������������������������JVM���������������Attach������������������������������.
  2. Serviceability Agent(������������������Attach������Linux���������������������ptrace���������).
// These options imply the use of a SA tool
private static String SA_TOOL_OPTIONS =
      "-heap|-heap:format=b|-clstats|-finalizerinfo";
if (option.matches(SA_TOOL_OPTIONS)) {
            useSA = true;
}
// The -F (force) option is currently not passed through to SA
private static String FORCE_SA_OPTION = "-F";

if (arg.equals(FORCE_SA_OPTION)) {
        useSA = true;
}
private void setupDebugger() {
    ....
    if (os.equals("solaris")) {
           setupDebuggerSolaris();
    } else if (os.equals("win32")) {
           setupDebuggerWin32();
    } else if (os.equals("linux")) {
           setupDebuggerLinux();
    } else if (os.equals("bsd")) {
           setupDebuggerBsd();
    } else if (os.equals("darwin")) {
           setupDebuggerDarwin();
    } else {
           // Add support for more operating systems here
           throw new DebuggerException("Operating system " + os + " not yet supported");
    }
    ....
}
private void setupDebuggerLinux() {
        setupJVMLibNamesLinux();

        if (cpu.equals("x86")) {
            machDesc = new MachineDescriptionIntelX86();
        } else if (cpu.equals("ia64")) {
            machDesc = new MachineDescriptionIA64();
        } else if (cpu.equals("amd64")) {
            machDesc = new MachineDescriptionAMD64();
        } else if (cpu.equals("sparc")) {
            if (LinuxDebuggerLocal.getAddressSize()==8) {
                    machDesc = new MachineDescriptionSPARC64Bit();
            } else {
                    machDesc = new MachineDescriptionSPARC32Bit();
            }
        } else {
          try {
            machDesc = (MachineDescription)
              Class.forName("sun.jvm.hotspot.debugger.MachineDescription" +
                            cpu.toUpperCase()).newInstance();
          } catch (Exception e) {
            throw new DebuggerException("Linux not supported on machine type " + cpu);
          }
        }

        LinuxDebuggerLocal dbg =
        new LinuxDebuggerLocal(machDesc, !isServer);
        debugger = dbg;

        attachDebugger();
    }
public synchronized void attach(int processID) throws DebuggerException {
        checkAttached();
        threadList = new ArrayList();
        loadObjectList = new ArrayList();
        class AttachTask implements WorkerThreadTask {
           int pid;
           public void doit(LinuxDebuggerLocal debugger) {
              debugger.attach0(pid);
              debugger.attached = true;
              debugger.isCore = false;
              findABIVersion();
           }
        }

        AttachTask task = new AttachTask();
        task.pid = processID;
        workerThread.execute(task);
    }
/*
 * Class:     sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal
 * Method:    attach0
 * Signature: (I)V
 */
JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_attach0__I
  (JNIEnv *env, jobject this_obj, jint jpid) {

  // For bitness checking, locate binary at /proc/jpid/exe
  char buf[PATH_MAX];
  snprintf((char *) &buf, PATH_MAX, "/proc/%d/exe", jpid);
  verifyBitness(env, (char *) &buf);
  CHECK_EXCEPTION;

  char err_buf[200];
  struct ps_prochandle* ph;
  if ( (ph = Pgrab(jpid, err_buf, sizeof(err_buf))) == NULL) {
    char msg[230];
    snprintf(msg, sizeof(msg), "Can't attach to the process: %s", err_buf);
    THROW_NEW_DEBUGGER_EXCEPTION(msg);
  }
  (*env)->SetLongField(env, this_obj, p_ps_prochandle_ID, (jlong)(intptr_t)ph);
  fillThreadsAndLoadObjects(env, this_obj, ph);
}

/*
 * Class:     sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal
 * Method:    attach0
 * Signature: (Ljava/lang/String;Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_sun_jvm_hotspot_debugger_linux_LinuxDebuggerLocal_attach0__Ljava_lang_String_2Ljava_lang_String_2
  (JNIEnv *env, jobject this_obj, jstring execName, jstring coreName) {
  const char *execName_cstr;
  const char *coreName_cstr;
  jboolean isCopy;
  struct ps_prochandle* ph;

  execName_cstr = (*env)->GetStringUTFChars(env, execName, &isCopy);
  CHECK_EXCEPTION;
  coreName_cstr = (*env)->GetStringUTFChars(env, coreName, &isCopy);
  CHECK_EXCEPTION;

  verifyBitness(env, execName_cstr);
  CHECK_EXCEPTION;

  if ( (ph = Pgrab_core(execName_cstr, coreName_cstr)) == NULL) {
    (*env)->ReleaseStringUTFChars(env, execName, execName_cstr);
    (*env)->ReleaseStringUTFChars(env, coreName, coreName_cstr);
    THROW_NEW_DEBUGGER_EXCEPTION("Can't attach to the core file");
  }
  (*env)->SetLongField(env, this_obj, p_ps_prochandle_ID, (jlong)(intptr_t)ph);
  (*env)->ReleaseStringUTFChars(env, execName, execName_cstr);
  (*env)->ReleaseStringUTFChars(env, coreName, coreName_cstr);
  fillThreadsAndLoadObjects(env, this_obj, ph);
}
// attach to the process. One and only one exposed stuff
struct ps_prochandle* Pgrab(pid_t pid, char* err_buf, size_t err_buf_len) {
  struct ps_prochandle* ph = NULL;
  thread_info* thr = NULL;

  if ( (ph = (struct ps_prochandle*) calloc(1, sizeof(struct ps_prochandle))) == NULL) {
     snprintf(err_buf, err_buf_len, "can't allocate memory for ps_prochandle");
     print_debug("%s\n", err_buf);
     return NULL;
  }

  if (ptrace_attach(pid, err_buf, err_buf_len) != true) {
     free(ph);
     return NULL;
  }

  // initialize ps_prochandle
  ph->pid = pid;

  // initialize vtable
  ph->ops = &process_ops;

  // read library info and symbol tables, must do this before attaching threads,
  // as the symbols in the pthread library will be used to figure out
  // the list of threads within the same process.
  read_lib_info(ph);

  // read thread info
  read_thread_info(ph, add_new_thread);

  // attach to the threads
  thr = ph->threads;
  while (thr) {
     // don't attach to the main thread again
    if (ph->pid != thr->lwp_id && ptrace_attach(thr->lwp_id, err_buf, err_buf_len) != true) {
        // even if one attach fails, we get return NULL
        Prelease(ph);
        return NULL;
     }
     thr = thr->next;
  }
  return ph;
}
// attach to a process/thread specified by "pid"
static bool ptrace_attach(pid_t pid, char* err_buf, size_t err_buf_len) {
  if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) < 0) {
    char buf[200];
    char* msg = strerror_r(errno, buf, sizeof(buf));
    snprintf(err_buf, err_buf_len, "ptrace(PTRACE_ATTACH, ..) failed for %d: %s", pid, msg);
    print_debug("%s\n", err_buf);
    return false;
  } else {
    return ptrace_waitpid(pid);
  }
}

Significant syscalls blocked by the default profile

Docker���s default seccomp profile is a whitelist which specifies the calls that are allowed. The table below lists the significant (but not all) syscalls that are effectively blocked because they are not on the whitelist. The table includes the reason each syscall is blocked rather than white-listed.

Syscall Description
..... .....
ptrace Tracing/profiling syscall. Blocked in Linux kernel versions before 4.8 to avoid seccomp bypass. Tracing/profiling arbitrary processes is already blocked by dropping CAP_SYS_PTRACE, because it could leak a lot of information on the host.
..... .....

Runtime privilege and Linux capabilities

Option Description
--cap-add Add Linux capabilities
--privileged Give extended privileges to this container
..... .....

The next table shows the capabilities which are not granted by default and may be added.

Capability Key Capability Description
..... .....
SYS_PTRACE Trace arbitrary processes using ptrace(2).
..... .....

������������

  • ���������������������������������������

���������OOM ���������HeapDump������������������������������������IO������������������������������������������������������������

Thanks For Watching.