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; 040import org.apache.hbase.thirdparty.io.netty.buffer.ByteBufAllocatorMetric; 041import org.apache.hbase.thirdparty.io.netty.buffer.ByteBufAllocatorMetricProvider; 042import org.apache.hbase.thirdparty.io.netty.buffer.PooledByteBufAllocator; 043 044 045/** 046 * Utilities for interacting with and monitoring DirectByteBuffer allocations. 047 */ 048@InterfaceAudience.Private 049@InterfaceStability.Evolving 050public class DirectMemoryUtils { 051 private static final Logger LOG = LoggerFactory.getLogger(DirectMemoryUtils.class); 052 private static final String MEMORY_USED = "MemoryUsed"; 053 private static final MBeanServer BEAN_SERVER; 054 private static final ObjectName NIO_DIRECT_POOL; 055 private static final boolean HAS_MEMORY_USED_ATTRIBUTE; 056 057 static { 058 // initialize singletons. Only maintain a reference to the MBeanServer if 059 // we're able to consume it -- hence convoluted logic. 060 ObjectName n = null; 061 MBeanServer s = null; 062 Object a = null; 063 try { 064 n = new ObjectName("java.nio:type=BufferPool,name=direct"); 065 } catch (MalformedObjectNameException e) { 066 LOG.warn("Unable to initialize ObjectName for DirectByteBuffer allocations."); 067 } finally { 068 NIO_DIRECT_POOL = n; 069 } 070 if (NIO_DIRECT_POOL != null) { 071 s = ManagementFactory.getPlatformMBeanServer(); 072 } 073 BEAN_SERVER = s; 074 if (BEAN_SERVER != null) { 075 try { 076 a = BEAN_SERVER.getAttribute(NIO_DIRECT_POOL, MEMORY_USED); 077 } catch (JMException e) { 078 LOG.debug("Failed to retrieve nio.BufferPool direct MemoryUsed attribute: " + e); 079 } 080 } 081 HAS_MEMORY_USED_ATTRIBUTE = a != null; 082 } 083 084 /** 085 * @return the setting of -XX:MaxDirectMemorySize as a long. Returns 0 if 086 * -XX:MaxDirectMemorySize is not set. 087 */ 088 public static long getDirectMemorySize() { 089 RuntimeMXBean runtimemxBean = ManagementFactory.getRuntimeMXBean(); 090 List<String> arguments = runtimemxBean.getInputArguments(); 091 long multiplier = 1; //for the byte case. 092 for (String s : arguments) { 093 if (s.contains("-XX:MaxDirectMemorySize=")) { 094 String memSize = s.toLowerCase(Locale.ROOT) 095 .replace("-xx:maxdirectmemorysize=", "").trim(); 096 097 if (memSize.contains("k")) { 098 multiplier = 1024; 099 } 100 101 else if (memSize.contains("m")) { 102 multiplier = 1048576; 103 } 104 105 else if (memSize.contains("g")) { 106 multiplier = 1073741824; 107 } 108 memSize = memSize.replaceAll("[^\\d]", ""); 109 110 long retValue = Long.parseLong(memSize); 111 return retValue * multiplier; 112 } 113 } 114 return 0; 115 } 116 117 /** 118 * @return the current amount of direct memory used. 119 */ 120 public static long getDirectMemoryUsage() { 121 if (BEAN_SERVER == null || NIO_DIRECT_POOL == null || !HAS_MEMORY_USED_ATTRIBUTE) return 0; 122 try { 123 Long value = (Long) BEAN_SERVER.getAttribute(NIO_DIRECT_POOL, MEMORY_USED); 124 return value == null ? 0 : value; 125 } catch (JMException e) { 126 // should print further diagnostic information? 127 return 0; 128 } 129 } 130 131 /** 132 * @return the current amount of direct memory used by netty module. 133 */ 134 public static long getNettyDirectMemoryUsage() { 135 136 ByteBufAllocatorMetric metric = ((ByteBufAllocatorMetricProvider) 137 PooledByteBufAllocator.DEFAULT).metric(); 138 return metric.usedDirectMemory(); 139 } 140 141 /** 142 * DirectByteBuffers are garbage collected by using a phantom reference and a 143 * reference queue. Every once a while, the JVM checks the reference queue and 144 * cleans the DirectByteBuffers. However, as this doesn't happen 145 * immediately after discarding all references to a DirectByteBuffer, it's 146 * easy to OutOfMemoryError yourself using DirectByteBuffers. This function 147 * explicitly calls the Cleaner method of a DirectByteBuffer. 148 * 149 * @param toBeDestroyed 150 * The DirectByteBuffer that will be "cleaned". Utilizes reflection. 151 * 152 */ 153 public static void destroyDirectByteBuffer(ByteBuffer toBeDestroyed) 154 throws IllegalArgumentException, IllegalAccessException, 155 InvocationTargetException, SecurityException, NoSuchMethodException { 156 157 Preconditions.checkArgument(toBeDestroyed.isDirect(), 158 "toBeDestroyed isn't direct!"); 159 160 Method cleanerMethod = toBeDestroyed.getClass().getMethod("cleaner"); 161 cleanerMethod.setAccessible(true); 162 Object cleaner = cleanerMethod.invoke(toBeDestroyed); 163 Method cleanMethod = cleaner.getClass().getMethod("clean"); 164 cleanMethod.setAccessible(true); 165 cleanMethod.invoke(cleaner); 166 } 167}