Repository: pinpoint-apm/pinpoint
Issue: Fix agent startup failure in certain ibm java versions
Labels: bug
module:agent

#### Description
Certain IBM java builds do not have `bootstrapClassLoader` field in **ClassLoader** and instead have `systemClassLoader` pointing to the bootstrap class loader. (Access to this field is needed in **J9BootLoader**)

Some IBM java runtimes also do not appear to have methods required for cpu usage metrics.
Java 6 - `com.ibm.lang.management.OperatingSystemMXBean.getProcessCpuTimeByNS()` reported via #4630 
Java 7 - `com.ibm.lang.management.OperatingSystemMXBean.getProcessCpuLoad()` reported via #4585 

This is weird as the IBM API reference does have the methods listed - 
https://www.ibm.com/support/knowledgecenter/en/SSYKE2_6.0.0/com.ibm.java.api.60.doc/com.ibm.lang.management/com/ibm/lang/management/OperatingSystemMXBean.html
https://www.ibm.com/support/knowledgecenter/en/SSYKE2_7.0.0/com.ibm.java.api.70.doc/com.ibm.lang.management/com/ibm/lang/management/OperatingSystemMXBean.html

Either way, we'll need to add a check to see if the methods really do exist instead of blowing up during runtime.

Issue comments:

Pull request review comments:

Patch that resolved the issue:
diff --git a/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/classloader/BootLoaderFactory.java b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/classloader/BootLoaderFactory.java
index f16ce008cb6e..da036fdac33a 100644
--- a/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/classloader/BootLoaderFactory.java
+++ b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/classloader/BootLoaderFactory.java
@@ -16,6 +16,9 @@
 
 package com.navercorp.pinpoint.bootstrap.classloader;
 
+import com.navercorp.pinpoint.common.util.JvmType;
+import com.navercorp.pinpoint.common.util.JvmUtils;
+
 /**
  * @author Woonduk Kang(emeroad)
  */
@@ -40,17 +43,8 @@ public static BootLoader newBootLoader() {
     }
 
     private static boolean isJ9VM() {
-        // ibm-j9 java.vm.name=IBM J9 VM;
-        // openj9 java.vm.name=Eclipse OpenJ9 VM
-//        if (System.getProperty("java.vm.name", "").contains("J9 VM")) {
-//            return true;
-//        }
-        try {
-            ClassLoader.class.getDeclaredField("bootstrapClassLoader");
-            return true;
-        } catch (NoSuchFieldException e) {
-            return false;
-        }
+        JvmType jvmType = JvmUtils.getType();
+        return jvmType == JvmType.IBM;
     }
 
 }
