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