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.lang.management.ManagementFactory; 021import java.lang.management.RuntimeMXBean; 022import java.lang.reflect.InvocationTargetException; 023import java.lang.reflect.Method; 024import java.nio.ByteBuffer; 025import java.util.List; 026import java.util.Locale; 027import javax.management.JMException; 028import javax.management.MBeanServer; 029import javax.management.MalformedObjectNameException; 030import javax.management.ObjectName; 031import org.apache.yetus.audience.InterfaceAudience; 032import org.apache.yetus.audience.InterfaceStability; 033import org.slf4j.Logger; 034import org.slf4j.LoggerFactory; 035 036import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 037import org.apache.hbase.thirdparty.io.netty.buffer.ByteBufAllocatorMetric; 038import org.apache.hbase.thirdparty.io.netty.buffer.ByteBufAllocatorMetricProvider; 039import org.apache.hbase.thirdparty.io.netty.buffer.PooledByteBufAllocator; 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 -XX:MaxDirectMemorySize 082 * 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).replace("-xx:maxdirectmemorysize=", "").trim(); 091 092 if (memSize.contains("k")) { 093 multiplier = 1024; 094 } 095 096 else if (memSize.contains("m")) { 097 multiplier = 1048576; 098 } 099 100 else if (memSize.contains("g")) { 101 multiplier = 1073741824; 102 } 103 memSize = memSize.replaceAll("[^\\d]", ""); 104 105 long retValue = Long.parseLong(memSize); 106 return retValue * multiplier; 107 } 108 } 109 return 0; 110 } 111 112 /** Returns the current amount of direct memory used. */ 113 public static long getDirectMemoryUsage() { 114 if (BEAN_SERVER == null || NIO_DIRECT_POOL == null || !HAS_MEMORY_USED_ATTRIBUTE) return 0; 115 try { 116 Long value = (Long) BEAN_SERVER.getAttribute(NIO_DIRECT_POOL, MEMORY_USED); 117 return value == null ? 0 : value; 118 } catch (JMException e) { 119 // should print further diagnostic information? 120 return 0; 121 } 122 } 123 124 /** Returns the current amount of direct memory used by netty module. */ 125 public static long getNettyDirectMemoryUsage() { 126 127 ByteBufAllocatorMetric metric = 128 ((ByteBufAllocatorMetricProvider) PooledByteBufAllocator.DEFAULT).metric(); 129 return metric.usedDirectMemory(); 130 } 131 132 /** 133 * DirectByteBuffers are garbage collected by using a phantom reference and a reference queue. 134 * Every once a while, the JVM checks the reference queue and cleans the DirectByteBuffers. 135 * However, as this doesn't happen immediately after discarding all references to a 136 * DirectByteBuffer, it's easy to OutOfMemoryError yourself using DirectByteBuffers. This function 137 * explicitly calls the Cleaner method of a DirectByteBuffer. n * The DirectByteBuffer that will 138 * be "cleaned". Utilizes reflection. 139 */ 140 public static void destroyDirectByteBuffer(ByteBuffer toBeDestroyed) 141 throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, 142 SecurityException, NoSuchMethodException { 143 144 Preconditions.checkArgument(toBeDestroyed.isDirect(), "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}