diff --git a/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/classloader/J9BootLoader.java b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/classloader/J9BootLoader.java
index b615eaeae1e7..b0e11b3aa218 100644
--- a/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/classloader/J9BootLoader.java
+++ b/bootstrap-core/src/main/java/com/navercorp/pinpoint/bootstrap/classloader/J9BootLoader.java
@@ -44,12 +44,19 @@ public class J9BootLoader implements BootLoader {
     }
 
     private static ClassLoader findBootstrapClassLoader() {
+        Field bootstrapClassLoader;
+        try {
+            bootstrapClassLoader = ClassLoader.class.getDeclaredField("bootstrapClassLoader");
+        } catch (NoSuchFieldException e) {
+            try {
+                bootstrapClassLoader = ClassLoader.class.getDeclaredField("systemClassLoader");
+            } catch (NoSuchFieldException e2) {
+                throw new IllegalStateException("bootstrapClassLoader not found Caused by:" + e.getMessage() + ", and " + e2.getMessage(), e2);
+            }
+        }
         try {
-            Field bootstrapClassLoader = ClassLoader.class.getDeclaredField("bootstrapClassLoader");
             bootstrapClassLoader.setAccessible(true);
             return (ClassLoader) bootstrapClassLoader.get(ClassLoader.class);
-        } catch (NoSuchFieldException e) {
-            throw new IllegalStateException("bootstrapClassLoader not found Caused by:" + e.getMessage(), e);
         } catch (IllegalAccessException e) {
             throw new IllegalStateException("bootstrapClassLoader access fail Caused by:" + e.getMessage(), e);
         }
diff --git a/commons/src/main/java/com/navercorp/pinpoint/common/util/JvmType.java b/commons/src/main/java/com/navercorp/pinpoint/common/util/JvmType.java
index a63af83b5fe7..2537eb561172 100644
--- a/commons/src/main/java/com/navercorp/pinpoint/common/util/JvmType.java
+++ b/commons/src/main/java/com/navercorp/pinpoint/common/util/JvmType.java
@@ -23,7 +23,9 @@
  */
 public enum JvmType {
     UNKNOWN(null),
-    IBM("IBM"),
+    // ibm-j9 java.vm.name=IBM J9 VM;
+    // openj9 java.vm.name=Eclipse OpenJ9 VM
+    IBM("J9"),
     OPENJDK("OpenJDK"),
     ORACLE("HotSpot");
 
diff --git a/profiler-optional/profiler-optional-jdk6/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/JvmCpuUsageCalculator.java b/profiler-optional/profiler-optional-jdk6/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/JvmCpuUsageCalculator.java
index cee669e9b80e..a4c2bdf899c0 100644
--- a/profiler-optional/profiler-optional-jdk6/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/JvmCpuUsageCalculator.java
+++ b/profiler-optional/profiler-optional-jdk6/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/JvmCpuUsageCalculator.java
@@ -46,12 +46,14 @@ public double getJvmCpuUsage(final long cpuTimeNS, final long upTimeMS) {
         final long diffUpTimeMS = upTimeMS - lastUpTimeMS;
         final long totalUpTimeNS = (diffUpTimeMS * 1000000) * CPU_COUNT;
 
-        final double cpuLoad = totalUpTimeNS > 0 ?
-                Math.min(100F, totalCpuTimeNS / (float) totalUpTimeNS) : UNSUPPORTED;
+        double cpuUsage = UNSUPPORTED;
+        if (totalUpTimeNS > 0) {
+            cpuUsage = Math.min(100F, totalCpuTimeNS / (float) totalUpTimeNS);
+        }
 
         this.lastCpuTimeNS = cpuTimeNS;
         this.lastUpTimeMS = upTimeMS;
 
-        return cpuLoad;
+        return cpuUsage;
     }
 }
diff --git a/profiler-optional/profiler-optional-jdk6/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/ibm/Java6CpuLoadMetric.java b/profiler-optional/profiler-optional-jdk6/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/ibm/Java6CpuLoadMetric.java
index 6faf29537b21..d54426415f4c 100644
--- a/profiler-optional/profiler-optional-jdk6/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/ibm/Java6CpuLoadMetric.java
+++ b/profiler-optional/profiler-optional-jdk6/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/ibm/Java6CpuLoadMetric.java
@@ -16,11 +16,15 @@
 
 package com.navercorp.pinpoint.profiler.monitor.metric.cpu.ibm;
 
+import com.ibm.lang.management.OperatingSystemMXBean;
 import com.navercorp.pinpoint.profiler.monitor.metric.cpu.CpuLoadMetric;
 import com.navercorp.pinpoint.profiler.monitor.metric.cpu.CpuLoadMetricSnapshot;
+import com.navercorp.pinpoint.profiler.monitor.metric.cpu.CpuUsageProvider;
 import com.navercorp.pinpoint.profiler.monitor.metric.cpu.JvmCpuUsageCalculator;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-import java.lang.management.OperatingSystemMXBean;
+import java.lang.management.ManagementFactory;
 import java.lang.management.RuntimeMXBean;
 
 /**
@@ -28,32 +32,58 @@
  */
 public class Java6CpuLoadMetric implements CpuLoadMetric {
 
-    private final JvmCpuUsageCalculator jvmCpuUsageCalculator = new JvmCpuUsageCalculator();
-    private final com.ibm.lang.management.OperatingSystemMXBean operatingSystemMXBean;
-    private final RuntimeMXBean runtimeMXBean;
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
 
-    public Java6CpuLoadMetric(OperatingSystemMXBean operatingSystemMXBean, RuntimeMXBean runtimeMXBean) {
+    private CpuUsageProvider jvmCpuUsageProvider;
+
+    public Java6CpuLoadMetric() {
+        final OperatingSystemMXBean operatingSystemMXBean = (com.ibm.lang.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
         if (operatingSystemMXBean == null) {
-            throw new NullPointerException("operatingSystemMXBean must not be null");
+            throw new IllegalStateException("OperatingSystemMXBean not available");
+        }
+        RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
+        if (runtimeMXBean == null) {
+            throw new IllegalStateException("RuntimeMXBean not available");
         }
-        this.operatingSystemMXBean = (com.ibm.lang.management.OperatingSystemMXBean) operatingSystemMXBean;
-        this.runtimeMXBean = runtimeMXBean;
+
+        CpuUsageProvider jvmCpuUsageProvider = new JvmCpuUsageProvider(operatingSystemMXBean, runtimeMXBean);
+        try {
+            jvmCpuUsageProvider.getCpuUsage();
+        } catch (NoSuchMethodError e) {
+            logger.warn("Expected method not found for retrieving jvm cpu usage. Cause : {}", e.getMessage());
+            jvmCpuUsageProvider = CpuUsageProvider.UNSUPPORTED;
+        }
+        this.jvmCpuUsageProvider = jvmCpuUsageProvider;
     }
 
     @Override
     public CpuLoadMetricSnapshot getSnapshot() {
-        double jvmCpuUsage = UNCOLLECTED_USAGE;
-        if (runtimeMXBean != null) {
-            long cpuTimeNS = operatingSystemMXBean.getProcessCpuTimeByNS();
-            long upTimeMS = runtimeMXBean.getUptime();
-            jvmCpuUsage = jvmCpuUsageCalculator.getJvmCpuUsage(cpuTimeNS, upTimeMS);
-        }
-        double systemCpuUsage = operatingSystemMXBean.getSystemCpuLoad();
-        return new CpuLoadMetricSnapshot(jvmCpuUsage, systemCpuUsage);
+        double jvmCpuUsage = jvmCpuUsageProvider.getCpuUsage();
+        return new CpuLoadMetricSnapshot(jvmCpuUsage, UNCOLLECTED_USAGE);
     }
 
     @Override
     public String toString() {
         return "CpuLoadMetric for IBM Java 1.6";
     }
+
+    private static class JvmCpuUsageProvider implements CpuUsageProvider {
+
+        private final JvmCpuUsageCalculator jvmCpuUsageCalculator = new JvmCpuUsageCalculator();
+
+        private final OperatingSystemMXBean operatingSystemMXBean;
+        private final RuntimeMXBean runtimeMXBean;
+
+        private JvmCpuUsageProvider(OperatingSystemMXBean operatingSystemMXBean, RuntimeMXBean runtimeMXBean) {
+            this.operatingSystemMXBean = operatingSystemMXBean;
+            this.runtimeMXBean = runtimeMXBean;
+        }
+
+        @Override
+        public double getCpuUsage() {
+            long cpuTimeNS = operatingSystemMXBean.getProcessCpuTimeByNS();
+            long upTimeMS = runtimeMXBean.getUptime();
+            return jvmCpuUsageCalculator.getJvmCpuUsage(cpuTimeNS, upTimeMS);
+        }
+    }
 }
diff --git a/profiler-optional/profiler-optional-jdk6/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/oracle/Java6CpuLoadMetric.java b/profiler-optional/profiler-optional-jdk6/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/oracle/Java6CpuLoadMetric.java
index e3fe36eef104..d4f9ee627ac1 100644
--- a/profiler-optional/profiler-optional-jdk6/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/oracle/Java6CpuLoadMetric.java
+++ b/profiler-optional/profiler-optional-jdk6/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/oracle/Java6CpuLoadMetric.java
@@ -18,9 +18,13 @@
 
 import com.navercorp.pinpoint.profiler.monitor.metric.cpu.CpuLoadMetric;
 import com.navercorp.pinpoint.profiler.monitor.metric.cpu.CpuLoadMetricSnapshot;
+import com.navercorp.pinpoint.profiler.monitor.metric.cpu.CpuUsageProvider;
 import com.navercorp.pinpoint.profiler.monitor.metric.cpu.JvmCpuUsageCalculator;
+import com.sun.management.OperatingSystemMXBean;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-import java.lang.management.OperatingSystemMXBean;
+import java.lang.management.ManagementFactory;
 import java.lang.management.RuntimeMXBean;
 
 /**
@@ -28,32 +32,58 @@
  */
 public class Java6CpuLoadMetric implements CpuLoadMetric {
 
-    private final JvmCpuUsageCalculator jvmCpuUsageCalculator = new JvmCpuUsageCalculator();
-    private final com.sun.management.OperatingSystemMXBean operatingSystemMXBean;
-    private final RuntimeMXBean runtimeMXBean;
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
 
-    public Java6CpuLoadMetric(OperatingSystemMXBean operatingSystemMXBean, RuntimeMXBean runtimeMXBean) {
+    private final CpuUsageProvider jvmCpuUsageProvider;
+
+    public Java6CpuLoadMetric() {
+        final OperatingSystemMXBean operatingSystemMXBean = (com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
         if (operatingSystemMXBean == null) {
-            throw new NullPointerException("operatingSystemMXBean must not be null");
+            throw new IllegalStateException("OperatingSystemMXBean not available");
+        }
+        final RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
+        if (runtimeMXBean == null) {
+            throw new IllegalStateException("RuntimeMXBean not available");
         }
-        this.operatingSystemMXBean = (com.sun.management.OperatingSystemMXBean) operatingSystemMXBean;
-        this.runtimeMXBean = runtimeMXBean;
+
+        CpuUsageProvider jvmCpuUsageProvider = new JvmCpuUsageProvider(operatingSystemMXBean, runtimeMXBean);
+        try {
+            jvmCpuUsageProvider.getCpuUsage();
+        } catch (NoSuchMethodError e) {
+            logger.warn("Expected method not found for retrieving jvm cpu usage. Cause : {}", e.getMessage());
+            jvmCpuUsageProvider = CpuUsageProvider.UNSUPPORTED;
+        }
+        this.jvmCpuUsageProvider = jvmCpuUsageProvider;
     }
 
     @Override
     public CpuLoadMetricSnapshot getSnapshot() {
-        double jvmCpuUsage = UNCOLLECTED_USAGE;
-        if (runtimeMXBean != null) {
-            long cpuTimeNS = operatingSystemMXBean.getProcessCpuTime();
-            long upTimeMS = runtimeMXBean.getUptime();
-            jvmCpuUsage = jvmCpuUsageCalculator.getJvmCpuUsage(cpuTimeNS, upTimeMS);
-        }
-        double systemCpuUsage = UNCOLLECTED_USAGE;
-        return new CpuLoadMetricSnapshot(jvmCpuUsage, systemCpuUsage);
+        double jvmCpuUsage = jvmCpuUsageProvider.getCpuUsage();
+        return new CpuLoadMetricSnapshot(jvmCpuUsage, UNCOLLECTED_USAGE);
     }
 
     @Override
     public String toString() {
         return "CpuLoadMetric for Oracle Java 1.6";
     }
+
+    private static class JvmCpuUsageProvider implements CpuUsageProvider {
+
+        private final JvmCpuUsageCalculator jvmCpuUsageCalculator = new JvmCpuUsageCalculator();
+
+        private final OperatingSystemMXBean operatingSystemMXBean;
+        private final RuntimeMXBean runtimeMXBean;
+
+        private JvmCpuUsageProvider(OperatingSystemMXBean operatingSystemMXBean, RuntimeMXBean runtimeMXBean) {
+            this.operatingSystemMXBean = operatingSystemMXBean;
+            this.runtimeMXBean = runtimeMXBean;
+        }
+
+        @Override
+        public double getCpuUsage() {
+            long cpuTimeNS = operatingSystemMXBean.getProcessCpuTime();
+            long upTimeMS = runtimeMXBean.getUptime();
+            return jvmCpuUsageCalculator.getJvmCpuUsage(cpuTimeNS, upTimeMS);
+        }
+    }
 }
diff --git a/profiler-optional/profiler-optional-jdk7/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/ibm/DefaultCpuLoadMetric.java b/profiler-optional/profiler-optional-jdk7/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/ibm/DefaultCpuLoadMetric.java
index 7d6abb3b2c8b..1baeb68910f1 100644
--- a/profiler-optional/profiler-optional-jdk7/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/ibm/DefaultCpuLoadMetric.java
+++ b/profiler-optional/profiler-optional-jdk7/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/ibm/DefaultCpuLoadMetric.java
@@ -16,29 +16,54 @@
 
 package com.navercorp.pinpoint.profiler.monitor.metric.cpu.ibm;
 
+import com.ibm.lang.management.OperatingSystemMXBean;
 import com.navercorp.pinpoint.profiler.monitor.metric.cpu.CpuLoadMetric;
 import com.navercorp.pinpoint.profiler.monitor.metric.cpu.CpuLoadMetricSnapshot;
+import com.navercorp.pinpoint.profiler.monitor.metric.cpu.CpuUsageProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-import java.lang.management.OperatingSystemMXBean;
+import java.lang.management.ManagementFactory;
 
 /**
  * @author HyunGil Jeong
  */
 public class DefaultCpuLoadMetric implements CpuLoadMetric {
 
-    private final com.ibm.lang.management.OperatingSystemMXBean operatingSystemMXBean;
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
 
-    public DefaultCpuLoadMetric(OperatingSystemMXBean operatingSystemMXBean) {
+    private final CpuUsageProvider jvmCpuUsageProvider;
+    private final CpuUsageProvider systemCpuUsageProvider;
+
+    public DefaultCpuLoadMetric() {
+        final OperatingSystemMXBean operatingSystemMXBean = (com.ibm.lang.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
         if (operatingSystemMXBean == null) {
-            throw new NullPointerException("operatingSystemMXBean must not be null");
+            throw new IllegalStateException("OperatingSystemMXBean not available");
+        }
+
+        CpuUsageProvider jvmCpuUsageProvider = new JvmCpuUsageProvider(operatingSystemMXBean);
+        try {
+            jvmCpuUsageProvider.getCpuUsage();
+        } catch (NoSuchMethodError e) {
+            logger.warn("Expected method not found for retrieving jvm cpu usage. Cause : {}", e.getMessage());
+            jvmCpuUsageProvider = CpuUsageProvider.UNSUPPORTED;
+        }
+        this.jvmCpuUsageProvider = jvmCpuUsageProvider;
+
+        CpuUsageProvider systemCpuUsageProvider = new SystemCpuUsageProvider(operatingSystemMXBean);
+        try {
+            systemCpuUsageProvider.getCpuUsage();
+        } catch (NoSuchMethodError e) {
+            logger.warn("Expected method not found for retrieving system cpu usage. Cause : {}", e.getMessage());
+            systemCpuUsageProvider = CpuUsageProvider.UNSUPPORTED;
         }
-        this.operatingSystemMXBean = (com.ibm.lang.management.OperatingSystemMXBean) operatingSystemMXBean;
+        this.systemCpuUsageProvider = systemCpuUsageProvider;
     }
 
     @Override
     public CpuLoadMetricSnapshot getSnapshot() {
-        double jvmCpuUsage = operatingSystemMXBean.getProcessCpuLoad();
-        double systemCpuUsage = operatingSystemMXBean.getSystemCpuLoad();
+        double jvmCpuUsage = jvmCpuUsageProvider.getCpuUsage();
+        double systemCpuUsage = systemCpuUsageProvider.getCpuUsage();
         return new CpuLoadMetricSnapshot(jvmCpuUsage, systemCpuUsage);
     }
 
@@ -46,4 +71,32 @@ public CpuLoadMetricSnapshot getSnapshot() {
     public String toString() {
         return "CpuLoadMetric for IBM Java 1.7+";
     }
+
+    private static class JvmCpuUsageProvider implements CpuUsageProvider {
+
+        private final OperatingSystemMXBean operatingSystemMXBean;
+
+        private JvmCpuUsageProvider(OperatingSystemMXBean operatingSystemMXBean) {
+            this.operatingSystemMXBean = operatingSystemMXBean;
+        }
+
+        @Override
+        public double getCpuUsage() {
+            return operatingSystemMXBean.getProcessCpuLoad();
+        }
+    }
+
+    private static class SystemCpuUsageProvider implements CpuUsageProvider {
+
+        private final OperatingSystemMXBean operatingSystemMXBean;
+
+        private SystemCpuUsageProvider(OperatingSystemMXBean operatingSystemMXBean) {
+            this.operatingSystemMXBean = operatingSystemMXBean;
+        }
+
+        @Override
+        public double getCpuUsage() {
+            return operatingSystemMXBean.getSystemCpuLoad();
+        }
+    }
 }
diff --git a/profiler-optional/profiler-optional-jdk7/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/oracle/DefaultCpuLoadMetric.java b/profiler-optional/profiler-optional-jdk7/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/oracle/DefaultCpuLoadMetric.java
index 9954ba770128..5f3d011674a4 100644
--- a/profiler-optional/profiler-optional-jdk7/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/oracle/DefaultCpuLoadMetric.java
+++ b/profiler-optional/profiler-optional-jdk7/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/oracle/DefaultCpuLoadMetric.java
@@ -18,27 +18,52 @@
 
 import com.navercorp.pinpoint.profiler.monitor.metric.cpu.CpuLoadMetric;
 import com.navercorp.pinpoint.profiler.monitor.metric.cpu.CpuLoadMetricSnapshot;
+import com.navercorp.pinpoint.profiler.monitor.metric.cpu.CpuUsageProvider;
+import com.sun.management.OperatingSystemMXBean;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
-import java.lang.management.OperatingSystemMXBean;
+import java.lang.management.ManagementFactory;
 
 /**
  * @author HyunGil Jeong
  */
 public class DefaultCpuLoadMetric implements CpuLoadMetric {
 
-    private final com.sun.management.OperatingSystemMXBean operatingSystemMXBean;
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
 
-    public DefaultCpuLoadMetric(OperatingSystemMXBean operatingSystemMXBean) {
+    private final CpuUsageProvider jvmCpuUsageProvider;
+    private final CpuUsageProvider systemCpuUsageProvider;
+
+    public DefaultCpuLoadMetric() {
+        final OperatingSystemMXBean operatingSystemMXBean = (com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
         if (operatingSystemMXBean == null) {
-            throw new NullPointerException("operatingSystemMXBean must not be null");
+            throw new IllegalStateException("OperatingSystemMXBean not available");
+        }
+
+        CpuUsageProvider jvmCpuUsageProvider = new JvmCpuUsageProvider(operatingSystemMXBean);
+        try {
+            jvmCpuUsageProvider.getCpuUsage();
+        } catch (NoSuchMethodError e) {
+            logger.warn("Expected method not found for retrieving jvm cpu usage. Cause : {}", e.getMessage());
+            jvmCpuUsageProvider = CpuUsageProvider.UNSUPPORTED;
+        }
+        this.jvmCpuUsageProvider = jvmCpuUsageProvider;
+
+        CpuUsageProvider systemCpuUsageProvider = new SystemCpuUsageProvider(operatingSystemMXBean);
+        try {
+            systemCpuUsageProvider.getCpuUsage();
+        } catch (NoSuchMethodError e) {
+            logger.warn("Expected method not found for retrieving system cpu usage. Cause : {}", e.getMessage());
+            systemCpuUsageProvider = CpuUsageProvider.UNSUPPORTED;
         }
-        this.operatingSystemMXBean = (com.sun.management.OperatingSystemMXBean) operatingSystemMXBean;
+        this.systemCpuUsageProvider = systemCpuUsageProvider;
     }
 
     @Override
     public CpuLoadMetricSnapshot getSnapshot() {
-        double jvmCpuUsage = operatingSystemMXBean.getProcessCpuLoad();
-        double systemCpuUsage = operatingSystemMXBean.getSystemCpuLoad();
+        double jvmCpuUsage = jvmCpuUsageProvider.getCpuUsage();
+        double systemCpuUsage = systemCpuUsageProvider.getCpuUsage();
         return new CpuLoadMetricSnapshot(jvmCpuUsage, systemCpuUsage);
     }
 
@@ -46,4 +71,32 @@ public CpuLoadMetricSnapshot getSnapshot() {
     public String toString() {
         return "CpuLoadMetric for Oracle Java 1.7+";
     }
+
+    private static class JvmCpuUsageProvider implements CpuUsageProvider {
+
+        private final OperatingSystemMXBean operatingSystemMXBean;
+
+        private JvmCpuUsageProvider(OperatingSystemMXBean operatingSystemMXBean) {
+            this.operatingSystemMXBean = operatingSystemMXBean;
+        }
+
+        @Override
+        public double getCpuUsage() {
+            return operatingSystemMXBean.getProcessCpuLoad();
+        }
+    }
+
+    private static class SystemCpuUsageProvider implements CpuUsageProvider {
+
+        private final OperatingSystemMXBean operatingSystemMXBean;
+
+        private SystemCpuUsageProvider(OperatingSystemMXBean operatingSystemMXBean) {
+            this.operatingSystemMXBean = operatingSystemMXBean;
+        }
+
+        @Override
+        public double getCpuUsage() {
+            return operatingSystemMXBean.getSystemCpuLoad();
+        }
+    }
 }
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/provider/stat/cpu/CpuLoadMetricProvider.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/provider/stat/cpu/CpuLoadMetricProvider.java
index 322e31d4b252..f4d6faa4db26 100644
--- a/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/provider/stat/cpu/CpuLoadMetricProvider.java
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/context/provider/stat/cpu/CpuLoadMetricProvider.java
@@ -26,9 +26,6 @@
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.lang.management.ManagementFactory;
-import java.lang.management.OperatingSystemMXBean;
-import java.lang.management.RuntimeMXBean;
 import java.lang.reflect.Constructor;
 
 /**
@@ -85,27 +82,15 @@ private CpuLoadMetric createCpuLoadMetric(String classToLoad) {
         if (classToLoad == null) {
             return CpuLoadMetric.UNSUPPORTED_CPU_LOAD_METRIC;
         }
-        OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();
-        if (operatingSystemMXBean == null) {
-            return CpuLoadMetric.UNSUPPORTED_CPU_LOAD_METRIC;
-        }
         try {
             @SuppressWarnings("unchecked")
             Class<CpuLoadMetric> cpuLoadMetricClass = (Class<CpuLoadMetric>) Class.forName(classToLoad);
             try {
-                // Create CpuLoadMetric for Java 7+
-                Constructor<CpuLoadMetric> cpuLoadMetricConstructor = cpuLoadMetricClass.getConstructor(OperatingSystemMXBean.class);
-                return cpuLoadMetricConstructor.newInstance(operatingSystemMXBean);
-            } catch (NoSuchMethodException e1) {
-                try {
-                    // Create CpuLoadMetric for Java 6
-                    RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
-                    Constructor<CpuLoadMetric> cpuLoadMetricConstructor = cpuLoadMetricClass.getConstructor(OperatingSystemMXBean.class, RuntimeMXBean.class);
-                    return cpuLoadMetricConstructor.newInstance(operatingSystemMXBean, runtimeMXBean);
-                } catch (NoSuchMethodException e2) {
-                    logger.warn("Unknown CpuLoadMetric : {}", classToLoad);
-                    return CpuLoadMetric.UNSUPPORTED_CPU_LOAD_METRIC;
-                }
+                Constructor<CpuLoadMetric> cpuLoadMetricConstructor = cpuLoadMetricClass.getConstructor();
+                return cpuLoadMetricConstructor.newInstance();
+            } catch (NoSuchMethodException e) {
+                logger.warn("Unknown CpuLoadMetric : {}", classToLoad);
+                return CpuLoadMetric.UNSUPPORTED_CPU_LOAD_METRIC;
             }
         } catch (Exception e) {
             logger.warn("Error creating CpuLoadMetric [" + classToLoad + "]", e);
diff --git a/profiler/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/CpuUsageProvider.java b/profiler/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/CpuUsageProvider.java
new file mode 100644
index 000000000000..66f0717134d1
--- /dev/null
+++ b/profiler/src/main/java/com/navercorp/pinpoint/profiler/monitor/metric/cpu/CpuUsageProvider.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2018 NAVER Corp.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.navercorp.pinpoint.profiler.monitor.metric.cpu;
+
+/**
+ * @author HyunGil Jeong
+ */
+public interface CpuUsageProvider {
+
+    double getCpuUsage();
+
+    CpuUsageProvider UNSUPPORTED = new CpuUsageProvider() {
+        @Override
+        public double getCpuUsage() {
+            return CpuLoadMetric.UNCOLLECTED_USAGE;
+        }
+    };
+}


Test patch: