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}