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}