This is an automated email from the ASF dual-hosted git repository.
alberto pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/geode.git
The following commit(s) were added to refs/heads/develop by this push:
new a350ed2 GEODE-10087: Enhance off-heap fragmentation visibility.
(#7407)
a350ed2 is described below
commit a350ed22d912bee409e62fda3770dba91631d7a3
Author: Alberto Gomez <[email protected]>
AuthorDate: Wed Mar 30 13:26:07 2022 +0200
GEODE-10087: Enhance off-heap fragmentation visibility. (#7407)
* GEODE-10087: Enhance off-heap fragmentation visibility.
As per RFC
https://cwiki.apache.org/confluence/display/GEODE/Enhance+Off-heap+memory+fragmentation+visibility
Geode's off-heap fragmentation visibility has been improved
by adding a new stat: freedChunks as well as
by the periodic update of the largestFragment stat.
The new one stat will also be periodically updated
with a default frequency of one hour that can be changed by the
update-off-heap-stats-frequency-ms system property.
Besides, the new stat and the largestFragment stat
will be published via JMX.
* GEODE-10087: Update after review
* GEODE-10087: Rename method after review
---
.../src/main/resources/japicmp_exceptions.json | 6 +-
.../MemberMXBeanAttributesDistributedTest.java | 51 ++++++++++++++-
.../geode/internal/offheap/FreeListManager.java | 47 +++++++++++++-
.../internal/offheap/MemoryAllocatorImpl.java | 45 +++++++++++---
.../geode/internal/offheap/OffHeapMemoryStats.java | 4 ++
.../geode/internal/offheap/OffHeapStorage.java | 31 +++++++++-
.../offheap/OffHeapStoredObjectAddressStack.java | 11 ++++
.../org/apache/geode/management/MemberMXBean.java | 6 ++
.../management/internal/beans/MemberMBean.java | 12 ++++
.../internal/beans/MemberMBeanBridge.java | 30 +++++++++
.../OffHeapStorageNonRuntimeStatsJUnitTest.java | 72 ++++++++++++++++++++++
.../internal/cli/commands/ShowMetricsCommand.java | 7 +++
.../internal/offheap/NullOffHeapMemoryStats.java | 8 +++
13 files changed, 316 insertions(+), 14 deletions(-)
diff --git a/buildSrc/src/main/resources/japicmp_exceptions.json
b/buildSrc/src/main/resources/japicmp_exceptions.json
index be20d4c..0fc957c 100755
--- a/buildSrc/src/main/resources/japicmp_exceptions.json
+++ b/buildSrc/src/main/resources/japicmp_exceptions.json
@@ -1,9 +1,13 @@
{
"Class
org.apache.geode.management.builder.GeodeClusterManagementServiceBuilder":
"Moved internal class to fix split packages between geode-core and
geode-management",
"Class org.apache.geode.management.api.ClusterManagementOperation": "Fixed
missing @Experimental annotation",
+ "Class org.apache.geode.management.MemberMXBean": "Added new stats",
"Method
org.apache.geode.management.api.ClusterManagementOperation.getEndpoint()":
"Fixed missing @Experimental annotation",
"Method
org.apache.geode.management.api.ClusterManagementOperation.getOperator()":
"Fixed missing @Experimental annotation",
"Class org.apache.geode.cache.query.IndexStatistics": "Added new methods.",
"Method
org.apache.geode.cache.query.IndexStatistics.getNumberOfBucketIndexesLong()":
"Added new methods.",
- "Method
org.apache.geode.cache.query.IndexStatistics.getReadLockCountLong()": "Added
new methods."
+ "Method
org.apache.geode.cache.query.IndexStatistics.getReadLockCountLong()": "Added
new methods.",
+ "Method org.apache.geode.management.MemberMXBean.getOffHeapFragments()":
"Added new stat",
+ "Method org.apache.geode.management.MemberMXBean.getOffHeapFreedChunks()":
"Added new stat",
+ "Method
org.apache.geode.management.MemberMXBean.getOffHeapLargestFragment()": "Added
new stat"
}
diff --git
a/geode-core/src/distributedTest/java/org/apache/geode/management/MemberMXBeanAttributesDistributedTest.java
b/geode-core/src/distributedTest/java/org/apache/geode/management/MemberMXBeanAttributesDistributedTest.java
index 22cff50..b890d99 100644
---
a/geode-core/src/distributedTest/java/org/apache/geode/management/MemberMXBeanAttributesDistributedTest.java
+++
b/geode-core/src/distributedTest/java/org/apache/geode/management/MemberMXBeanAttributesDistributedTest.java
@@ -23,6 +23,7 @@ import static
org.apache.geode.distributed.ConfigurationProperties.HTTP_SERVICE_
import static org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER;
import static
org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_PORT;
import static
org.apache.geode.distributed.ConfigurationProperties.JMX_MANAGER_START;
+import static
org.apache.geode.distributed.ConfigurationProperties.OFF_HEAP_MEMORY_SIZE;
import static
org.apache.geode.distributed.ConfigurationProperties.STATISTIC_SAMPLE_RATE;
import static
org.apache.geode.distributed.ConfigurationProperties.STATISTIC_SAMPLING_ENABLED;
import static
org.apache.geode.internal.process.ProcessUtils.identifyPidAsUnchecked;
@@ -45,6 +46,7 @@ import org.apache.geode.internal.statistics.HostStatSampler;
import org.apache.geode.internal.statistics.SampleCollector;
import org.apache.geode.management.internal.SystemManagementService;
import org.apache.geode.test.dunit.rules.DistributedRestoreSystemProperties;
+import org.apache.geode.util.internal.GeodeGlossary;
/**
* Distributed tests for {@link MemberMXBean} attributes.
@@ -162,6 +164,50 @@ public class MemberMXBeanAttributesDistributedTest extends
CacheTestCase {
assertThat(memberMXBean.isManagerCreated()).isFalse();
}
+ @Test
+ public void testOffHeapMemoryAttributes() {
+ MemberMXBean memberMXBean = getSystemManagementService().getMemberMXBean();
+ sampleStatistics();
+
+ int initialLargestFragment = (int) (((4096 * BYTES_PER_MEGABYTE) / 2) - 1);
+ assertThat(memberMXBean.getOffHeapFragments()).isEqualTo(2);
+
assertThat(memberMXBean.getOffHeapLargestFragment()).isEqualTo(initialLargestFragment);
+ assertThat(memberMXBean.getOffHeapFreedChunks()).isEqualTo(0);
+
+ RegionFactory regionFactory =
+ getCache().createRegionFactory(PARTITION_REDUNDANT);
+ regionFactory.setConcurrencyChecksEnabled(false);
+ regionFactory.setOffHeap(true);
+
+ regionFactory.create("testPRRegion1");
+ Region region1 = getCache().getRegion(SEPARATOR + "testPRRegion1");
+
+ // fill first fragment
+ int hugeAllocations = 100;
+ for (int i = 0; i < hugeAllocations; i++) {
+ region1.put(i + 10, new byte[initialLargestFragment / hugeAllocations]);
+ }
+ for (int i = 0; i < hugeAllocations; i++) {
+ region1.remove(i + 10);
+ }
+
+ region1.put(1, new byte[100]);
+ region1.remove(1);
+ // Release the memory of the object so that the next allocation reuses the
freed chunk
+ region1.put(2, new byte[100]);
+ region1.remove(2);
+ region1.put(3, new byte[200]);
+ region1.remove(3);
+
+ sampleStatistics();
+
+ assertThat(memberMXBean.getOffHeapFragments()).isEqualTo(2);
+ await().untilAsserted(() ->
assertThat(memberMXBean.getOffHeapLargestFragment())
+ .isLessThan(initialLargestFragment));
+ await().untilAsserted(
+ () ->
assertThat(memberMXBean.getOffHeapFreedChunks()).isEqualTo(hugeAllocations +
2));
+ }
+
@Override
public Properties getDistributedSystemProperties() {
Properties props = new Properties();
@@ -174,7 +220,10 @@ public class MemberMXBeanAttributesDistributedTest extends
CacheTestCase {
}
private void createMember() {
- getCache(getDistributedSystemProperties());
+ Properties props = getDistributedSystemProperties();
+ props.setProperty(OFF_HEAP_MEMORY_SIZE, "4096");
+ System.setProperty(GeodeGlossary.GEMFIRE_PREFIX +
"off-heap-stats-update-frequency-ms", "1000");
+ getCache(props);
}
private void createManager() {
diff --git
a/geode-core/src/main/java/org/apache/geode/internal/offheap/FreeListManager.java
b/geode-core/src/main/java/org/apache/geode/internal/offheap/FreeListManager.java
index c7154be..04c84f5 100644
---
a/geode-core/src/main/java/org/apache/geode/internal/offheap/FreeListManager.java
+++
b/geode-core/src/main/java/org/apache/geode/internal/offheap/FreeListManager.java
@@ -252,8 +252,10 @@ public class FreeListManager {
OffHeapMemoryStats stats = ma.getStats();
lw.info("OutOfOffHeapMemory allocating size of " + chunkSize + ".
allocated="
+ allocatedSize.get() + " defragmentations=" +
defragmentationCount.get()
- + " objects=" + stats.getObjects() + " free=" + stats.getFreeMemory()
+ " fragments="
- + stats.getFragments() + " largestFragment=" +
stats.getLargestFragment()
+ + " objects=" + stats.getObjects() + " free=" + stats.getFreeMemory()
+ + " freedChunks=" + stats.getFreedChunks()
+ + " fragments=" + stats.getFragments()
+ + " largestFragment=" + stats.getLargestFragment()
+ " fragmentation=" + stats.getFragmentation());
logFragmentState(lw);
logTinyState(lw);
@@ -518,10 +520,51 @@ public class FreeListManager {
ma.getStats().setLargestFragment(largestFragment);
ma.getStats().setFragments(tmp.size());
ma.getStats().setFragmentation(getFragmentation());
+ ma.getStats().setFreedChunks(0);
return result;
}
+ public void updateNonRealTimeStats() {
+ ma.getStats().setLargestFragment(largestFragmentSize());
+ ma.getStats().setFreedChunks(getFreedChunks());
+ }
+
+ public int getFreedChunks() {
+ int elementCountFromTinyFreeLists =
+ getElementCountFromTinyFreeLists();
+ int elementCountFromHugeFreeLists =
+ getElementCountFromHugeFreeLists();
+
+ return elementCountFromTinyFreeLists + elementCountFromHugeFreeLists;
+ }
+
+ private int getElementCountFromTinyFreeLists() {
+ int fragmentCount = 0;
+ for (int i = 0; i < tinyFreeLists.length(); i++) {
+ OffHeapStoredObjectAddressStack cl = tinyFreeLists.get(i);
+ if (cl != null) {
+ fragmentCount += cl.size();
+ }
+ }
+ return fragmentCount;
+ }
+
+ private int getElementCountFromHugeFreeLists() {
+ return hugeChunkSet.size();
+ }
+
+ private int largestFragmentSize() {
+ int largestFreeSpaceFromFragments = 0;
+ for (Fragment f : fragmentList) {
+ int fragmentFreeSpace = f.freeSpace();
+ if (fragmentFreeSpace > largestFreeSpaceFromFragments) {
+ largestFreeSpaceFromFragments = fragmentFreeSpace;
+ }
+ }
+ return largestFreeSpaceFromFragments;
+ }
+
/**
* Unit tests override this method to get better test coverage
*/
diff --git
a/geode-core/src/main/java/org/apache/geode/internal/offheap/MemoryAllocatorImpl.java
b/geode-core/src/main/java/org/apache/geode/internal/offheap/MemoryAllocatorImpl.java
index 54dd458..4e433e4 100644
---
a/geode-core/src/main/java/org/apache/geode/internal/offheap/MemoryAllocatorImpl.java
+++
b/geode-core/src/main/java/org/apache/geode/internal/offheap/MemoryAllocatorImpl.java
@@ -20,6 +20,9 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.logging.log4j.Logger;
@@ -33,8 +36,10 @@ import org.apache.geode.internal.cache.InternalRegion;
import org.apache.geode.internal.cache.PartitionedRegion;
import org.apache.geode.internal.cache.PartitionedRegionDataStore;
import org.apache.geode.internal.cache.RegionEntry;
+import org.apache.geode.internal.lang.SystemProperty;
import org.apache.geode.internal.offheap.annotations.OffHeapIdentifier;
import org.apache.geode.internal.offheap.annotations.Unretained;
+import org.apache.geode.logging.internal.executors.LoggingExecutors;
import org.apache.geode.logging.internal.log4j.api.LogService;
import org.apache.geode.util.internal.GeodeGlossary;
@@ -55,6 +60,14 @@ public class MemoryAllocatorImpl implements MemoryAllocator {
public static final String FREE_OFF_HEAP_MEMORY_PROPERTY =
GeodeGlossary.GEMFIRE_PREFIX + "free-off-heap-memory";
+ public static final int UPDATE_OFF_HEAP_STATS_FREQUENCY_MS =
+ SystemProperty.getProductIntegerProperty(
+ "off-heap-stats-update-frequency-ms").orElse(3600000);
+
+ private final ScheduledExecutorService updateNonRealTimeStatsExecutor;
+
+ private final ScheduledFuture<?> updateNonRealTimeStatsFuture;
+
private volatile OffHeapMemoryStats stats;
private volatile OutOfOffHeapMemoryListener ooohml;
@@ -84,14 +97,21 @@ public class MemoryAllocatorImpl implements MemoryAllocator
{
Boolean.getBoolean(GeodeGlossary.GEMFIRE_PREFIX +
"OFF_HEAP_DO_EXPENSIVE_VALIDATION");
public static MemoryAllocator create(OutOfOffHeapMemoryListener ooohml,
OffHeapMemoryStats stats,
+ int slabCount, long offHeapMemorySize, long maxSlabSize,
+ int updateOffHeapStatsFrequencyMs) {
+ return create(ooohml, stats, slabCount, offHeapMemorySize, maxSlabSize,
null,
+ SlabImpl::new, updateOffHeapStatsFrequencyMs);
+ }
+
+ public static MemoryAllocator create(OutOfOffHeapMemoryListener ooohml,
OffHeapMemoryStats stats,
int slabCount, long offHeapMemorySize, long maxSlabSize) {
return create(ooohml, stats, slabCount, offHeapMemorySize, maxSlabSize,
null,
- SlabImpl::new);
+ SlabImpl::new, UPDATE_OFF_HEAP_STATS_FREQUENCY_MS);
}
private static MemoryAllocatorImpl create(OutOfOffHeapMemoryListener ooohml,
OffHeapMemoryStats stats, int slabCount, long offHeapMemorySize, long
maxSlabSize,
- Slab[] slabs, SlabFactory slabFactory) {
+ Slab[] slabs, SlabFactory slabFactory, int
updateOffHeapStatsFrequencyMs) {
MemoryAllocatorImpl result = singleton;
boolean created = false;
try {
@@ -135,7 +155,7 @@ public class MemoryAllocatorImpl implements MemoryAllocator
{
}
}
- result = new MemoryAllocatorImpl(ooohml, stats, slabs);
+ result = new MemoryAllocatorImpl(ooohml, stats, slabs,
updateOffHeapStatsFrequencyMs);
singleton = result;
LifecycleListener.invokeAfterCreate(result);
created = true;
@@ -156,7 +176,8 @@ public class MemoryAllocatorImpl implements MemoryAllocator
{
static MemoryAllocatorImpl createForUnitTest(OutOfOffHeapMemoryListener
ooohml,
OffHeapMemoryStats stats, int slabCount, long offHeapMemorySize, long
maxSlabSize,
SlabFactory memChunkFactory) {
- return create(ooohml, stats, slabCount, offHeapMemorySize, maxSlabSize,
null, memChunkFactory);
+ return create(ooohml, stats, slabCount, offHeapMemorySize, maxSlabSize,
null, memChunkFactory,
+ UPDATE_OFF_HEAP_STATS_FREQUENCY_MS);
}
public static MemoryAllocatorImpl
createForUnitTest(OutOfOffHeapMemoryListener oooml,
@@ -174,7 +195,8 @@ public class MemoryAllocatorImpl implements MemoryAllocator
{
}
}
}
- return create(oooml, stats, slabCount, offHeapMemorySize, maxSlabSize,
slabs, null);
+ return create(oooml, stats, slabCount, offHeapMemorySize, maxSlabSize,
slabs, null,
+ UPDATE_OFF_HEAP_STATS_FREQUENCY_MS);
}
@@ -200,11 +222,11 @@ public class MemoryAllocatorImpl implements
MemoryAllocator {
}
private MemoryAllocatorImpl(final OutOfOffHeapMemoryListener oooml,
- final OffHeapMemoryStats stats, final Slab[] slabs) {
+ final OffHeapMemoryStats stats, final Slab[] slabs,
+ int updateOffHeapStatsFrequencyMs) {
if (oooml == null) {
throw new IllegalArgumentException("OutOfOffHeapMemoryListener is null");
}
-
ooohml = oooml;
this.stats = stats;
@@ -216,6 +238,12 @@ public class MemoryAllocatorImpl implements
MemoryAllocator {
this.stats.incMaxMemory(freeList.getTotalMemory());
this.stats.incFreeMemory(freeList.getTotalMemory());
+
+ updateNonRealTimeStatsExecutor =
+ LoggingExecutors.newSingleThreadScheduledExecutor("Update Freelist
Stats thread");
+ updateNonRealTimeStatsFuture =
+
updateNonRealTimeStatsExecutor.scheduleAtFixedRate(freeList::updateNonRealTimeStats,
0,
+ updateOffHeapStatsFrequencyMs, TimeUnit.MILLISECONDS);
}
public List<OffHeapStoredObject> getLostChunks(InternalCache cache) {
@@ -379,6 +407,8 @@ public class MemoryAllocatorImpl implements MemoryAllocator
{
if (setClosed()) {
freeList.freeSlabs();
stats.close();
+ updateNonRealTimeStatsFuture.cancel(true);
+ updateNonRealTimeStatsExecutor.shutdown();
singleton = null;
}
}
@@ -510,5 +540,4 @@ public class MemoryAllocatorImpl implements MemoryAllocator
{
public MemoryInspector getMemoryInspector() {
return memoryInspector;
}
-
}
diff --git
a/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapMemoryStats.java
b/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapMemoryStats.java
index 08e716d..b539798 100755
---
a/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapMemoryStats.java
+++
b/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapMemoryStats.java
@@ -43,6 +43,8 @@ public interface OffHeapMemoryStats {
void setFragmentation(int value);
+ void setFreedChunks(long value);
+
long getFreeMemory();
long getMaxMemory();
@@ -59,6 +61,8 @@ public interface OffHeapMemoryStats {
long getFragments();
+ long getFreedChunks();
+
int getLargestFragment();
int getFragmentation();
diff --git
a/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapStorage.java
b/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapStorage.java
index a5ef246..755fef9 100755
---
a/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapStorage.java
+++
b/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapStorage.java
@@ -61,6 +61,7 @@ public class OffHeapStorage implements OffHeapMemoryStats {
private static final int defragmentationTimeId;
private static final int fragmentationId;
private static final int defragmentationsInProgressId;
+ private static final int freedChunksId;
// NOTE!!!! When adding new stats make sure and update the initialize method
on this class
// creates and registers the statistics type
@@ -78,10 +79,12 @@ public class OffHeapStorage implements OffHeapMemoryStats {
"The percentage of off-heap free memory that is fragmented. Updated
every time a defragmentation is performed.";
final String fragmentsDesc =
"The number of fragments of free off-heap memory. Updated every time a
defragmentation is done.";
+ final String freedChunksDesc =
+ "The number of off-heap memory chunks that have been freed since the
last defragmentation and that are not currently being used to store an object
in the off-heap memory space. Updated every time a defragmentation is done and
periodically according to off-heap-stats-update-frequency-ms system property
(default 3600 seconds).";
final String freeMemoryDesc =
"The amount of off-heap memory, in bytes, that is not being used.";
final String largestFragmentDesc =
- "The largest fragment of memory found by the last defragmentation of
off heap memory. Updated every time a defragmentation is done.";
+ "The largest fragment of off-heap memory that can be used to allocate
an object. Updated every time a defragmentation is done and periodically
according to off-heap-stats-update-frequency-ms system property (default 3600
seconds)";
final String objectsDesc = "The number of objects stored in off-heap
memory.";
final String readsDesc =
"The total number of reads of off-heap memory. Only reads of a full
object increment this statistic. If only a part of the object is read this
statistic is not incremented.";
@@ -94,6 +97,7 @@ public class OffHeapStorage implements OffHeapMemoryStats {
final String defragmentationTime = "defragmentationTime";
final String fragmentation = "fragmentation";
final String fragments = "fragments";
+ final String freedChunks = "freedChunks";
final String freeMemory = "freeMemory";
final String largestFragment = "largestFragment";
final String objects = "objects";
@@ -108,6 +112,7 @@ public class OffHeapStorage implements OffHeapMemoryStats {
f.createLongCounter(defragmentationTime, defragmentationTimeDesc,
"nanoseconds", false),
f.createIntGauge(fragmentation, fragmentationDesc, "percentage"),
f.createLongGauge(fragments, fragmentsDesc, "fragments"),
+ f.createLongGauge(freedChunks, freedChunksDesc, "freedChunks"),
f.createLongGauge(freeMemory, freeMemoryDesc, "bytes"),
f.createIntGauge(largestFragment, largestFragmentDesc, "bytes"),
f.createIntGauge(objects, objectsDesc, "objects"),
@@ -116,6 +121,7 @@ public class OffHeapStorage implements OffHeapMemoryStats {
usedMemoryId = statsType.nameToId(usedMemory);
defragmentationId = statsType.nameToId(defragmentations);
+ freedChunksId = statsType.nameToId(freedChunks);
defragmentationsInProgressId =
statsType.nameToId(defragmentationsInProgress);
defragmentationTimeId = statsType.nameToId(defragmentationTime);
fragmentationId = statsType.nameToId(fragmentation);
@@ -220,7 +226,6 @@ public class OffHeapStorage implements OffHeapMemoryStats {
OutOfOffHeapMemoryListener ooohml) {
final OffHeapMemoryStats stats = new OffHeapStorage(sf);
- // determine off-heap and slab sizes
final long maxSlabSize = calcMaxSlabSize(offHeapMemorySize);
final int slabCount = calcSlabCount(maxSlabSize, offHeapMemorySize);
@@ -228,6 +233,18 @@ public class OffHeapStorage implements OffHeapMemoryStats {
return MemoryAllocatorImpl.create(ooohml, stats, slabCount,
offHeapMemorySize, maxSlabSize);
}
+ static MemoryAllocator basicCreateOffHeapStorage(StatisticsFactory sf, long
offHeapMemorySize,
+ OutOfOffHeapMemoryListener ooohml, int updateOffHeapStatsFrequencyMs) {
+ final OffHeapMemoryStats stats = new OffHeapStorage(sf);
+
+ final long maxSlabSize = calcMaxSlabSize(offHeapMemorySize);
+
+ final int slabCount = calcSlabCount(maxSlabSize, offHeapMemorySize);
+
+ return MemoryAllocatorImpl.create(ooohml, stats, slabCount,
offHeapMemorySize, maxSlabSize,
+ updateOffHeapStatsFrequencyMs);
+ }
+
private static final long MAX_SLAB_SIZE = Integer.MAX_VALUE;
static final long MIN_SLAB_SIZE = 1024;
@@ -343,6 +360,11 @@ public class OffHeapStorage implements OffHeapMemoryStats {
}
@Override
+ public long getFreedChunks() {
+ return this.stats.getLong(freedChunksId);
+ }
+
+ @Override
public void setLargestFragment(int value) {
stats.setInt(largestFragmentId, value);
}
@@ -383,6 +405,11 @@ public class OffHeapStorage implements OffHeapMemoryStats {
}
@Override
+ public void setFreedChunks(long value) {
+ stats.setLong(freedChunksId, value);
+ }
+
+ @Override
public int getFragmentation() {
return stats.getInt(fragmentationId);
}
diff --git
a/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapStoredObjectAddressStack.java
b/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapStoredObjectAddressStack.java
index ae4d31c..86efa42 100644
---
a/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapStoredObjectAddressStack.java
+++
b/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapStoredObjectAddressStack.java
@@ -27,6 +27,9 @@ public class OffHeapStoredObjectAddressStack implements
LongStack {
// Ok to read without sync but must be synced on write
private volatile long topAddr;
+ // Ok to read without sync but must be synced on write
+ private volatile int size = 0;
+
public OffHeapStoredObjectAddressStack(long addr) {
if (addr != 0L) {
MemoryAllocatorImpl.validateAddress(addr);
@@ -48,9 +51,14 @@ public class OffHeapStoredObjectAddressStack implements
LongStack {
synchronized (this) {
OffHeapStoredObject.setNext(e, topAddr);
topAddr = e;
+ size++;
}
}
+ public int size() {
+ return size;
+ }
+
@Override
public long poll() {
long result;
@@ -58,7 +66,9 @@ public class OffHeapStoredObjectAddressStack implements
LongStack {
result = topAddr;
if (result != 0L) {
topAddr = OffHeapStoredObject.getNext(result);
+ size--;
}
+
}
return result;
}
@@ -81,6 +91,7 @@ public class OffHeapStoredObjectAddressStack implements
LongStack {
if (result != 0L) {
topAddr = 0L;
}
+ size = 0;
}
return result;
}
diff --git
a/geode-core/src/main/java/org/apache/geode/management/MemberMXBean.java
b/geode-core/src/main/java/org/apache/geode/management/MemberMXBean.java
index f81a272..c077cad 100644
--- a/geode-core/src/main/java/org/apache/geode/management/MemberMXBean.java
+++ b/geode-core/src/main/java/org/apache/geode/management/MemberMXBean.java
@@ -877,6 +877,12 @@ public interface MemberMXBean {
*/
int getOffHeapFragmentation();
+ long getOffHeapFragments();
+
+ long getOffHeapFreedChunks();
+
+ int getOffHeapLargestFragment();
+
/**
* Returns the total time spent compacting in milliseconds.
*/
diff --git
a/geode-core/src/main/java/org/apache/geode/management/internal/beans/MemberMBean.java
b/geode-core/src/main/java/org/apache/geode/management/internal/beans/MemberMBean.java
index fab63c0..045e6b3 100644
---
a/geode-core/src/main/java/org/apache/geode/management/internal/beans/MemberMBean.java
+++
b/geode-core/src/main/java/org/apache/geode/management/internal/beans/MemberMBean.java
@@ -696,6 +696,18 @@ public class MemberMBean extends
NotificationBroadcasterSupport implements Membe
return bridge.getOffHeapFragmentation();
}
+ public long getOffHeapFragments() {
+ return bridge.getOffHeapFragments();
+ }
+
+ public long getOffHeapFreedChunks() {
+ return bridge.getOffHeapFreedChunks();
+ }
+
+ public int getOffHeapLargestFragment() {
+ return bridge.getOffHeapLargestFragment();
+ }
+
@Override
public long getOffHeapCompactionTime() {
return bridge.getOffHeapCompactionTime();
diff --git
a/geode-core/src/main/java/org/apache/geode/management/internal/beans/MemberMBeanBridge.java
b/geode-core/src/main/java/org/apache/geode/management/internal/beans/MemberMBeanBridge.java
index 8128e8e..8746e66 100644
---
a/geode-core/src/main/java/org/apache/geode/management/internal/beans/MemberMBeanBridge.java
+++
b/geode-core/src/main/java/org/apache/geode/management/internal/beans/MemberMBeanBridge.java
@@ -1352,6 +1352,36 @@ public class MemberMBeanBridge {
return 0;
}
+ long getOffHeapFragments() {
+ OffHeapMemoryStats stats = getOffHeapStats();
+
+ if (null != stats) {
+ return stats.getFragments();
+ }
+
+ return 0;
+ }
+
+ long getOffHeapFreedChunks() {
+ OffHeapMemoryStats stats = getOffHeapStats();
+
+ if (null != stats) {
+ return stats.getFreedChunks();
+ }
+
+ return 0;
+ }
+
+ int getOffHeapLargestFragment() {
+ OffHeapMemoryStats stats = getOffHeapStats();
+
+ if (null != stats) {
+ return stats.getLargestFragment();
+ }
+
+ return 0;
+ }
+
long getOffHeapCompactionTime() {
OffHeapMemoryStats stats = getOffHeapStats();
diff --git
a/geode-core/src/test/java/org/apache/geode/internal/offheap/OffHeapStorageNonRuntimeStatsJUnitTest.java
b/geode-core/src/test/java/org/apache/geode/internal/offheap/OffHeapStorageNonRuntimeStatsJUnitTest.java
new file mode 100755
index 0000000..2aecc7b
--- /dev/null
+++
b/geode-core/src/test/java/org/apache/geode/internal/offheap/OffHeapStorageNonRuntimeStatsJUnitTest.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
contributor license
+ * agreements. See the NOTICE file distributed with this work for additional
information regarding
+ * copyright ownership. The ASF licenses this file to You 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.
+ */
+package org.apache.geode.internal.offheap;
+
+import static org.apache.geode.test.awaitility.GeodeAwaitility.await;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+import org.junit.Test;
+
+import org.apache.geode.StatisticsFactory;
+import org.apache.geode.internal.statistics.LocalStatisticsFactory;
+
+public class OffHeapStorageNonRuntimeStatsJUnitTest {
+
+ @Test
+ public void testUpdateNonRealTimeOffHeapStorageStats() {
+ StatisticsFactory localStatsFactory = new LocalStatisticsFactory(null);
+ OutOfOffHeapMemoryListener ooohml = mock(OutOfOffHeapMemoryListener.class);
+ MemoryAllocator ma =
+ OffHeapStorage.basicCreateOffHeapStorage(localStatsFactory, 1024 *
1024, ooohml, 100);
+ try {
+ OffHeapMemoryStats stats = ma.getStats();
+
+ assertThat(stats.getFreeMemory()).isEqualTo(1024 * 1024);
+ assertThat(stats.getMaxMemory()).isEqualTo(1024 * 1024);
+ assertThat(stats.getUsedMemory()).isEqualTo(0);
+ assertThat(stats.getFragments()).isEqualTo(1);
+ assertThat(stats.getLargestFragment()).isEqualTo(1024 * 1024);
+ assertThat(stats.getFreedChunks()).isEqualTo(0);
+
+ FreeListManager freeListManager = ((MemoryAllocatorImpl)
ma).getFreeListManager();
+ StoredObject storedObject1 = ma.allocate(10);
+ // Release the memory of the object so that the next allocation reuses
the freed chunk
+ ReferenceCounter.release(storedObject1.getAddress(), freeListManager);
+ StoredObject storedObject2 = ma.allocate(10);
+ StoredObject storedObject3 = ma.allocate(10);
+
+ assertThat(stats.getFreeMemory()).isLessThan(1024 * 1024);
+ assertThat(stats.getUsedMemory()).isGreaterThan(0);
+ await().untilAsserted(() ->
assertThat(stats.getLargestFragment()).isLessThan(1024 * 1024));
+ await().untilAsserted(() ->
assertThat(stats.getFreedChunks()).isEqualTo(0));
+
+ ReferenceCounter.release(storedObject3.getAddress(), freeListManager);
+ ReferenceCounter.release(storedObject2.getAddress(), freeListManager);
+
+ assertThat(stats.getFreeMemory()).isEqualTo(1024 * 1024);
+ assertThat(stats.getUsedMemory()).isEqualTo(0);
+ await().untilAsserted(() ->
assertThat(stats.getLargestFragment()).isLessThan(1024 * 1024));
+ await().untilAsserted(() ->
assertThat(stats.getFreedChunks()).isEqualTo(2));
+ } finally {
+ System.setProperty(MemoryAllocatorImpl.FREE_OFF_HEAP_MEMORY_PROPERTY,
"true");
+ try {
+ ma.close();
+ } finally {
+
System.clearProperty(MemoryAllocatorImpl.FREE_OFF_HEAP_MEMORY_PROPERTY);
+ }
+ }
+ }
+}
diff --git
a/geode-gfsh/src/main/java/org/apache/geode/management/internal/cli/commands/ShowMetricsCommand.java
b/geode-gfsh/src/main/java/org/apache/geode/management/internal/cli/commands/ShowMetricsCommand.java
index 8ad698d..ce269cb 100644
---
a/geode-gfsh/src/main/java/org/apache/geode/management/internal/cli/commands/ShowMetricsCommand.java
+++
b/geode-gfsh/src/main/java/org/apache/geode/management/internal/cli/commands/ShowMetricsCommand.java
@@ -502,6 +502,13 @@ public class ShowMetricsCommand extends GfshCommand {
writeToTableAndCsv(metricsTable, "", "objects",
memberMxBean.getOffHeapObjects(), csvBuilder);
writeToTableAndCsv(metricsTable, "", "fragmentation",
memberMxBean.getOffHeapFragmentation(),
csvBuilder);
+ writeToTableAndCsv(metricsTable, "", "largestFragment",
+ memberMxBean.getOffHeapLargestFragment(),
+ csvBuilder);
+ writeToTableAndCsv(metricsTable, "", "fragments",
memberMxBean.getOffHeapFragments(),
+ csvBuilder);
+ writeToTableAndCsv(metricsTable, "", "freedChunks",
memberMxBean.getOffHeapFreedChunks(),
+ csvBuilder);
writeToTableAndCsv(metricsTable, "", "compactionTime",
memberMxBean.getOffHeapCompactionTime(), csvBuilder);
}
diff --git
a/geode-junit/src/main/java/org/apache/geode/internal/offheap/NullOffHeapMemoryStats.java
b/geode-junit/src/main/java/org/apache/geode/internal/offheap/NullOffHeapMemoryStats.java
index bf17f87..7b2a134 100755
---
a/geode-junit/src/main/java/org/apache/geode/internal/offheap/NullOffHeapMemoryStats.java
+++
b/geode-junit/src/main/java/org/apache/geode/internal/offheap/NullOffHeapMemoryStats.java
@@ -88,6 +88,11 @@ public class NullOffHeapMemoryStats implements
OffHeapMemoryStats {
}
@Override
+ public long getFreedChunks() {
+ return 0;
+ }
+
+ @Override
public void setLargestFragment(int value) {}
@Override
@@ -107,6 +112,9 @@ public class NullOffHeapMemoryStats implements
OffHeapMemoryStats {
public void setFragmentation(int value) {}
@Override
+ public void setFreedChunks(long value) {}
+
+ @Override
public int getFragmentation() {
return 0;
}