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}