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