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}