/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2015 ForgeRock AS. */ package org.opends.server.core; import java.lang.management.ManagementFactory; import java.lang.management.MemoryPoolMXBean; import java.lang.management.MemoryUsage; import java.util.List; import java.util.concurrent.Semaphore; import static org.opends.server.util.ServerConstants.*; /** * Estimates the amount of memory in the running JVM for use of long term caches * by looking at the Old Generation, where implemented, or at the Runtime * information as fallback. Allows for reserving memory to avoid over commitment. * There is a fudge factor involved, so it is not byte exact. */ public final class MemoryQuota { private static final long ONE_MEGABYTE = 1024 * 1024; private Semaphore reservedMemory; private int reservableMemory; private boolean allowOvercommit; /** * Returns the memory quota reservation system for this server instance. */ public MemoryQuota() { allowOvercommit = System.getProperty(ENABLE_MEMORY_OVERCOMMIT) != null; reservableMemory = (int)(Math.pow(Math.E / Math.PI, 2) * (getOldGenInfo().getMax() / ONE_MEGABYTE)); reservedMemory = new Semaphore(reservableMemory, true); } /** * Returns the maximum amount of memory the server will use when giving quotas. * @return the maximum amount of memory the server will use when giving quotas */ public long getMaxMemory() { return getOldGenInfo().getMax(); } private MemoryUsage getOldGenInfo() { List<MemoryPoolMXBean> mpools = ManagementFactory.getMemoryPoolMXBeans(); for (MemoryPoolMXBean mpool : mpools) { MemoryUsage usage = mpool.getUsage(); if (usage != null && mpool.getName().endsWith("Old Gen")) { return usage; } } Runtime runtime = Runtime.getRuntime(); return new MemoryUsage(0, runtime.totalMemory() - runtime.freeMemory(), runtime.totalMemory(), runtime.maxMemory()); } /** * Check enough memory is available in the reservable pool. * @param size the amount of requested memory * @return true if enough memory is available in the reservable pool */ public boolean isMemoryAvailable(long size) { if (allowOvercommit) { return true; } if (acquireMemory(size)) { releaseMemory(size); return true; } return false; } /** * Reserves the requested amount of memory in OldGen. * * @param size the requested amount of memory in bytes * @return true if the requested amount of memory in OldGen could be reserved */ public boolean acquireMemory(long size) { return allowOvercommit || reservedMemory.tryAcquire((int) (size / ONE_MEGABYTE)); } /** * Returns how much memory is currently not reserved (free) in OldGen. * @return how much memory is currently not reserved (free) in OldGen */ public long getAvailableMemory() { if (allowOvercommit) { return reservableMemory * ONE_MEGABYTE; } return reservedMemory.availablePermits() * ONE_MEGABYTE; } /** * Translates bytes to percent of reservable memory. * @param size the amount of memory in bytes * @return percent of reservable memory */ public int memBytesToPercent(long size) { return (int)(((size / ONE_MEGABYTE) * 100) / reservableMemory); } /** * Translates a percentage of memory to the equivalent number of bytes. * @param percent a percentage of memory * @return the equivalent number of bytes */ public long memPercentToBytes(int percent) { return (reservableMemory * percent / 100) * ONE_MEGABYTE; } /** * Declares OldGen memory is not needed anymore. * @param size the amount of memory to return */ public void releaseMemory(long size) { if (allowOvercommit) { return; } reservedMemory.release((int)(size / ONE_MEGABYTE)); } }