com.gemstone.gemfire.internal.cache.lru.HeapLRUCapacityController Maven / Gradle / Ivy
Show all versions of gemfire-core Show documentation
/*
* Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License. See accompanying
* LICENSE file.
*/
package com.gemstone.gemfire.internal.cache.lru;
import java.util.Properties;
import com.gemstone.gemfire.StatisticDescriptor;
import com.gemstone.gemfire.StatisticsFactory;
import com.gemstone.gemfire.StatisticsType;
import com.gemstone.gemfire.StatisticsTypeFactory;
import com.gemstone.gemfire.cache.EvictionAction;
import com.gemstone.gemfire.cache.EvictionAlgorithm;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.util.ObjectSizer;
import com.gemstone.gemfire.internal.StatisticsTypeFactoryImpl;
import com.gemstone.gemfire.internal.cache.AbstractRegion;
import com.gemstone.gemfire.internal.cache.BucketRegion;
import com.gemstone.gemfire.internal.cache.GemFireCacheImpl;
import com.gemstone.gemfire.internal.cache.LocalRegion;
import com.gemstone.gemfire.internal.cache.Token;
import com.gemstone.gemfire.internal.cache.control.InternalResourceManager;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
/**
* A HeapLRUCapacityController
controls the contents of
* {@link Region} based on the percentage of memory that is
* currently being used. If the percentage of memory in use exceeds
* the given percentage, then the least recently used entry of the region is
* evicted.
*
*
*
* For heap regions:
* GemStone has found that the HeapLRUCapacityController
has the
* most effect on a VM that is lauched with both the -Xmx
and
* -Xms
switches used. Many virtual machine implementations have
* additional VM switches to control the behavior of the garbage collector. We
* suggest that you investigate tuning the garbage collector when using a
* HeapLRUCapacityController
. In particular, we have found that
* when running with Sun's HotSpot VM, the
* -XX:+UseConcMarkSweepGC
and -XX:+UseParNewGC
* options improve the behavior of the HeapLRUCapacityController
.
*
* @author David Whitlock
*
* @since 3.2
*/
@SuppressWarnings("synthetic-access")
public class HeapLRUCapacityController extends LRUAlgorithm {
private static final long serialVersionUID = 4970685814429530675L;
/**
* The default percentage of VM heap usage over which LRU eviction occurs
*/
public static final String TOP_UP_HEAP_EVICTION_PERCENTAGE_PROPERTY = "gemfire.topUpHeapEvictionPercentage";
public static final float DEFAULT_TOP_UP_HEAP_EVICTION_PERCENTAGE = 4.0f;
public static final int DEFAULT_HEAP_PERCENTAGE = 75;
public static final int PER_ENTRY_OVERHEAD = 250;
private int perEntryOverhead = PER_ENTRY_OVERHEAD;
/**
* The default number of milliseconds the evictor thread should wait before
* evicting the LRU entry.
*/
public static final int DEFAULT_EVICTOR_INTERVAL = 500;
protected static final StatisticsType statType;
static {
// create the stats type for MemLRU.
StatisticsTypeFactory f = StatisticsTypeFactoryImpl.singleton();
final String entryBytesDesc = "The amount of memory currently used by regions configured for eviction.";
final String lruEvictionsDesc = "Number of total entry evictions triggered by LRU.";
final String lruDestroysDesc = "Number of entries destroyed in the region through both destroy cache operations and eviction. Reset to zero each time it exceeds lruDestroysLimit.";
final String lruDestroysLimitDesc = "Maximum number of entry destroys triggered by LRU before scan occurs.";
final String lruEvaluationsDesc = "Number of entries evaluated during LRU operations.";
final String lruGreedyReturnsDesc = "Number of non-LRU entries evicted during LRU operations";
statType = f
.createType(
"HeapLRUStatistics",
"Statistics about byte based Least Recently Used region entry disposal",
new StatisticDescriptor[] {
f.createLongGauge("entryBytes", entryBytesDesc, "bytes"),
f.createLongCounter("lruEvictions", lruEvictionsDesc, "entries"),
f.createLongCounter("lruDestroys", lruDestroysDesc, "entries"),
f.createLongGauge("lruDestroysLimit", lruDestroysLimitDesc, "entries"),
f.createLongCounter("lruEvaluations", lruEvaluationsDesc, "entries"),
f.createLongCounter("lruGreedyReturns", lruGreedyReturnsDesc, "entries"), });
}
// //////////////////// Instance Fields /////////////////////
// ////////////////////// Constructors ///////////////////////
/**
* Creates a new HeapLRUCapacityController
with the given eviction action.
*
* @param evictionAction
* The action that will occur when an entry is evicted
*
*/
public HeapLRUCapacityController(EvictionAction evictionAction, Region region) {
super(evictionAction, region);
}
public HeapLRUCapacityController(ObjectSizer sizerImpl,EvictionAction evictionAction, Region region) {
super(evictionAction, region);
setSizer(sizerImpl);
}
// ///////////////////// Instance Methods ///////////////////////
@Override
public void setLimit(int maximum) {
}
// Candidate for removal since capacity controller is no longer part of
// cache.xml
@Override
public Properties getProperties() {
throw new IllegalStateException("Unused properties");
}
@Override
public void close() {
super.close();
}
@Override
public long getLimit() {
return 0;
}
@Override
public boolean equals(Object cc) {
if (!super.equals(cc))
return false;
return true;
}
/*
* (non-Javadoc)
* @see java.lang.Object#hashCode()
*
* Note that we just need to make sure that equal objects return equal
* hashcodes; nothing really elaborate is done here.
*/
@Override
public int hashCode() {
int result = super.hashCode();
return result;
}
/**
* Returns a brief description of this eviction controller.
*
* @since 4.0
*/
@Override
public String toString() {
return LocalizedStrings.HeapLRUCapacityController_HEAPLRUCAPACITYCONTROLLER_WITH_A_CAPACITY_OF_0_OF_HEAP_AND_AN_THREAD_INTERVAL_OF_1_AND_EVICTION_ACTION_2.toLocalizedString(new Object[] { Long.valueOf(this.getLimit()), this.getEvictionAction()});
}
/**
* Sets the {@link ObjectSizer} used to calculate the size of
* objects placed in the cache.
*
* @param sizer
* The name of the sizer class
*/
private void setSizer(ObjectSizer sizer) {
this.sizer = sizer;
}
@Override
protected EnableLRU createLRUHelper() {
return new AbstractEnableLRU() {
/**
* Indicate what kind of EvictionAlgorithm
this helper
* implements
*/
public EvictionAlgorithm getEvictionAlgorithm() {
return EvictionAlgorithm.LRU_HEAP;
}
/**
* As far as we're concerned all entries have the same size
*/
public int entrySize(Object key, Object value)
throws IllegalArgumentException {
// value is null only after eviction occurs. A change in size is
// required for eviction stats, bug 30974
/*
* if (value != null) { return 1; } else { return 0; }
*/
if (value == Token.TOMBSTONE) {
return 0;
}
int size = HeapLRUCapacityController.this.getPerEntryOverhead();
size += sizeof(key);
size += sizeof(value);
return size;
}
/**
* In addition to initializing the statistics, create an evictor thread to
* periodically evict the LRU entry.
*/
@Override
public LRUStatistics initStats(Object region, StatisticsFactory sf) {
setRegionName(region);
final LRUStatistics stats
= new HeapLRUStatistics(sf, getRegionName(), this);
setStats(stats);
return stats;
}
public StatisticsType getStatisticsType() {
return statType;
}
public String getStatisticsName() {
return "HeapLRUStatistics";
}
public int getLimitStatId() {
throw new UnsupportedOperationException("Limit not used with this LRU type");
}
public int getCountStatId() {
return statType.nameToId("entryBytes");
}
public int getEvictionsStatId() {
return statType.nameToId("lruEvictions");
}
public int getDestroysStatId() {
return statType.nameToId("lruDestroys");
}
public int getDestroysLimitStatId() {
return statType.nameToId("lruDestroysLimit");
}
public int getEvaluationsStatId() {
return statType.nameToId("lruEvaluations");
}
public int getGreedyReturnsStatId() {
return statType.nameToId("lruGreedyReturns");
}
/**
* Okay, deep breath. Instead of basing the LRU calculation on the number
* of entries in the region or on their "size" (which turned out to be
* incorrectly estimated in the general case), we use the amount of
* memory currently in use. If the amount of memory current in use
* {@linkplain Runtime#maxMemory max memory} -
* {@linkplain Runtime#freeMemory free memory} is greater than the
* overflow threshold, then we evict the LRU entry.
*/
public boolean mustEvict(LRUStatistics stats, Region region, int delta) {
final GemFireCacheImpl cache;
if (region != null) {
cache = (GemFireCacheImpl) region.getRegionService();
} else {
cache = GemFireCacheImpl.getInstance();
}
InternalResourceManager resourceManager = cache.getResourceManager();
if (region == null) {
return resourceManager.getHeapMonitor().getState().isEviction();
}
final boolean monitorStateIsEviction;
if (!((AbstractRegion) region).getEnableOffHeapMemory()) {
monitorStateIsEviction = resourceManager.getHeapMonitor().getState().isEviction();
} else {
monitorStateIsEviction = resourceManager.getOffHeapMonitor().getState().isEviction();
}
if (region instanceof BucketRegion) {
return monitorStateIsEviction && ((BucketRegion) region).getSizeForEviction() > 0;
}
return monitorStateIsEviction && ((LocalRegion) region).getRegionMap().sizeInVM() > 0;
}
};
}
// ////////////////////// Inner Classes ////////////////////////
private ObjectSizer sizer;
/**
* Return the size of an object as stored in GemFire... Typically this is the
* serialized size in bytes.. This implementation is slow.... Need to add
* Sizer interface and call it for customer objects.
*/
protected int sizeof(Object o) throws IllegalArgumentException {
return MemLRUCapacityController.basicSizeof(o, this.sizer);
}
public int getPerEntryOverhead() {
return perEntryOverhead;
}
public void setEntryOverHead(int entryOverHead) {
this.perEntryOverhead = entryOverHead;
}
}