001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2015 ForgeRock AS.
015 */
016package org.opends.server.core;
017
018import java.lang.management.ManagementFactory;
019import java.lang.management.MemoryPoolMXBean;
020import java.lang.management.MemoryUsage;
021import java.util.List;
022import java.util.concurrent.Semaphore;
023
024import static org.opends.server.util.ServerConstants.*;
025
026/**
027 * Estimates the amount of memory in the running JVM for use of long term caches
028 * by looking at the Old Generation, where implemented, or at the Runtime
029 * information as fallback. Allows for reserving memory to avoid over commitment.
030 * There is a fudge factor involved, so it is not byte exact.
031 */
032public final class MemoryQuota
033{
034  private static final long ONE_MEGABYTE = 1024 * 1024;
035
036  private Semaphore reservedMemory;
037  private int reservableMemory;
038  private boolean allowOvercommit;
039
040  /**
041   * Returns the memory quota reservation system for this server instance.
042   */
043  public MemoryQuota()
044  {
045    allowOvercommit = System.getProperty(ENABLE_MEMORY_OVERCOMMIT) != null;
046    reservableMemory = (int)(Math.pow(Math.E / Math.PI, 2) * (getOldGenInfo().getMax() / ONE_MEGABYTE));
047    reservedMemory = new Semaphore(reservableMemory, true);
048  }
049
050  /**
051   * Returns the maximum amount of memory the server will use when giving quotas.
052   * @return the maximum amount of memory the server will use when giving quotas
053   */
054  public long getMaxMemory()
055  {
056    return getOldGenInfo().getMax();
057  }
058
059  private MemoryUsage getOldGenInfo()
060  {
061    List<MemoryPoolMXBean> mpools = ManagementFactory.getMemoryPoolMXBeans();
062    for (MemoryPoolMXBean mpool : mpools)
063    {
064      MemoryUsage usage = mpool.getUsage();
065      if (usage != null && mpool.getName().endsWith("Old Gen"))
066      {
067        return usage;
068      }
069    }
070    Runtime runtime = Runtime.getRuntime();
071    return new MemoryUsage(0, runtime.totalMemory() - runtime.freeMemory(), runtime.totalMemory(), runtime.maxMemory());
072  }
073
074  /**
075   * Check enough memory is available in the reservable pool.
076   * @param size the amount of requested memory
077   * @return true if enough memory is available in the reservable pool
078  */
079  public boolean isMemoryAvailable(long size)
080  {
081    if (allowOvercommit)
082    {
083      return true;
084    }
085    if (acquireMemory(size))
086    {
087      releaseMemory(size);
088      return true;
089    }
090    return false;
091  }
092
093  /**
094   * Reserves the requested amount of memory in OldGen.
095   *
096   * @param size the requested amount of memory in bytes
097   * @return true if the requested amount of memory in OldGen could be reserved
098   */
099  public boolean acquireMemory(long size)
100  {
101    return allowOvercommit
102        || reservedMemory.tryAcquire((int) (size / ONE_MEGABYTE));
103  }
104
105  /**
106   * Returns how much memory is currently not reserved (free) in OldGen.
107   * @return how much memory is currently not reserved (free) in OldGen
108   */
109  public long getAvailableMemory()
110  {
111    if (allowOvercommit)
112    {
113      return reservableMemory * ONE_MEGABYTE;
114    }
115    return reservedMemory.availablePermits() * ONE_MEGABYTE;
116  }
117
118  /**
119   * Translates bytes to percent of reservable memory.
120   * @param size the amount of memory in bytes
121   * @return percent of reservable memory
122   */
123  public int memBytesToPercent(long size)
124  {
125    return (int)(((size / ONE_MEGABYTE) * 100) / reservableMemory);
126  }
127
128  /**
129   * Translates a percentage of memory to the equivalent number of bytes.
130   * @param percent a percentage of memory
131   * @return the equivalent number of bytes
132   */
133  public long memPercentToBytes(int percent)
134  {
135    return (reservableMemory * percent / 100) * ONE_MEGABYTE;
136  }
137
138  /**
139   * Declares OldGen memory is not needed anymore.
140   * @param size the amount of memory to return
141   */
142  public void releaseMemory(long size)
143  {
144    if (allowOvercommit)
145    {
146      return;
147    }
148    reservedMemory.release((int)(size / ONE_MEGABYTE));
149  }
150}