001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019 020package org.apache.hadoop.hbase.util; 021 022import java.lang.management.ManagementFactory; 023import java.lang.management.RuntimeMXBean; 024import java.lang.reflect.InvocationTargetException; 025import java.lang.reflect.Method; 026import java.nio.ByteBuffer; 027import java.util.List; 028import java.util.Locale; 029 030import javax.management.JMException; 031import javax.management.MBeanServer; 032import javax.management.MalformedObjectNameException; 033import javax.management.ObjectName; 034 035import org.apache.yetus.audience.InterfaceAudience; 036import org.apache.yetus.audience.InterfaceStability; 037import org.slf4j.Logger; 038import org.slf4j.LoggerFactory; 039import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 040 041/** 042 * Utilities for interacting with and monitoring DirectByteBuffer allocations. 043 */ 044@InterfaceAudience.Private 045@InterfaceStability.Evolving 046public class DirectMemoryUtils { 047 private static final Logger LOG = LoggerFactory.getLogger(DirectMemoryUtils.class); 048 private static final String MEMORY_USED = "MemoryUsed"; 049 private static final MBeanServer BEAN_SERVER; 050 private static final ObjectName NIO_DIRECT_POOL; 051 private static final boolean HAS_MEMORY_USED_ATTRIBUTE; 052 053 static { 054 // initialize singletons. Only maintain a reference to the MBeanServer if 055 // we're able to consume it -- hence convoluted logic. 056 ObjectName n = null; 057 MBeanServer s = null; 058 Object a = null; 059 try { 060 n = new ObjectName("java.nio:type=BufferPool,name=direct"); 061 } catch (MalformedObjectNameException e) { 062 LOG.warn("Unable to initialize ObjectName for DirectByteBuffer allocations."); 063 } finally { 064 NIO_DIRECT_POOL = n; 065 } 066 if (NIO_DIRECT_POOL != null) { 067 s = ManagementFactory.getPlatformMBeanServer(); 068 } 069 BEAN_SERVER = s; 070 if (BEAN_SERVER != null) { 071 try { 072 a = BEAN_SERVER.getAttribute(NIO_DIRECT_POOL, MEMORY_USED); 073 } catch (JMException e) { 074 LOG.debug("Failed to retrieve nio.BufferPool direct MemoryUsed attribute: " + e); 075 } 076 } 077 HAS_MEMORY_USED_ATTRIBUTE = a != null; 078 } 079 080 /** 081 * @return the setting of -XX:MaxDirectMemorySize as a long. Returns 0 if 082 * -XX:MaxDirectMemorySize is not set. 083 */ 084 public static long getDirectMemorySize() { 085 RuntimeMXBean runtimemxBean = ManagementFactory.getRuntimeMXBean(); 086 List<String> arguments = runtimemxBean.getInputArguments(); 087 long multiplier = 1; //for the byte case. 088 for (String s : arguments) { 089 if (s.contains("-XX:MaxDirectMemorySize=")) { 090 String memSize = s.toLowerCase(Locale.ROOT) 091 .replace("-xx:maxdirectmemorysize=", "").trim(); 092 093 if (memSize.contains("k")) { 094 multiplier = 1024; 095 } 096 097 else if (memSize.contains("m")) { 098 multiplier = 1048576; 099 } 100 101 else if (memSize.contains("g")) { 102 multiplier = 1073741824; 103 } 104 memSize = memSize.replaceAll("[^\\d]", ""); 105 106 long retValue = Long.parseLong(memSize); 107 return retValue * multiplier; 108 } 109 } 110 return 0; 111 } 112 113 /** 114 * @return the current amount of direct memory used. 115 */ 116 public static long getDirectMemoryUsage() { 117 if (BEAN_SERVER == null || NIO_DIRECT_POOL == null || !HAS_MEMORY_USED_ATTRIBUTE) return 0; 118 try { 119 Long value = (Long) BEAN_SERVER.getAttribute(NIO_DIRECT_POOL, MEMORY_USED); 120 return value == null ? 0 : value; 121 } catch (JMException e) { 122 // should print further diagnostic information? 123 return 0; 124 } 125 } 126 127 /** 128 * DirectByteBuffers are garbage collected by using a phantom reference and a 129 * reference queue. Every once a while, the JVM checks the reference queue and 130 * cleans the DirectByteBuffers. However, as this doesn't happen 131 * immediately after discarding all references to a DirectByteBuffer, it's 132 * easy to OutOfMemoryError yourself using DirectByteBuffers. This function 133 * explicitly calls the Cleaner method of a DirectByteBuffer. 134 * 135 * @param toBeDestroyed 136 * The DirectByteBuffer that will be "cleaned". Utilizes reflection. 137 * 138 */ 139 public static void destroyDirectByteBuffer(ByteBuffer toBeDestroyed) 140 throws IllegalArgumentException, IllegalAccessException, 141 InvocationTargetException, SecurityException, NoSuchMethodException { 142 143 Preconditions.checkArgument(toBeDestroyed.isDirect(), 144 "toBeDestroyed isn't direct!"); 145 146 Method cleanerMethod = toBeDestroyed.getClass().getMethod("cleaner"); 147 cleanerMethod.setAccessible(true); 148 Object cleaner = cleanerMethod.invoke(toBeDestroyed); 149 Method cleanMethod = cleaner.getClass().getMethod("clean"); 150 cleanMethod.setAccessible(true); 151 cleanMethod.invoke(cleaner); 152 } 153}