001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.util; 019 020import java.io.BufferedReader; 021import java.io.IOException; 022import java.io.InputStream; 023import java.io.InputStreamReader; 024import java.lang.management.ManagementFactory; 025import java.lang.management.OperatingSystemMXBean; 026import java.lang.management.RuntimeMXBean; 027import java.lang.reflect.Method; 028import java.nio.charset.StandardCharsets; 029import java.util.Iterator; 030import org.apache.yetus.audience.InterfaceAudience; 031import org.slf4j.Logger; 032import org.slf4j.LoggerFactory; 033 034import org.apache.hbase.thirdparty.com.google.common.base.Splitter; 035 036/** 037 * This class is a wrapper for the implementation of com.sun.management.UnixOperatingSystemMXBean It 038 * will decide to use the sun api or its own implementation depending on the runtime (vendor) used. 039 */ 040 041@InterfaceAudience.Private 042public class JVM { 043 private static final Logger LOG = LoggerFactory.getLogger(JVM.class); 044 private OperatingSystemMXBean osMbean; 045 046 private static final boolean ibmvendor = 047 System.getProperty("java.vendor") != null && System.getProperty("java.vendor").contains("IBM"); 048 // At least on my systems os.name reports as "linux", not "Linux". Prefer case insensitive tests. 049 private static final boolean windows = System.getProperty("os.name") != null 050 && System.getProperty("os.name").toLowerCase().contains("windows"); 051 private static final boolean linux = System.getProperty("os.name") != null 052 && System.getProperty("os.name").toLowerCase().contains("linux"); 053 private static final boolean amd64 = 054 System.getProperty("os.arch") != null && System.getProperty("os.arch").contains("amd64"); 055 private static final boolean aarch64 = 056 System.getProperty("os.arch") != null && System.getProperty("os.arch").contains("aarch64"); 057 058 private static final String JVMVersion = System.getProperty("java.version"); 059 060 /** 061 * The raw String of java specification version. "1.8" for java8, "9","10"... for Java 9, 10... 062 */ 063 private static final String JVM_SPEC_VERSION_STRING = 064 System.getProperty("java.specification.version"); 065 066 /** 067 * The Integer represent of JVM_SPEC_VERSION, for the JVM version comparison. Java 8, 9, 10 ... 068 * will be noted as 8, 9 10 ... 069 */ 070 private static final int JVM_SPEC_VERSION = JVM_SPEC_VERSION_STRING.contains(".") 071 ? (int) (Float.parseFloat(JVM_SPEC_VERSION_STRING) * 10 % 10) 072 : Integer.parseInt(JVM_SPEC_VERSION_STRING); 073 074 /** 075 * Constructor. Get the running Operating System instance 076 */ 077 public JVM() { 078 this.osMbean = ManagementFactory.getOperatingSystemMXBean(); 079 } 080 081 /** 082 * Check if the OS is unix. 083 * @return whether this is unix or not. 084 */ 085 public static boolean isUnix() { 086 if (windows) { 087 return false; 088 } 089 return (ibmvendor ? linux : true); 090 } 091 092 /** 093 * Check if the OS is linux. 094 * @return whether this is linux or not. 095 */ 096 public static boolean isLinux() { 097 return linux; 098 } 099 100 /** 101 * Check if the arch is amd64; 102 * @return whether this is amd64 or not. 103 */ 104 public static boolean isAmd64() { 105 return amd64; 106 } 107 108 /** 109 * Check if the arch is aarch64; 110 * @return whether this is aarch64 or not. 111 */ 112 public static boolean isAarch64() { 113 return aarch64; 114 } 115 116 /** 117 * Check if the finish() method of GZIPOutputStream is broken 118 * @return whether GZIPOutputStream.finish() is broken. 119 */ 120 public static boolean isGZIPOutputStreamFinishBroken() { 121 return ibmvendor && JVMVersion.contains("1.6.0"); 122 } 123 124 public static int getJVMSpecVersion() { 125 return JVM_SPEC_VERSION; 126 } 127 128 /** 129 * Load the implementation of UnixOperatingSystemMXBean for Oracle jvm and runs the desired 130 * method. 131 * @param mBeanMethodName : method to run from the interface UnixOperatingSystemMXBean 132 * @return the method result 133 */ 134 private Long runUnixMXBeanMethod(String mBeanMethodName) { 135 Object unixos; 136 Class<?> classRef; 137 Method mBeanMethod; 138 139 try { 140 classRef = Class.forName("com.sun.management.UnixOperatingSystemMXBean"); 141 if (classRef.isInstance(osMbean)) { 142 mBeanMethod = classRef.getMethod(mBeanMethodName); 143 unixos = classRef.cast(osMbean); 144 return (Long) mBeanMethod.invoke(unixos); 145 } 146 } catch (Exception e) { 147 LOG.warn( 148 "Not able to load class or method for" + " com.sun.management.UnixOperatingSystemMXBean.", 149 e); 150 } 151 return null; 152 } 153 154 /** 155 * Get the number of opened filed descriptor for the runtime jvm. If Oracle java, it will use the 156 * com.sun.management interfaces. Otherwise, this methods implements it (linux only). 157 * @return number of open file descriptors for the jvm 158 */ 159 public long getOpenFileDescriptorCount() { 160 Long ofdc; 161 162 if (!ibmvendor) { 163 ofdc = runUnixMXBeanMethod("getOpenFileDescriptorCount"); 164 return (ofdc != null ? ofdc : -1); 165 } 166 InputStream inputStream = null; 167 InputStreamReader inputStreamReader = null; 168 BufferedReader bufferedReader = null; 169 try { 170 // need to get the PID number of the process first 171 RuntimeMXBean rtmbean = ManagementFactory.getRuntimeMXBean(); 172 String rtname = rtmbean.getName(); 173 Iterator<String> pidhost = Splitter.on('@').split(rtname).iterator(); 174 // using linux bash commands to retrieve info 175 Process p = Runtime.getRuntime() 176 .exec(new String[] { "bash", "-c", "ls /proc/" + pidhost.next() + "/fdinfo | wc -l" }); 177 inputStream = p.getInputStream(); 178 inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); 179 bufferedReader = new BufferedReader(inputStreamReader); 180 String openFileDesCount; 181 if ((openFileDesCount = bufferedReader.readLine()) != null) { 182 return Long.parseLong(openFileDesCount); 183 } 184 } catch (IOException ie) { 185 LOG.warn("Not able to get the number of open file descriptors", ie); 186 } finally { 187 if (bufferedReader != null) { 188 try { 189 bufferedReader.close(); 190 } catch (IOException e) { 191 LOG.warn("Not able to close the BufferedReader", e); 192 } 193 } 194 if (inputStreamReader != null) { 195 try { 196 inputStreamReader.close(); 197 } catch (IOException e) { 198 LOG.warn("Not able to close the InputStreamReader", e); 199 } 200 } 201 if (inputStream != null) { 202 try { 203 inputStream.close(); 204 } catch (IOException e) { 205 LOG.warn("Not able to close the InputStream", e); 206 } 207 } 208 } 209 return -1; 210 } 211 212 /** 213 * Get the system load average 214 * @see java.lang.management.OperatingSystemMXBean#getSystemLoadAverage 215 */ 216 public double getSystemLoadAverage() { 217 return osMbean.getSystemLoadAverage(); 218 } 219 220 /** 221 * Return the physical free memory (not the JVM one, as it's not very useful as it depends on the 222 * GC), but the one from the OS as it allows a little bit more to guess if the machine is 223 * overloaded or not). 224 */ 225 public long getFreeMemory() { 226 if (ibmvendor) { 227 return 0; 228 } 229 230 Long r = runUnixMXBeanMethod("getFreePhysicalMemorySize"); 231 return (r != null ? r : -1); 232 } 233 234 /** 235 * Workaround to get the current number of process running. Approach is the one described here: 236 * http://stackoverflow.com/questions/54686/how-to-get-a-list-of-current-open-windows-process-with-java 237 */ 238 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RV_DONT_JUST_NULL_CHECK_READLINE", 239 justification = "used by testing") 240 public int getNumberOfRunningProcess() { 241 if (!isUnix()) { 242 return 0; 243 } 244 245 InputStream inputStream = null; 246 InputStreamReader inputStreamReader = null; 247 BufferedReader bufferedReader = null; 248 249 try { 250 int count = 0; 251 Process p = Runtime.getRuntime().exec("ps -e"); 252 inputStream = p.getInputStream(); 253 inputStreamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8); 254 bufferedReader = new BufferedReader(inputStreamReader); 255 while (bufferedReader.readLine() != null) { 256 count++; 257 } 258 return count - 1; // -1 because there is a headline 259 } catch (IOException e) { 260 return -1; 261 } finally { 262 if (bufferedReader != null) { 263 try { 264 bufferedReader.close(); 265 } catch (IOException e) { 266 LOG.warn("Not able to close the BufferedReader", e); 267 } 268 } 269 if (inputStreamReader != null) { 270 try { 271 inputStreamReader.close(); 272 } catch (IOException e) { 273 LOG.warn("Not able to close the InputStreamReader", e); 274 } 275 } 276 if (inputStream != null) { 277 try { 278 inputStream.close(); 279 } catch (IOException e) { 280 LOG.warn("Not able to close the InputStream", e); 281 } 282 } 283 } 284 } 285 286 /** 287 * Get the number of the maximum file descriptors the system can use. If Oracle java, it will use 288 * the com.sun.management interfaces. Otherwise, this methods implements it (linux only). 289 * @return max number of file descriptors the operating system can use. 290 */ 291 public long getMaxFileDescriptorCount() { 292 Long mfdc; 293 if (!ibmvendor) { 294 mfdc = runUnixMXBeanMethod("getMaxFileDescriptorCount"); 295 return (mfdc != null ? mfdc : -1); 296 } 297 InputStream in = null; 298 BufferedReader output = null; 299 try { 300 // using linux bash commands to retrieve info 301 Process p = Runtime.getRuntime().exec(new String[] { "bash", "-c", "ulimit -n" }); 302 in = p.getInputStream(); 303 output = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)); 304 String maxFileDesCount; 305 if ((maxFileDesCount = output.readLine()) != null) { 306 return Long.parseLong(maxFileDesCount); 307 } 308 } catch (IOException ie) { 309 LOG.warn("Not able to get the max number of file descriptors", ie); 310 } finally { 311 if (output != null) { 312 try { 313 output.close(); 314 } catch (IOException e) { 315 LOG.warn("Not able to close the reader", e); 316 } 317 } 318 if (in != null) { 319 try { 320 in.close(); 321 } catch (IOException e) { 322 LOG.warn("Not able to close the InputStream", e); 323 } 324 } 325 } 326 return -1; 327 } 328}