This is an automated email from the ASF dual-hosted git repository.

dschneider 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 1210cbc  GEODE-9493: rework sizing of RedisString, RedisHash, 
RedisSet, and RedisSortedSet (#6727)
1210cbc is described below

commit 1210cbc48c80df9d4f87a94cd417e424b85cbccf
Author: Darrel Schneider <dar...@vmware.com>
AuthorDate: Mon Aug 16 09:09:42 2021 -0700

    GEODE-9493: rework sizing of RedisString, RedisHash, RedisSet, and 
RedisSortedSet (#6727)
    
    Removed SizeableObjectSizer and made the Sizeable redis classes abstract.
    They now require subclasses that implement sizeKey, sizeValue, or 
sizeElement.
    
    Instead of having hard coded constants stating what the size of a redis 
base class is,
    the code now uses JvmSizeUtils.memoryOverhead(Class) to statically compute 
the class size overhead.
    This allows the size to be correct for different jvm configs.
    
    Also simplified the sizing logic to use int and if needed use casts instead 
of narrow.
    Narrow was more expensive and would still cause problems as the size goes 
back down toward zero.
    
    The sizing of OrderStatisticsTree was simplified some more since it did not 
need an extra int field
    since we do not keep track of its element sizes.
    
    Also the size of the hashing strategy object is no longer accounted for 
since it is a singleton.
    
    OrderStatisticsTree has been simplified to not size the elements it 
contains.
    It is currently only used by RedisSortedSet and it does not need the element
    size to be computed (it already is done in the hash map it also uses).
    If in the future we need to also size
    the elements of an OrderStatisticsTree we can figure out the best
    way to do that then in that new context.
---
 .../hash/MemoryOverheadIntegrationTest.java        |   2 +-
 .../apache/geode/codeAnalysis/excludedClasses.txt  |   3 +
 .../codeAnalysis/sanctionedDataSerializables.txt   |   6 +-
 ...orBenchmark.java => RedisHashMapBenchmark.java} |  10 +-
 .../internal/collections/OrderStatisticsTree.java  |  39 ++---
 ...leObject2ObjectOpenCustomHashMapWithCursor.java |  45 +++---
 .../SizeableObjectOpenCustomHashSet.java           |  36 ++---
 .../geode/redis/internal/data/RedisHash.java       |  47 ++++--
 .../apache/geode/redis/internal/data/RedisSet.java |  39 +++--
 .../geode/redis/internal/data/RedisSortedSet.java  |  94 ++++++-----
 .../geode/redis/internal/data/RedisString.java     |  15 +-
 .../redis/internal/data/SizeableObjectSizer.java   |  38 -----
 .../collections/OrderStatisticsTreeTest.java       |  45 ++++--
 .../OrderedStatisticTreeQuickCheckTest.java        |   1 +
 ...tOpenCustomHashMapWithCursorQuickCheckTest.java |  24 ++-
 ...ject2ObjectOpenCustomHashMapWithCursorTest.java | 175 ++++++++-------------
 .../SizeableObjectOpenCustomHashSetTest.java       | 123 +--------------
 .../geode/redis/internal/data/RedisHashTest.java   |  24 +--
 .../geode/redis/internal/data/RedisSetTest.java    |  41 ++---
 .../redis/internal/data/RedisSortedSetTest.java    |  48 +++---
 .../geode/redis/internal/data/RedisStringTest.java |  37 ++---
 .../cache30/MemLRUEvictionControllerDUnitTest.java |  18 ++-
 .../eviction/EvictionObjectSizerDUnitTest.java     |  12 +-
 .../org/apache/geode/internal/JvmSizeUtils.java    |  57 +++++++
 .../internal/size/ReflectionSingleObjectSizer.java |  13 +-
 .../geode/internal/size/WellKnownClassSizer.java   |   8 +-
 26 files changed, 447 insertions(+), 553 deletions(-)

diff --git 
a/geode-apis-compatible-with-redis/src/integrationTest/java/org/apache/geode/redis/internal/executor/hash/MemoryOverheadIntegrationTest.java
 
b/geode-apis-compatible-with-redis/src/integrationTest/java/org/apache/geode/redis/internal/executor/hash/MemoryOverheadIntegrationTest.java
index b096671..97750f3 100755
--- 
a/geode-apis-compatible-with-redis/src/integrationTest/java/org/apache/geode/redis/internal/executor/hash/MemoryOverheadIntegrationTest.java
+++ 
b/geode-apis-compatible-with-redis/src/integrationTest/java/org/apache/geode/redis/internal/executor/hash/MemoryOverheadIntegrationTest.java
@@ -58,7 +58,7 @@ public class MemoryOverheadIntegrationTest extends 
AbstractMemoryOverheadIntegra
     result.put(Measurement.SET_ENTRY, 25);
     result.put(Measurement.HASH, 339);
     result.put(Measurement.HASH_ENTRY, 50);
-    result.put(Measurement.SORTED_SET, 455);
+    result.put(Measurement.SORTED_SET, 435);
     result.put(Measurement.SORTED_SET_ENTRY, 126);
 
     return result;
diff --git 
a/geode-apis-compatible-with-redis/src/integrationTest/resources/org/apache/geode/codeAnalysis/excludedClasses.txt
 
b/geode-apis-compatible-with-redis/src/integrationTest/resources/org/apache/geode/codeAnalysis/excludedClasses.txt
index 850671e..272f2dc 100644
--- 
a/geode-apis-compatible-with-redis/src/integrationTest/resources/org/apache/geode/codeAnalysis/excludedClasses.txt
+++ 
b/geode-apis-compatible-with-redis/src/integrationTest/resources/org/apache/geode/codeAnalysis/excludedClasses.txt
@@ -5,7 +5,10 @@ org/apache/geode/redis/internal/collections/IndexibleTreeSet
 org/apache/geode/redis/internal/data/RedisDataMovedException
 org/apache/geode/redis/internal/data/RedisDataTypeMismatchException
 org/apache/geode/redis/internal/RedisException
+org/apache/geode/redis/internal/data/RedisHash$Hash
 org/apache/geode/redis/internal/data/RedisRestoreKeyExistsException
+org/apache/geode/redis/internal/data/RedisSet$MemberSet
 org/apache/geode/redis/internal/data/RedisSetCommandsFunctionExecutor$SetOp
+org/apache/geode/redis/internal/data/RedisSortedSet$MemberMap
 org/apache/geode/redis/internal/data/RedisStringCommandsFunctionExecutor$BitOp
 org/apache/geode/redis/internal/services/StripedExecutorService$State
diff --git 
a/geode-apis-compatible-with-redis/src/integrationTest/resources/org/apache/geode/codeAnalysis/sanctionedDataSerializables.txt
 
b/geode-apis-compatible-with-redis/src/integrationTest/resources/org/apache/geode/codeAnalysis/sanctionedDataSerializables.txt
index 8735ef5..cad8be5 100644
--- 
a/geode-apis-compatible-with-redis/src/integrationTest/resources/org/apache/geode/codeAnalysis/sanctionedDataSerializables.txt
+++ 
b/geode-apis-compatible-with-redis/src/integrationTest/resources/org/apache/geode/codeAnalysis/sanctionedDataSerializables.txt
@@ -12,7 +12,7 @@ toData,8
 
 org/apache/geode/redis/internal/data/RedisHash,2
 toData,90
-fromData,61
+fromData,58
 
 org/apache/geode/redis/internal/data/RedisKey,2
 fromData,22
@@ -20,11 +20,11 @@ toData,19
 
 org/apache/geode/redis/internal/data/RedisSet,2
 toData,55
-fromData,54
+fromData,51
 
 org/apache/geode/redis/internal/data/RedisSortedSet,2
 toData,93
-fromData,102
+fromData,86
 
 org/apache/geode/redis/internal/data/RedisString,2
 toData,23
diff --git 
a/geode-apis-compatible-with-redis/src/jmh/java/org/apache/geode/redis/internal/collections/SizeableObject2ObjectOpenCustomHashmapWithCursorBenchmark.java
 
b/geode-apis-compatible-with-redis/src/jmh/java/org/apache/geode/redis/internal/collections/RedisHashMapBenchmark.java
similarity index 86%
rename from 
geode-apis-compatible-with-redis/src/jmh/java/org/apache/geode/redis/internal/collections/SizeableObject2ObjectOpenCustomHashmapWithCursorBenchmark.java
rename to 
geode-apis-compatible-with-redis/src/jmh/java/org/apache/geode/redis/internal/collections/RedisHashMapBenchmark.java
index cb3be45..8064c32 100644
--- 
a/geode-apis-compatible-with-redis/src/jmh/java/org/apache/geode/redis/internal/collections/SizeableObject2ObjectOpenCustomHashmapWithCursorBenchmark.java
+++ 
b/geode-apis-compatible-with-redis/src/jmh/java/org/apache/geode/redis/internal/collections/RedisHashMapBenchmark.java
@@ -17,7 +17,6 @@ package org.apache.geode.redis.internal.collections;
 import java.util.Iterator;
 import java.util.Random;
 
-import it.unimi.dsi.fastutil.bytes.ByteArrays;
 import org.openjdk.jmh.annotations.Benchmark;
 import org.openjdk.jmh.annotations.Param;
 import org.openjdk.jmh.annotations.Scope;
@@ -25,7 +24,9 @@ import org.openjdk.jmh.annotations.Setup;
 import org.openjdk.jmh.annotations.State;
 import org.openjdk.jmh.infra.Blackhole;
 
-public class SizeableObject2ObjectOpenCustomHashmapWithCursorBenchmark {
+import org.apache.geode.redis.internal.data.RedisHash;
+
+public class RedisHashMapBenchmark {
 
   @State(Scope.Benchmark)
   public static class BenchmarkState {
@@ -33,15 +34,14 @@ public class 
SizeableObject2ObjectOpenCustomHashmapWithCursorBenchmark {
     private int numEntries;
     @Param({"32"})
     private int keySize;
-    private SizeableObject2ObjectOpenCustomHashMapWithCursor<byte[], byte[]> 
map;
+    private RedisHash.Hash map;
     private int cursor;
     private Iterator<byte[]> iterator;
 
     @Setup
     public void createMap() {
       Random random = new Random(0);
-      map = new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(numEntries,
-          ByteArrays.HASH_STRATEGY);
+      map = new RedisHash.Hash(numEntries);
       for (int i = 0; i < numEntries; i++) {
         byte[] key = new byte[keySize];
         random.nextBytes(key);
diff --git 
a/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/collections/OrderStatisticsTree.java
 
b/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/collections/OrderStatisticsTree.java
index 96c3f5e..8cad3a1 100644
--- 
a/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/collections/OrderStatisticsTree.java
+++ 
b/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/collections/OrderStatisticsTree.java
@@ -26,6 +26,7 @@
 package org.apache.geode.redis.internal.collections;
 
 
+import static org.apache.geode.internal.JvmSizeUtils.memoryOverhead;
 import static org.apache.geode.util.internal.UncheckedUtils.uncheckedCast;
 
 import java.util.Arrays;
@@ -38,7 +39,6 @@ import java.util.Objects;
 import java.util.Set;
 
 import org.apache.geode.annotations.VisibleForTesting;
-import org.apache.geode.redis.internal.data.SizeableObjectSizer;
 
 /**
  * This class implements an order statistic tree which is based on AVL-trees.
@@ -47,19 +47,17 @@ import 
org.apache.geode.redis.internal.data.SizeableObjectSizer;
  * @author Rodion "rodde" Efremov
  * @version 1.6 (Feb 11, 2016)
  */
-public class OrderStatisticsTree<E extends Comparable<? super E>> implements 
OrderStatisticsSet<E> {
-
-  // The following constants were calculated using reflection. You can find 
the tests for these
-  // values in OrderStatisticsTreeTest, which shows the way these numbers were 
calculated. If our
-  // internal implementation changes, these values may be incorrect. An 
increase in overhead should
-  // be carefully considered.
-  public static final int ORDER_STATISTICS_TREE_BASE_SIZE = 32;
-  public static final int PER_ENTRY_OVERHEAD = 40;
+public class OrderStatisticsTree<E extends Comparable<? super E>>
+    implements OrderStatisticsSet<E> {
+
+  private static final int ORDER_STATISTICS_TREE_OVERHEAD =
+      memoryOverhead(OrderStatisticsTree.class);
+  @VisibleForTesting
+  static final int NODE_OVERHEAD = memoryOverhead(Node.class);
+
   private Node<E> root;
   private int size;
   private int modCount;
-  private int sizeInBytes = ORDER_STATISTICS_TREE_BASE_SIZE;
-  private static final SizeableObjectSizer elementSizer = new 
SizeableObjectSizer();
 
   @Override
   public Iterator<E> iterator() {
@@ -162,7 +160,6 @@ public class OrderStatisticsTree<E extends Comparable<? 
super E>> implements Ord
       root = new Node<>(element);
       size = 1;
       modCount++;
-      incrementSize(element);
       return true;
     }
 
@@ -198,7 +195,6 @@ public class OrderStatisticsTree<E extends Comparable<? 
super E>> implements Ord
     newNode.parent = parent;
     size++;
     modCount++;
-    incrementSize(element);
     Node<E> hi = parent;
     Node<E> lo = newNode;
 
@@ -269,7 +265,6 @@ public class OrderStatisticsTree<E extends Comparable<? 
super E>> implements Ord
     }
 
     x = deleteNode(x);
-    decrementSize(x.key);
     fixAfterModification(x, false);
     size--;
     modCount++;
@@ -743,17 +738,14 @@ public class OrderStatisticsTree<E extends Comparable<? 
super E>> implements Ord
     return leftTreeSize + 1 + rightTreeSize;
   }
 
-  void incrementSize(E element) {
-    sizeInBytes += elementSizer.sizeof(element) + PER_ENTRY_OVERHEAD;
-  }
-
-  void decrementSize(E element) {
-    sizeInBytes -= elementSizer.sizeof(element) + PER_ENTRY_OVERHEAD;
-  }
-
+  /**
+   * Returns the amount of memory used by this instance and its Node instances.
+   * Note that this does not include the memory used by the elements referenced
+   * by the nodes.
+   */
   @Override
   public int getSizeInBytes() {
-    return sizeInBytes;
+    return ORDER_STATISTICS_TREE_OVERHEAD + (size * NODE_OVERHEAD);
   }
 
   private static final class Node<T> {
@@ -825,7 +817,6 @@ public class OrderStatisticsTree<E extends Comparable<? 
super E>> implements Ord
       checkConcurrentModification();
 
       Node<E> x = deleteNode(previousNode);
-      decrementSize(x.key);
       fixAfterModification(x, false);
 
       if (x == nextNode) {
diff --git 
a/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/collections/SizeableObject2ObjectOpenCustomHashMapWithCursor.java
 
b/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/collections/SizeableObject2ObjectOpenCustomHashMapWithCursor.java
index b2239dd..dc552c5 100644
--- 
a/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/collections/SizeableObject2ObjectOpenCustomHashMapWithCursor.java
+++ 
b/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/collections/SizeableObject2ObjectOpenCustomHashMapWithCursor.java
@@ -15,31 +15,29 @@
 package org.apache.geode.redis.internal.collections;
 
 import static it.unimi.dsi.fastutil.HashCommon.mix;
+import static org.apache.geode.internal.JvmSizeUtils.memoryOverhead;
 
 import java.util.Map;
 
 import it.unimi.dsi.fastutil.objects.Object2ObjectMap;
 import it.unimi.dsi.fastutil.objects.Object2ObjectOpenCustomHashMap;
 
-import org.apache.geode.annotations.VisibleForTesting;
 import org.apache.geode.internal.size.Sizeable;
-import org.apache.geode.redis.internal.data.SizeableObjectSizer;
 
 /**
- * An extention of {@link Object2ObjectOpenCustomHashMap} that supports
+ * An extension of {@link Object2ObjectOpenCustomHashMap} that supports
  * a method of iteration where each scan operation returns an integer cursor
  * that allows future scan operations to start from that same point.
  *
  * The scan method provides the same guarantees as Redis's HSCAN, and in fact
  * uses the same algorithm.
  */
-public class SizeableObject2ObjectOpenCustomHashMapWithCursor<K, V>
+public abstract class SizeableObject2ObjectOpenCustomHashMapWithCursor<K, V>
     extends Object2ObjectOpenCustomHashMap<K, V> implements Sizeable {
 
   private static final long serialVersionUID = 9079713776660851891L;
-  public static final int BACKING_ARRAY_OVERHEAD_CONSTANT = 128;
-  public static final int BACKING_ARRAY_LENGTH_COEFFICIENT = 4;
-  private static final SizeableObjectSizer elementSizer = new 
SizeableObjectSizer();
+  public static final int OPEN_HASH_MAP_OVERHEAD =
+      memoryOverhead(SizeableObject2ObjectOpenCustomHashMapWithCursor.class);
 
   private int arrayContentsOverhead;
 
@@ -196,46 +194,39 @@ public class 
SizeableObject2ObjectOpenCustomHashMapWithCursor<K, V>
     V oldValue = super.put(k, v);
     if (oldValue == null) {
       // A create
-      arrayContentsOverhead += elementSizer.sizeof(k) + elementSizer.sizeof(v);
+      arrayContentsOverhead += sizeKey(k) + sizeValue(v);
     } else {
       // An update
-      arrayContentsOverhead += elementSizer.sizeof(v) - 
elementSizer.sizeof(oldValue);
+      arrayContentsOverhead += sizeValue(v) - sizeValue(oldValue);
     }
     return oldValue;
   }
 
   @Override
+  @SuppressWarnings("unchecked")
   public V remove(Object k) {
     V oldValue = super.remove(k);
     if (oldValue != null) {
-      arrayContentsOverhead -= elementSizer.sizeof(k) + 
elementSizer.sizeof(oldValue);
+      arrayContentsOverhead -= sizeKey((K) k) + sizeValue(oldValue);
     }
     return oldValue;
   }
 
   @Override
   public int getSizeInBytes() {
-    return arrayContentsOverhead + calculateBackingArraysOverhead();
-  }
-
-  @VisibleForTesting
-  int calculateBackingArraysOverhead() {
-    // This formula determined experimentally using tests.
-    return BACKING_ARRAY_OVERHEAD_CONSTANT
-        + BACKING_ARRAY_LENGTH_COEFFICIENT * getTotalBackingArrayLength();
-  }
-
-  @VisibleForTesting
-  int getArrayContentsOverhead() {
-    return arrayContentsOverhead;
-  }
+    // The size of the object referenced by the "strategy" field is not 
included
+    // here because in most cases it is a static singleton.
 
-  @VisibleForTesting
-  int getTotalBackingArrayLength() {
-    return key.length + value.length;
+    return OPEN_HASH_MAP_OVERHEAD + memoryOverhead(key) + memoryOverhead(value)
+        + arrayContentsOverhead;
   }
 
   public interface EntryConsumer<K, V, D> {
     void consume(D privateData, K key, V value);
   }
+
+  protected abstract int sizeKey(K key);
+
+  protected abstract int sizeValue(V value);
+
 }
diff --git 
a/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/collections/SizeableObjectOpenCustomHashSet.java
 
b/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/collections/SizeableObjectOpenCustomHashSet.java
index 4b64caf..aa526ba 100644
--- 
a/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/collections/SizeableObjectOpenCustomHashSet.java
+++ 
b/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/collections/SizeableObjectOpenCustomHashSet.java
@@ -15,22 +15,21 @@
 package org.apache.geode.redis.internal.collections;
 
 
+import static org.apache.geode.internal.JvmSizeUtils.memoryOverhead;
+
 import java.util.Collection;
 import java.util.Iterator;
 
 import it.unimi.dsi.fastutil.objects.ObjectCollection;
 import it.unimi.dsi.fastutil.objects.ObjectOpenCustomHashSet;
 
-import org.apache.geode.annotations.VisibleForTesting;
 import org.apache.geode.internal.size.Sizeable;
-import org.apache.geode.redis.internal.data.SizeableObjectSizer;
 
-public class SizeableObjectOpenCustomHashSet<K> extends 
ObjectOpenCustomHashSet<K>
+public abstract class SizeableObjectOpenCustomHashSet<K> extends 
ObjectOpenCustomHashSet<K>
     implements Sizeable {
   private static final long serialVersionUID = 9174920505089089517L;
-  public static final int BACKING_ARRAY_OVERHEAD_CONSTANT = 92;
-  public static final int BACKING_ARRAY_LENGTH_COEFFICIENT = 4;
-  private static final SizeableObjectSizer elementSizer = new 
SizeableObjectSizer();
+  private static final int OPEN_HASH_SET_OVERHEAD =
+      memoryOverhead(SizeableObjectOpenCustomHashSet.class);
 
   private int memberOverhead;
 
@@ -96,38 +95,27 @@ public class SizeableObjectOpenCustomHashSet<K> extends 
ObjectOpenCustomHashSet<
   public boolean add(K k) {
     boolean added = super.add(k);
     if (added) {
-      memberOverhead += elementSizer.sizeof(k);
+      memberOverhead += sizeElement(k);
     }
     return added;
   }
 
+  @SuppressWarnings("unchecked")
   @Override
   public boolean remove(Object k) {
     boolean removed = super.remove(k);
     if (removed) {
-      memberOverhead -= elementSizer.sizeof(k);
+      memberOverhead -= sizeElement((K) k);
     }
     return removed;
   }
 
   @Override
   public int getSizeInBytes() {
-    return memberOverhead + calculateBackingArrayOverhead();
-  }
-
-  @VisibleForTesting
-  int getMemberOverhead() {
-    return memberOverhead;
+    // The object referenced by the "strategy" field is not sized
+    // since it is usually a singleton instance.
+    return OPEN_HASH_SET_OVERHEAD + memoryOverhead(key) + memberOverhead;
   }
 
-  @VisibleForTesting
-  int getBackingArrayLength() {
-    return key.length;
-  }
-
-  @VisibleForTesting
-  int calculateBackingArrayOverhead() {
-    // This formula determined experimentally using tests
-    return BACKING_ARRAY_OVERHEAD_CONSTANT + (BACKING_ARRAY_LENGTH_COEFFICIENT 
* key.length);
-  }
+  protected abstract int sizeElement(K element);
 }
diff --git 
a/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/data/RedisHash.java
 
b/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/data/RedisHash.java
index d685205..5969d3a 100644
--- 
a/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/data/RedisHash.java
+++ 
b/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/data/RedisHash.java
@@ -16,6 +16,8 @@
 
 package org.apache.geode.redis.internal.data;
 
+import static it.unimi.dsi.fastutil.bytes.ByteArrays.HASH_STRATEGY;
+import static org.apache.geode.internal.JvmSizeUtils.memoryOverhead;
 import static org.apache.geode.redis.internal.RedisConstants.ERROR_NOT_INTEGER;
 import static org.apache.geode.redis.internal.RedisConstants.ERROR_OVERFLOW;
 import static org.apache.geode.redis.internal.netty.Coder.bytesToLong;
@@ -34,7 +36,6 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.regex.Pattern;
 
-import it.unimi.dsi.fastutil.bytes.ByteArrays;
 import org.apache.commons.lang3.tuple.ImmutablePair;
 
 import org.apache.geode.DataSerializer;
@@ -51,12 +52,9 @@ import org.apache.geode.redis.internal.delta.RemsDeltaInfo;
 import org.apache.geode.redis.internal.netty.Coder;
 
 public class RedisHash extends AbstractRedisData {
-  // The following constant was calculated using reflection. you can find the 
test for this value in
-  // RedisHashTest, which shows the way this number was calculated. If our 
internal implementation
-  // changes, these values may be incorrect. An increase in overhead should be 
carefully considered.
-  protected static final int BASE_REDIS_HASH_OVERHEAD = 32;
+  protected static final int REDIS_HASH_OVERHEAD = 
memoryOverhead(RedisHash.class);
 
-  private SizeableObject2ObjectOpenCustomHashMapWithCursor<byte[], byte[]> 
hash;
+  private Hash hash;
 
   @VisibleForTesting
   public RedisHash(List<byte[]> fieldsToSet) {
@@ -66,8 +64,7 @@ public class RedisHash extends AbstractRedisData {
           "fieldsToSet should have an even number of elements but was size " + 
numKeysAndValues);
     }
 
-    hash = new 
SizeableObject2ObjectOpenCustomHashMapWithCursor<>(numKeysAndValues / 2,
-        ByteArrays.HASH_STRATEGY);
+    hash = new Hash(numKeysAndValues / 2);
     Iterator<byte[]> iterator = fieldsToSet.iterator();
     while (iterator.hasNext()) {
       hashPut(iterator.next(), iterator.next());
@@ -101,7 +98,7 @@ public class RedisHash extends AbstractRedisData {
       throws IOException, ClassNotFoundException {
     super.fromData(in, context);
     int size = DataSerializer.readInteger(in);
-    hash = new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(size, 
ByteArrays.HASH_STRATEGY);
+    hash = new Hash(size);
     for (int i = 0; i < size; i++) {
       hash.put(DataSerializer.readByteArray(in), 
DataSerializer.readByteArray(in));
     }
@@ -392,7 +389,37 @@ public class RedisHash extends AbstractRedisData {
 
   @Override
   public int getSizeInBytes() {
-    return BASE_REDIS_HASH_OVERHEAD + hash.getSizeInBytes();
+    return REDIS_HASH_OVERHEAD + hash.getSizeInBytes();
+  }
+
+  public static class Hash
+      extends SizeableObject2ObjectOpenCustomHashMapWithCursor<byte[], byte[]> 
{
+
+    public Hash() {
+      super(HASH_STRATEGY);
+    }
+
+    public Hash(int expected) {
+      super(expected, HASH_STRATEGY);
+    }
+
+    public Hash(int expected, float f) {
+      super(expected, f, HASH_STRATEGY);
+    }
+
+    public Hash(Map<byte[], byte[]> m) {
+      super(m, HASH_STRATEGY);
+    }
+
+    @Override
+    protected int sizeKey(byte[] key) {
+      return memoryOverhead(key);
+    }
+
+    @Override
+    protected int sizeValue(byte[] value) {
+      return sizeKey(value);
+    }
   }
 
 }
diff --git 
a/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/data/RedisSet.java
 
b/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/data/RedisSet.java
index 5c2e18c..08bf977 100644
--- 
a/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/data/RedisSet.java
+++ 
b/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/data/RedisSet.java
@@ -17,6 +17,7 @@
 package org.apache.geode.redis.internal.data;
 
 import static java.util.Collections.emptyList;
+import static org.apache.geode.internal.JvmSizeUtils.memoryOverhead;
 import static org.apache.geode.redis.internal.data.RedisDataType.REDIS_SET;
 import static org.apache.geode.redis.internal.netty.Coder.bytesToString;
 
@@ -50,16 +51,12 @@ import org.apache.geode.redis.internal.delta.DeltaInfo;
 import org.apache.geode.redis.internal.delta.RemsDeltaInfo;
 
 public class RedisSet extends AbstractRedisData {
-  private SizeableObjectOpenCustomHashSet<byte[]> members;
+  protected static final int REDIS_SET_OVERHEAD = 
memoryOverhead(RedisSet.class);
 
-  // The following constant was calculated using reflection. you can find the 
test for this value in
-  // RedisSetTest, which shows the way this number was calculated. If our 
internal implementation
-  // changes, these values may be incorrect. An increase in overhead should be 
carefully considered.
-  protected static final int BASE_REDIS_SET_OVERHEAD = 32;
+  private MemberSet members;
 
   RedisSet(Collection<byte[]> members) {
-    this.members =
-        new SizeableObjectOpenCustomHashSet<>(members.size(), 
ByteArrays.HASH_STRATEGY);
+    this.members = new MemberSet(members.size());
     for (byte[] member : members) {
       membersAdd(member);
     }
@@ -218,7 +215,7 @@ public class RedisSet extends AbstractRedisData {
       throws IOException, ClassNotFoundException {
     super.fromData(in, context);
     int size = DataSerializer.readPrimitiveInt(in);
-    members = new SizeableObjectOpenCustomHashSet<>(size, 
ByteArrays.HASH_STRATEGY);
+    members = new MemberSet(size);
     for (int i = 0; i < size; ++i) {
       members.add(DataSerializer.readByteArray(in));
     }
@@ -332,6 +329,30 @@ public class RedisSet extends AbstractRedisData {
 
   @Override
   public int getSizeInBytes() {
-    return BASE_REDIS_SET_OVERHEAD + members.getSizeInBytes();
+    return REDIS_SET_OVERHEAD + members.getSizeInBytes();
   }
+
+  public static class MemberSet extends 
SizeableObjectOpenCustomHashSet<byte[]> {
+    public MemberSet() {
+      super(ByteArrays.HASH_STRATEGY);
+    }
+
+    public MemberSet(int size) {
+      super(size, ByteArrays.HASH_STRATEGY);
+    }
+
+    public MemberSet(int expected, float f) {
+      super(expected, f, ByteArrays.HASH_STRATEGY);
+    }
+
+    public MemberSet(Collection<byte[]> initialElements) {
+      super(initialElements, ByteArrays.HASH_STRATEGY);
+    }
+
+    @Override
+    protected int sizeElement(byte[] element) {
+      return memoryOverhead(element);
+    }
+  }
+
 }
diff --git 
a/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/data/RedisSortedSet.java
 
b/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/data/RedisSortedSet.java
index 0a6f2dd..1299be4 100644
--- 
a/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/data/RedisSortedSet.java
+++ 
b/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/data/RedisSortedSet.java
@@ -16,12 +16,12 @@
 
 package org.apache.geode.redis.internal.data;
 
-import static 
org.apache.geode.internal.size.ReflectionSingleObjectSizer.roundUpSize;
+import static it.unimi.dsi.fastutil.bytes.ByteArrays.HASH_STRATEGY;
+import static org.apache.geode.internal.JvmSizeUtils.memoryOverhead;
 import static 
org.apache.geode.redis.internal.RedisConstants.ERROR_NOT_A_VALID_FLOAT;
 import static 
org.apache.geode.redis.internal.data.RedisDataType.REDIS_SORTED_SET;
 import static org.apache.geode.redis.internal.netty.Coder.bytesToDouble;
 import static org.apache.geode.redis.internal.netty.Coder.doubleToBytes;
-import static org.apache.geode.redis.internal.netty.Coder.narrowLongToInt;
 import static 
org.apache.geode.redis.internal.netty.Coder.stripTrailingZeroFromDouble;
 import static 
org.apache.geode.redis.internal.netty.StringBytesGlossary.bGREATEST_MEMBER_NAME;
 import static 
org.apache.geode.redis.internal.netty.StringBytesGlossary.bLEAST_MEMBER_NAME;
@@ -37,8 +37,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 
-import it.unimi.dsi.fastutil.bytes.ByteArrays;
-
 import org.apache.geode.annotations.VisibleForTesting;
 import org.apache.geode.cache.Region;
 import org.apache.geode.internal.InternalDataSerializer;
@@ -47,7 +45,6 @@ import org.apache.geode.internal.serialization.KnownVersion;
 import org.apache.geode.internal.serialization.SerializationContext;
 import org.apache.geode.internal.size.Sizeable;
 import org.apache.geode.redis.internal.RedisConstants;
-import org.apache.geode.redis.internal.collections.OrderStatisticsSet;
 import org.apache.geode.redis.internal.collections.OrderStatisticsTree;
 import 
org.apache.geode.redis.internal.collections.SizeableObject2ObjectOpenCustomHashMapWithCursor;
 import org.apache.geode.redis.internal.delta.AddsDeltaInfo;
@@ -59,29 +56,18 @@ import 
org.apache.geode.redis.internal.executor.sortedset.SortedSetScoreRangeOpt
 import org.apache.geode.redis.internal.executor.sortedset.ZAddOptions;
 
 public class RedisSortedSet extends AbstractRedisData {
-  private SizeableObject2ObjectOpenCustomHashMapWithCursor<byte[], 
OrderedSetEntry> members;
-  private OrderStatisticsSet<AbstractOrderedSetEntry> scoreSet;
-  // This field is used to keep track of the size associated with objects that 
are referenced by
-  // both backing collections, since they will be counted twice otherwise
-  private int sizeInBytesAdjustment = 0;
-
-  // The following constant was calculated using reflection. You can find the 
test for this value in
-  // RedisSortedSetTest, which shows the way this number was calculated. If 
our internal
-  // implementation changes, this value may be incorrect. An increase in 
overhead should be
-  // carefully considered.
-  protected static final int BASE_REDIS_SORTED_SET_OVERHEAD = 40;
+  protected static final int REDIS_SORTED_SET_OVERHEAD = 
memoryOverhead(RedisSortedSet.class);
+
+  private MemberMap members;
+  private final ScoreSet scoreSet = new ScoreSet();
 
   @Override
   public int getSizeInBytes() {
-    return BASE_REDIS_SORTED_SET_OVERHEAD + members.getSizeInBytes() + 
scoreSet.getSizeInBytes()
-        - sizeInBytesAdjustment;
+    return REDIS_SORTED_SET_OVERHEAD + members.getSizeInBytes() + 
scoreSet.getSizeInBytes();
   }
 
   RedisSortedSet(List<byte[]> members) {
-    this.members =
-        new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(members.size() 
/ 2,
-            ByteArrays.HASH_STRATEGY);
-    scoreSet = new OrderStatisticsTree<>();
+    this.members = new MemberMap(members.size() / 2);
 
     Iterator<byte[]> iterator = members.iterator();
 
@@ -133,9 +119,7 @@ public class RedisSortedSet extends AbstractRedisData {
       throws IOException, ClassNotFoundException {
     super.fromData(in, context);
     int size = InternalDataSerializer.readPrimitiveInt(in);
-    members =
-        new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(size, 
ByteArrays.HASH_STRATEGY);
-    scoreSet = new OrderStatisticsTree<>();
+    members = new MemberMap(size);
     for (int i = 0; i < size; i++) {
       byte[] member = InternalDataSerializer.readByteArray(in);
       byte[] score = InternalDataSerializer.readByteArray(in);
@@ -184,9 +168,6 @@ public class RedisSortedSet extends AbstractRedisData {
       OrderedSetEntry newEntry = new OrderedSetEntry(memberToAdd, scoreToAdd);
       members.put(memberToAdd, newEntry);
       scoreSet.add(newEntry);
-      // Without this adjustment, we count the entry and member name array 
twice, since references
-      // to them appear in both backing collections.
-      sizeInBytesAdjustment += newEntry.getSizeInBytes() + 
calculateByteArraySize(memberToAdd);
       return null;
     } else {
       scoreSet.remove(existingEntry);
@@ -204,17 +185,11 @@ public class RedisSortedSet extends AbstractRedisData {
     if (orderedSetEntry != null) {
       scoreSet.remove(orderedSetEntry);
       oldValue = orderedSetEntry.getScoreBytes();
-      // Adjust for removal.
-      adjustSizeForRemoval(orderedSetEntry);
     }
 
     return oldValue;
   }
 
-  private void adjustSizeForRemoval(AbstractOrderedSetEntry entry) {
-    sizeInBytesAdjustment -= entry.getSizeInBytes() + 
calculateByteArraySize(entry.getMember());
-  }
-
   private synchronized void membersAddAll(AddsDeltaInfo addsDeltaInfo) {
     Iterator<byte[]> iterator = addsDeltaInfo.getAdds().iterator();
     while (iterator.hasNext()) {
@@ -427,7 +402,6 @@ public class RedisSortedSet extends AbstractRedisData {
       AbstractOrderedSetEntry entry = scoresIterator.next();
       scoresIterator.remove();
       members.remove(entry.member);
-      adjustSizeForRemoval(entry);
 
       result.add(entry.member);
       result.add(entry.scoreBytes);
@@ -635,11 +609,8 @@ public class RedisSortedSet extends AbstractRedisData {
     // with same name...
   }
 
-  private static int calculateByteArraySize(byte[] bytes) {
-    return narrowLongToInt(roundUpSize(bytes.length) + 16);
-  }
-
-  abstract static class AbstractOrderedSetEntry implements 
Comparable<AbstractOrderedSetEntry>,
+  public abstract static class AbstractOrderedSetEntry
+      implements Comparable<AbstractOrderedSetEntry>,
       Sizeable {
     byte[] member;
     byte[] scoreBytes;
@@ -676,11 +647,7 @@ public class RedisSortedSet extends AbstractRedisData {
   // Entry used to store data in the scoreSet
   public static class OrderedSetEntry extends AbstractOrderedSetEntry {
 
-    // The following constant was calculated using reflection. You can find 
the test for this value
-    // in RedisSortedSetTest, which shows the way this number was calculated. 
If our internal
-    // implementation changes, this value may be incorrect. An increase in 
overhead should be
-    // carefully considered.
-    public static final int BASE_ORDERED_SET_ENTRY_SIZE = 32;
+    public static final int ORDERED_SET_ENTRY_OVERHEAD = 
memoryOverhead(OrderedSetEntry.class);
 
     public OrderedSetEntry(byte[] member, byte[] score) {
       this.member = member;
@@ -695,11 +662,12 @@ public class RedisSortedSet extends AbstractRedisData {
 
     @Override
     public int getSizeInBytes() {
-      return BASE_ORDERED_SET_ENTRY_SIZE + calculateByteArraySize(member)
-          + calculateByteArraySize(scoreBytes);
+      // don't include the member size since it is accounted
+      // for as the key on the Hash.
+      return ORDERED_SET_ENTRY_OVERHEAD + memoryOverhead(scoreBytes);
     }
 
-    void updateScore(byte[] newScore) {
+    public void updateScore(byte[] newScore) {
       if (!Arrays.equals(newScore, scoreBytes)) {
         scoreBytes = newScore;
         score = processByteArrayAsDouble(newScore);
@@ -770,4 +738,34 @@ public class RedisSortedSet extends AbstractRedisData {
       return 0;
     }
   }
+
+  public static class MemberMap
+      extends SizeableObject2ObjectOpenCustomHashMapWithCursor<byte[], 
OrderedSetEntry> {
+
+    public MemberMap(int size) {
+      super(size, HASH_STRATEGY);
+    }
+
+    public MemberMap(Map<byte[], RedisSortedSet.OrderedSetEntry> 
initialElements) {
+      super(initialElements, HASH_STRATEGY);
+    }
+
+    @Override
+    protected int sizeKey(byte[] key) {
+      return memoryOverhead(key);
+    }
+
+    @Override
+    protected int sizeValue(OrderedSetEntry value) {
+      return value.getSizeInBytes();
+    }
+  }
+
+  /**
+   * ScoreSet does not keep track of the size of the element instances since 
they
+   * are already accounted for by the MemberMap.
+   */
+  public static class ScoreSet extends 
OrderStatisticsTree<AbstractOrderedSetEntry> {
+  }
+
 }
diff --git 
a/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/data/RedisString.java
 
b/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/data/RedisString.java
index 6de5e45..5012426 100644
--- 
a/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/data/RedisString.java
+++ 
b/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/data/RedisString.java
@@ -16,6 +16,7 @@
 
 package org.apache.geode.redis.internal.data;
 
+import static org.apache.geode.internal.JvmSizeUtils.memoryOverhead;
 import static org.apache.geode.redis.internal.netty.Coder.bytesToString;
 
 import java.io.DataInput;
@@ -37,18 +38,14 @@ import 
org.apache.geode.redis.internal.executor.string.SetOptions;
 import org.apache.geode.redis.internal.netty.Coder;
 
 public class RedisString extends AbstractRedisData {
-  private int appendSequence;
-
-  private byte[] value;
-
-  // this value is empirically derived using ReflectionObjectSizer, which 
provides an exact size
-  // of the object. It can't be used directly because of its performance 
impact. This value causes
-  // the size we keep track of to converge to the actual size as it increases.
-  protected static final int BASE_REDIS_STRING_OVERHEAD = 48;
 
+  private static final int REDIS_STRING_OVERHEAD = 
memoryOverhead(RedisString.class);
   // An array containing the number of set bits for each value from 0x00 to 
0xff
   private static final byte[] bitCountTable = getBitCountTable();
 
+  private int appendSequence;
+  private byte[] value;
+
   public RedisString(byte[] value) {
     this.value = value;
   }
@@ -475,7 +472,7 @@ public class RedisString extends AbstractRedisData {
 
   @Override
   public int getSizeInBytes() {
-    return BASE_REDIS_STRING_OVERHEAD + value.length;
+    return REDIS_STRING_OVERHEAD + memoryOverhead(value);
   }
 
   private static byte[] getBitCountTable() {
diff --git 
a/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/data/SizeableObjectSizer.java
 
b/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/data/SizeableObjectSizer.java
deleted file mode 100644
index 9243936..0000000
--- 
a/geode-apis-compatible-with-redis/src/main/java/org/apache/geode/redis/internal/data/SizeableObjectSizer.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.redis.internal.data;
-
-import static org.apache.geode.redis.internal.netty.Coder.narrowLongToInt;
-
-import org.apache.geode.internal.size.ReflectionSingleObjectSizer;
-import org.apache.geode.internal.size.SingleObjectSizer;
-import org.apache.geode.internal.size.Sizeable;
-
-/**
- * A sizer that allows for efficient sizing of objects that implement the 
{@link Sizeable}
- * interface, and delegates to {@link ReflectionSingleObjectSizer} otherwise.
- */
-public class SizeableObjectSizer {
-
-  private final SingleObjectSizer sizer = new ReflectionSingleObjectSizer();
-
-  public int sizeof(Object object) {
-    if (object instanceof Sizeable) {
-      return ((Sizeable) object).getSizeInBytes();
-    } else {
-      return narrowLongToInt(sizer.sizeof(object));
-    }
-  }
-}
diff --git 
a/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/OrderStatisticsTreeTest.java
 
b/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/OrderStatisticsTreeTest.java
index 04616ec..9079314 100644
--- 
a/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/OrderStatisticsTreeTest.java
+++ 
b/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/OrderStatisticsTreeTest.java
@@ -25,8 +25,7 @@
  */
 package org.apache.geode.redis.internal.collections;
 
-import static 
org.apache.geode.redis.internal.collections.OrderStatisticsTree.ORDER_STATISTICS_TREE_BASE_SIZE;
-import static 
org.apache.geode.redis.internal.collections.OrderStatisticsTree.PER_ENTRY_OVERHEAD;
+import static 
org.apache.geode.redis.internal.collections.OrderStatisticsTree.NODE_OVERHEAD;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
@@ -674,34 +673,41 @@ public class OrderStatisticsTreeTest {
 
   @Test
   public void getSizeInBytes_isAccurate_forAdds() {
+    int elementSize = 0;
     for (int i = 0; i < 1000; ++i) {
       tree.add(i);
-      assertThat(tree.getSizeInBytes()).isEqualTo(sizer.sizeof(tree));
+      elementSize += sizer.sizeof(i);
+      assertThat(tree.getSizeInBytes()).isEqualTo(sizer.sizeof(tree) - 
elementSize);
     }
   }
 
   @Test
   public void getSizeInBytes_isAccurate_forRemoves() {
     int entries = 1000;
+    int elementSize = 0;
     for (int i = 0; i < entries; ++i) {
       tree.add(i);
+      elementSize += sizer.sizeof(i);
     }
     for (int i = 0; i < entries; ++i) {
       tree.remove(i);
-      assertThat(tree.getSizeInBytes()).isEqualTo(sizer.sizeof(tree));
+      elementSize -= sizer.sizeof(i);
+      assertThat(tree.getSizeInBytes()).isEqualTo(sizer.sizeof(tree) - 
elementSize);
     }
   }
 
   @Test
   public void getSizeInBytes_isAccurate_forIteratorRemoves() {
+    int elementSize = 0;
     for (int i = 0; i < 1000; ++i) {
       tree.add(i);
+      elementSize += sizer.sizeof(i);
     }
     Iterator<Integer> iterator = tree.iterator();
     while (iterator.hasNext()) {
-      iterator.next();
+      elementSize -= sizer.sizeof(iterator.next());
       iterator.remove();
-      assertThat(tree.getSizeInBytes()).isEqualTo(sizer.sizeof(tree));
+      assertThat(tree.getSizeInBytes()).isEqualTo(sizer.sizeof(tree) - 
elementSize);
     }
   }
 
@@ -711,29 +717,32 @@ public class OrderStatisticsTreeTest {
     byte[] member;
     byte[] scoreBytes;
     List<RedisSortedSet.OrderedSetEntry> entries = new ArrayList<>();
-    OrderStatisticsSet<RedisSortedSet.OrderedSetEntry> tree = new 
OrderStatisticsTree<>();
+    RedisSortedSet.ScoreSet tree = new RedisSortedSet.ScoreSet();
 
     assertThat(tree.getSizeInBytes()).isEqualTo(sizer.sizeof(tree));
 
     // Populate the tree set with randomly-sized OrderedSetEntry instances and 
verify that the
     // reported size is accurate after each addition
+    int sizeOfEntries = 0;
     for (int i = 0; i < 100; ++i) {
       member = new byte[random.nextInt(50_000)];
       scoreBytes = String.valueOf(random.nextDouble()).getBytes();
       RedisSortedSet.OrderedSetEntry entry = new 
RedisSortedSet.OrderedSetEntry(member, scoreBytes);
+      sizeOfEntries += sizer.sizeof(entry);
       entries.add(entry);
       tree.add(entry);
-      assertThat(tree.getSizeInBytes()).isEqualTo(sizer.sizeof(tree));
+      assertThat(tree.getSizeInBytes()).isEqualTo(sizer.sizeof(tree) - 
sizeOfEntries);
     }
 
     // Remove half the entries using an iterator and verify that the reported 
size is accurate after
     // each removal
-    Iterator<RedisSortedSet.OrderedSetEntry> iterator =
+    Iterator<RedisSortedSet.AbstractOrderedSetEntry> iterator =
         tree.getIndexRange(tree.size() / 2, tree.size(), false);
     while (iterator.hasNext()) {
-      iterator.next();
+      RedisSortedSet.AbstractOrderedSetEntry entry = iterator.next();
+      sizeOfEntries -= sizer.sizeof(entry);
       iterator.remove();
-      assertThat(tree.getSizeInBytes()).isEqualTo(sizer.sizeof(tree));
+      assertThat(tree.getSizeInBytes()).isEqualTo(sizer.sizeof(tree) - 
sizeOfEntries);
     }
 
     // Remove the rest of the entries using tree.remove() and verify that the 
reported size is
@@ -741,8 +750,10 @@ public class OrderStatisticsTreeTest {
     int entriesSize = entries.size();
     for (int i = 0; i < entriesSize; i++) {
       RedisSortedSet.OrderedSetEntry entry = entries.get(i);
-      tree.remove(entry);
-      assertThat(tree.getSizeInBytes()).isEqualTo(sizer.sizeof(tree));
+      if (tree.remove(entry)) {
+        sizeOfEntries -= sizer.sizeof(entry);
+      }
+      assertThat(tree.getSizeInBytes()).isEqualTo(sizer.sizeof(tree) - 
sizeOfEntries);
     }
   }
 
@@ -752,13 +763,13 @@ public class OrderStatisticsTreeTest {
   // that. If they have increased, carefully consider that increase before 
changing the constants.
   @Test
   public void orderStatisticsTreeBaseSize_shouldMatchReflectedSize() {
-    OrderStatisticsSet<RedisSortedSet.OrderedSetEntry> tree = new 
OrderStatisticsTree<>();
-    assertThat(ORDER_STATISTICS_TREE_BASE_SIZE).isEqualTo(sizer.sizeof(tree));
+    RedisSortedSet.ScoreSet tree = new RedisSortedSet.ScoreSet();
+    assertThat(tree.getSizeInBytes()).isEqualTo(sizer.sizeof(tree));
   }
 
   @Test
   public void orderStatisticsTreePerEntryOverhead_shouldMatchReflectedSize() {
-    OrderStatisticsSet<RedisSortedSet.OrderedSetEntry> tree = new 
OrderStatisticsTree<>();
+    RedisSortedSet.ScoreSet tree = new RedisSortedSet.ScoreSet();
     int beforeSize = sizer.sizeof(tree);
     for (int i = 0; i < 100; ++i) {
       byte[] member = new byte[i];
@@ -767,7 +778,7 @@ public class OrderStatisticsTreeTest {
       tree.add(entry);
       int afterSize = sizer.sizeof(tree);
       int perMemberOverhead = afterSize - beforeSize - sizer.sizeof(entry);
-      assertThat(PER_ENTRY_OVERHEAD).isEqualTo(perMemberOverhead);
+      assertThat(NODE_OVERHEAD).isEqualTo(perMemberOverhead);
       beforeSize = afterSize;
     }
   }
diff --git 
a/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/OrderedStatisticTreeQuickCheckTest.java
 
b/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/OrderedStatisticTreeQuickCheckTest.java
index b853626..f6e24f1 100644
--- 
a/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/OrderedStatisticTreeQuickCheckTest.java
+++ 
b/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/OrderedStatisticTreeQuickCheckTest.java
@@ -34,6 +34,7 @@ import org.junit.runner.RunWith;
  */
 @RunWith(JUnitQuickcheck.class)
 public class OrderedStatisticTreeQuickCheckTest {
+
   OrderStatisticsSet<Long> list = new OrderStatisticsTree<>();
   OrderStatisticsSet<Long> expected = new IndexibleTreeSet<>();
 
diff --git 
a/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/SizeableObject2ObjectOpenCustomHashMapWithCursorQuickCheckTest.java
 
b/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/SizeableObject2ObjectOpenCustomHashMapWithCursorQuickCheckTest.java
index cef48ae..1f2c0e8 100644
--- 
a/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/SizeableObject2ObjectOpenCustomHashMapWithCursorQuickCheckTest.java
+++ 
b/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/SizeableObject2ObjectOpenCustomHashMapWithCursorQuickCheckTest.java
@@ -44,14 +44,31 @@ public class 
SizeableObject2ObjectOpenCustomHashMapWithCursorQuickCheckTest {
     }
   };
 
+  private static class Integer2IntegerMap
+      extends SizeableObject2ObjectOpenCustomHashMapWithCursor<Integer, 
Integer> {
+
+    public Integer2IntegerMap() {
+      super(NATURAL_HASH);
+    }
+
+    @Override
+    protected int sizeKey(Integer key) {
+      return 0;
+    }
+
+    @Override
+    protected int sizeValue(Integer value) {
+      return 0;
+    }
+  }
+
 
   @Property
   public void scanWithConcurrentModifications_ReturnsExpectedElements(
       @Size(min = 2, max = 500) Set<@InRange(minInt = 0, maxInt = 500) 
Integer> initialData,
       @Size(max = 500) Set<@InRange(minInt = 0, maxInt = 1000) Integer> 
dataToAdd,
       @Size(max = 500) Set<@InRange(minInt = 0, maxInt = 500) Integer> 
keysToRemove) {
-    SizeableObject2ObjectOpenCustomHashMapWithCursor<Integer, Integer> map =
-        new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(NATURAL_HASH);
+    Integer2IntegerMap map = new Integer2IntegerMap();
     initialData.forEach(i -> map.put(i, i));
 
     HashMap<Integer, Integer> scanned = new HashMap<>();
@@ -72,8 +89,7 @@ public class 
SizeableObject2ObjectOpenCustomHashMapWithCursorQuickCheckTest {
   @Property
   public void scanWithNoModificationsDoesNotReturnDuplicates(
       @Size(min = 2, max = 500) Set<@InRange(minInt = 0, maxInt = 500) 
Integer> initialData) {
-    SizeableObject2ObjectOpenCustomHashMapWithCursor<Integer, Integer> map =
-        new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(NATURAL_HASH);
+    Integer2IntegerMap map = new Integer2IntegerMap();
     initialData.forEach(i -> map.put(i, i));
 
     List<Integer> scanned = new ArrayList<>();
diff --git 
a/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/SizeableObject2ObjectOpenCustomHashMapWithCursorTest.java
 
b/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/SizeableObject2ObjectOpenCustomHashMapWithCursorTest.java
index 8e11f9a..6b2097f 100644
--- 
a/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/SizeableObject2ObjectOpenCustomHashMapWithCursorTest.java
+++ 
b/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/SizeableObject2ObjectOpenCustomHashMapWithCursorTest.java
@@ -14,22 +14,20 @@
  */
 package org.apache.geode.redis.internal.collections;
 
-import static 
org.apache.geode.redis.internal.collections.SizeableObject2ObjectOpenCustomHashMapWithCursor.BACKING_ARRAY_LENGTH_COEFFICIENT;
-import static 
org.apache.geode.redis.internal.collections.SizeableObject2ObjectOpenCustomHashMapWithCursor.BACKING_ARRAY_OVERHEAD_CONSTANT;
+import static org.apache.geode.internal.JvmSizeUtils.memoryOverhead;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import java.util.HashMap;
 import java.util.Map;
-import java.util.Random;
 import java.util.stream.IntStream;
 
 import it.unimi.dsi.fastutil.Hash;
 import it.unimi.dsi.fastutil.bytes.ByteArrays;
-import org.assertj.core.api.SoftAssertions;
 import org.junit.Test;
 
 import org.apache.geode.cache.util.ObjectSizer;
 import org.apache.geode.internal.size.ReflectionObjectSizer;
+import org.apache.geode.redis.internal.data.RedisHash;
 import org.apache.geode.redis.internal.data.RedisSortedSet;
 
 public class SizeableObject2ObjectOpenCustomHashMapWithCursorTest {
@@ -47,7 +45,7 @@ public class 
SizeableObject2ObjectOpenCustomHashMapWithCursorTest {
     }
   };
 
-  private static Hash.Strategy<Integer> COLLIDING_HASH = new 
Hash.Strategy<Integer>() {
+  private static final Hash.Strategy<Integer> COLLIDING_HASH = new 
Hash.Strategy<Integer>() {
     @Override
     public int hashCode(Integer o) {
       return o % 5;
@@ -59,10 +57,30 @@ public class 
SizeableObject2ObjectOpenCustomHashMapWithCursorTest {
     }
   };
 
+  private static class Integer2StringMap
+      extends SizeableObject2ObjectOpenCustomHashMapWithCursor<Integer, 
String> {
+    public Integer2StringMap() {
+      super(NATURAL_HASH);
+    }
+
+    public Integer2StringMap(Hash.Strategy<Integer> hashStrategy) {
+      super(hashStrategy);
+    }
+
+    @Override
+    protected int sizeKey(Integer key) {
+      return 0;
+    }
+
+    @Override
+    protected int sizeValue(String value) {
+      return 0;
+    }
+  }
+
   @Test
   public void scanEntireMap_ReturnsExpectedElements() {
-    SizeableObject2ObjectOpenCustomHashMapWithCursor<Integer, String> map =
-        new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(NATURAL_HASH);
+    Integer2StringMap map = new Integer2StringMap();
     IntStream.range(0, 10).forEach(i -> map.put(i, "value-" + i));
 
     Map<Integer, String> scanned = new HashMap<>();
@@ -73,8 +91,7 @@ public class 
SizeableObject2ObjectOpenCustomHashMapWithCursorTest {
 
   @Test
   public void twoScansWithNoModifications_ReturnsExpectedElements() {
-    SizeableObject2ObjectOpenCustomHashMapWithCursor<Integer, String> map =
-        new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(NATURAL_HASH);
+    Integer2StringMap map = new Integer2StringMap();
     IntStream.range(0, 10).forEach(i -> map.put(i, "value-" + i));
 
     Map<Integer, String> scanned = new HashMap<>();
@@ -94,8 +111,7 @@ public class 
SizeableObject2ObjectOpenCustomHashMapWithCursorTest {
 
   @Test
   public void scanWithConcurrentRemoves_ReturnsExpectedElements() {
-    SizeableObject2ObjectOpenCustomHashMapWithCursor<Integer, String> map =
-        new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(NATURAL_HASH);
+    Integer2StringMap map = new Integer2StringMap();
     IntStream.range(0, 10).forEach(i -> map.put(i, "value-" + i));
 
     Map<Integer, String> scanned = new HashMap<>();
@@ -116,8 +132,7 @@ public class 
SizeableObject2ObjectOpenCustomHashMapWithCursorTest {
 
   @Test
   public void scanWithHashcodeCollisions_ReturnsExpectedElements() {
-    SizeableObject2ObjectOpenCustomHashMapWithCursor<Integer, String> map =
-        new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(COLLIDING_HASH);
+    Integer2StringMap map = new Integer2StringMap(COLLIDING_HASH);
     IntStream.range(0, 10).forEach(i -> map.put(i, "value-" + i));
 
     // The colliding hash is just key % 5. So 0 and 5 will have the same 
hashcode, etc.
@@ -144,8 +159,7 @@ public class 
SizeableObject2ObjectOpenCustomHashMapWithCursorTest {
 
   @Test
   public void 
scanWithHashcodeCollisionsAndConcurrentRemoves_ReturnsExpectedElements() {
-    SizeableObject2ObjectOpenCustomHashMapWithCursor<Integer, String> map =
-        new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(COLLIDING_HASH);
+    Integer2StringMap map = new Integer2StringMap(COLLIDING_HASH);
     IntStream.range(0, 10).forEach(i -> map.put(i, "value-" + i));
 
     Map<Integer, String> scanned = new HashMap<>();
@@ -166,8 +180,7 @@ public class 
SizeableObject2ObjectOpenCustomHashMapWithCursorTest {
 
   @Test
   public void scanWithGrowingTable_DoesNotMissElements() {
-    SizeableObject2ObjectOpenCustomHashMapWithCursor<Integer, String> map =
-        new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(NATURAL_HASH);
+    Integer2StringMap map = new Integer2StringMap();
     IntStream.range(0, 10).forEach(i -> map.put(i, "value-" + i));
 
     Map<Integer, String> scanned = new HashMap<>();
@@ -188,8 +201,7 @@ public class 
SizeableObject2ObjectOpenCustomHashMapWithCursorTest {
 
   @Test
   public void scanWithShrinkingTable_DoesNotMissElements() {
-    SizeableObject2ObjectOpenCustomHashMapWithCursor<Integer, String> map =
-        new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(NATURAL_HASH);
+    Integer2StringMap map = new Integer2StringMap();
     IntStream.range(0, 500).forEach(i -> map.put(i, "value-" + i));
 
     Map<Integer, String> scanned = new HashMap<>();
@@ -213,104 +225,48 @@ public class 
SizeableObject2ObjectOpenCustomHashMapWithCursorTest {
     
assertThat(SizeableObject2ObjectOpenCustomHashMapWithCursor.rev(0xFF)).isEqualTo(0xFF000000);
   }
 
-  // This test can be used to derive the formula for calculating overhead 
associated with resizing
-  // the backing arrays of the map. If it fails, examine the output of this 
test and determine if
-  // the constant or the formula needs to be adjusted. If all the assertions 
fail with a constant
-  // difference between the expected and actual, adjust the constant. If they 
fail with inconsistent
-  // differences, adjust the formula
-  @Test
-  public void backingArrayOverheadCalculationTest() {
-    SizeableObject2ObjectOpenCustomHashMapWithCursor<byte[], byte[]> map =
-        new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(0, 
ByteArrays.HASH_STRATEGY);
-    int backingArrayOverhead;
-    SoftAssertions softly = new SoftAssertions();
-    for (int i = 0; i < 250; ++i) {
-      map.put(new byte[i], new byte[250 - i]);
-
-      // Calculate the overhead associated only with the backing array
-      backingArrayOverhead = sizer.sizeof(map) - 
map.getArrayContentsOverhead();
-      int expected = BACKING_ARRAY_OVERHEAD_CONSTANT
-          + BACKING_ARRAY_LENGTH_COEFFICIENT * 
map.getTotalBackingArrayLength();
-      softly.assertThat(backingArrayOverhead)
-          .as("Expecting backing array overhead to = "
-              + "backing array constant + (backing array length coefficient * 
total backing array length)"
-              + " = " + BACKING_ARRAY_OVERHEAD_CONSTANT + " + (" + 
BACKING_ARRAY_LENGTH_COEFFICIENT
-              + " * " + map.getTotalBackingArrayLength() + ") but was off by "
-              + (expected - backingArrayOverhead))
-          .isEqualTo(expected);
-    }
-    softly.assertAll();
-  }
-
   @Test
   public void putUpdatesSizeWhenCreatingNewEntry() {
-    SizeableObject2ObjectOpenCustomHashMapWithCursor<byte[], byte[]> hash =
-        new 
SizeableObject2ObjectOpenCustomHashMapWithCursor<>(ByteArrays.HASH_STRATEGY);
-    assertThat(hash.getSizeInBytes()).isEqualTo(sizer.sizeof(hash));
+    RedisHash.Hash hash = new RedisHash.Hash();
+    assertThat(hash.getSizeInBytes()).isEqualTo(expectedSize(hash));
     hash.put(new byte[] {(byte) 1}, new byte[] {(byte) 1});
-    assertThat(hash.getSizeInBytes()).isEqualTo(sizer.sizeof(hash));
+    assertThat(hash.getSizeInBytes()).isEqualTo(expectedSize(hash));
   }
 
   @Test
   public void putUpdatesSizeWhenUpdatingExistingEntry() {
-    SizeableObject2ObjectOpenCustomHashMapWithCursor<byte[], byte[]> hash =
-        new 
SizeableObject2ObjectOpenCustomHashMapWithCursor<>(ByteArrays.HASH_STRATEGY);
-    assertThat(hash.getSizeInBytes()).isEqualTo(sizer.sizeof(hash));
+    RedisHash.Hash hash = new RedisHash.Hash();
+    assertThat(hash.getSizeInBytes()).isEqualTo(expectedSize(hash));
     byte[] key = new byte[1];
     byte[] initialValue = new byte[1];
 
     hash.put(key, initialValue);
-    assertThat(hash.getSizeInBytes()).isEqualTo(sizer.sizeof(hash));
+    assertThat(hash.getSizeInBytes()).isEqualTo(expectedSize(hash));
 
     byte[] largerValue = new byte[100];
     hash.put(key, largerValue);
-    assertThat(hash.getSizeInBytes()).isEqualTo(sizer.sizeof(hash));
+    assertThat(hash.getSizeInBytes()).isEqualTo(expectedSize(hash));
 
     byte[] smallerValue = new byte[2];
     hash.put(key, smallerValue);
-    assertThat(hash.getSizeInBytes()).isEqualTo(sizer.sizeof(hash));
+    assertThat(hash.getSizeInBytes()).isEqualTo(expectedSize(hash));
   }
 
   @Test
   public void removeUpdatesSize() {
-    SizeableObject2ObjectOpenCustomHashMapWithCursor<byte[], byte[]> hash =
-        new 
SizeableObject2ObjectOpenCustomHashMapWithCursor<>(ByteArrays.HASH_STRATEGY);
+    RedisHash.Hash hash = new RedisHash.Hash();
     byte[] key = new byte[1];
     byte[] initialValue = new byte[100];
 
     hash.put(key, initialValue);
-    assertThat(hash.getSizeInBytes()).isEqualTo(sizer.sizeof(hash));
+    assertThat(hash.getSizeInBytes()).isEqualTo(expectedSize(hash));
 
     hash.remove(key);
-    assertThat(hash.getSizeInBytes()).isEqualTo(sizer.sizeof(hash));
+    assertThat(hash.getSizeInBytes()).isEqualTo(expectedSize(hash));
   }
 
-  @Test
-  public void calculateBackingArraysOverheadForDifferentInitialSizes() {
-    for (int i = 0; i < 1000; ++i) {
-      SizeableObject2ObjectOpenCustomHashMapWithCursor<byte[], byte[]> hash =
-          new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(i, 
ByteArrays.HASH_STRATEGY);
-      
assertThat(hash.calculateBackingArraysOverhead()).isEqualTo(sizer.sizeof(hash));
-    }
-  }
-
-  @Test
-  public void 
calculateBackingArraysOverheadForDifferentLoadFactorsAndInitialSizes() {
-    Random random = new Random(42);
-    for (int i = 0; i < 1000; ++i) {
-      float loadFactor = random.nextFloat();
-      int initialSize = random.nextInt(1000);
-
-      // Create a map with a random initial size and load factor
-      SizeableObject2ObjectOpenCustomHashMapWithCursor<byte[], byte[]> hash =
-          new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(initialSize, 
loadFactor,
-              ByteArrays.HASH_STRATEGY);
-
-      // Confirm that the calculated value matches the actual value
-      assertThat(hash.calculateBackingArraysOverhead())
-          .as("load factor = " + loadFactor + ", initial size = " + 
initialSize)
-          .isEqualTo(sizer.sizeof(hash));
-    }
+  private int 
expectedSize(SizeableObject2ObjectOpenCustomHashMapWithCursor<byte[], ?> map) {
+    return sizer.sizeof(map) - sizer.sizeof(ByteArrays.HASH_STRATEGY);
   }
 
   @Test
@@ -325,11 +281,9 @@ public class 
SizeableObject2ObjectOpenCustomHashMapWithCursorTest {
       byte[] value = {(byte) (initialNumberOfElements - i)};
       initialElements.put(key, value);
     }
-    SizeableObject2ObjectOpenCustomHashMapWithCursor<byte[], byte[]> hash =
-        new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(initialElements,
-            ByteArrays.HASH_STRATEGY);
+    RedisHash.Hash hash = new RedisHash.Hash(initialElements);
 
-    assertThat(hash.getSizeInBytes()).isEqualTo(sizer.sizeof(hash));
+    assertThat(hash.getSizeInBytes()).isEqualTo(expectedSize(hash));
 
     // Add more elements to force a resizing of the backing arrays and confirm 
that size changes as
     // expected
@@ -338,7 +292,7 @@ public class 
SizeableObject2ObjectOpenCustomHashMapWithCursorTest {
       byte[] key = {(byte) i};
       byte[] value = {(byte) (totalNumberOfElements - i)};
       hash.put(key, value);
-      assertThat(hash.getSizeInBytes()).isEqualTo(sizer.sizeof(hash));
+      assertThat(hash.getSizeInBytes()).isEqualTo(expectedSize(hash));
     }
 
     // Update values and confirm that size changes as expected
@@ -346,7 +300,7 @@ public class 
SizeableObject2ObjectOpenCustomHashMapWithCursorTest {
       byte[] key = {(byte) i};
       byte[] value = {(byte) i};
       hash.put(key, value);
-      assertThat(hash.getSizeInBytes()).isEqualTo(sizer.sizeof(hash));
+      assertThat(hash.getSizeInBytes()).isEqualTo(expectedSize(hash));
     }
 
     assertThat(hash.size()).isEqualTo(totalNumberOfElements);
@@ -354,7 +308,7 @@ public class 
SizeableObject2ObjectOpenCustomHashMapWithCursorTest {
     // Remove all elements and confirm that size changes as expected
     for (int i = 0; i < totalNumberOfElements; ++i) {
       hash.remove(new byte[] {(byte) i});
-      assertThat(hash.getSizeInBytes()).isEqualTo(sizer.sizeof(hash));
+      assertThat(hash.getSizeInBytes()).isEqualTo(expectedSize(hash));
     }
 
     assertThat(hash.size()).isEqualTo(0);
@@ -369,37 +323,44 @@ public class 
SizeableObject2ObjectOpenCustomHashMapWithCursorTest {
     // Create a map with an initial size
     for (int i = 0; i < initialNumberOfElements; ++i) {
       byte[] key = {(byte) i};
-      byte[] member = new byte[i];
+      byte[] member = key;
       byte[] scoreBytes = String.valueOf(i).getBytes();
       RedisSortedSet.OrderedSetEntry value = new 
RedisSortedSet.OrderedSetEntry(member, scoreBytes);
       initialElements.put(key, value);
     }
-    SizeableObject2ObjectOpenCustomHashMapWithCursor<byte[], 
RedisSortedSet.OrderedSetEntry> hash =
-        new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(initialElements,
-            ByteArrays.HASH_STRATEGY);
+    RedisSortedSet.MemberMap hash = new 
RedisSortedSet.MemberMap(initialElements);
 
-    assertThat(hash.getSizeInBytes()).isEqualTo(sizer.sizeof(hash));
+    assertThat(hash.getSizeInBytes()).isEqualTo(expectedSize(hash));
 
     // Add more elements to force a resizing of the backing arrays and confirm 
that size changes as
     // expected
     int totalNumberOfElements = initialNumberOfElements + elementsToAdd;
     for (int i = initialNumberOfElements; i < totalNumberOfElements; ++i) {
       byte[] key = {(byte) i};
-      byte[] member = new byte[i];
+      byte[] member = key;
       byte[] scoreBytes = String.valueOf(totalNumberOfElements - i).getBytes();
       RedisSortedSet.OrderedSetEntry value = new 
RedisSortedSet.OrderedSetEntry(member, scoreBytes);
       hash.put(key, value);
-      assertThat(hash.getSizeInBytes()).isEqualTo(sizer.sizeof(hash));
+      assertThat(hash.getSizeInBytes()).isEqualTo(expectedSize(hash));
     }
 
     // Update values and confirm that size changes as expected
     for (int i = initialNumberOfElements; i < totalNumberOfElements; ++i) {
       byte[] key = {(byte) i};
-      byte[] member = new byte[i];
+      byte[] member = key;
       byte[] scoreBytes = String.valueOf(i).getBytes();
-      RedisSortedSet.OrderedSetEntry value = new 
RedisSortedSet.OrderedSetEntry(member, scoreBytes);
-      hash.put(key, value);
-      assertThat(hash.getSizeInBytes()).isEqualTo(sizer.sizeof(hash));
+      RedisSortedSet.OrderedSetEntry value = hash.get(key);
+      byte[] oldScoreBytes = value.getScoreBytes();
+      int scoreDelta = memoryOverhead(scoreBytes)
+          - memoryOverhead(oldScoreBytes);
+
+      int oldSize = hash.getSizeInBytes();
+      value.updateScore(scoreBytes);
+      int sizeDelta = hash.getSizeInBytes() - oldSize;
+
+      assertThat(sizeDelta).isEqualTo(scoreDelta);
+
+      assertThat(hash.getSizeInBytes()).isEqualTo(expectedSize(hash));
     }
 
     assertThat(hash.size()).isEqualTo(totalNumberOfElements);
@@ -407,7 +368,7 @@ public class 
SizeableObject2ObjectOpenCustomHashMapWithCursorTest {
     // Remove all elements and confirm that size changes as expected
     for (int i = 0; i < totalNumberOfElements; ++i) {
       hash.remove(new byte[] {(byte) i});
-      assertThat(hash.getSizeInBytes()).isEqualTo(sizer.sizeof(hash));
+      assertThat(hash.getSizeInBytes()).isEqualTo(expectedSize(hash));
     }
 
     assertThat(hash.size()).isEqualTo(0);
diff --git 
a/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/SizeableObjectOpenCustomHashSetTest.java
 
b/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/SizeableObjectOpenCustomHashSetTest.java
index 2d90e93..d2667c3 100644
--- 
a/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/SizeableObjectOpenCustomHashSetTest.java
+++ 
b/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/collections/SizeableObjectOpenCustomHashSetTest.java
@@ -14,131 +14,23 @@
  */
 package org.apache.geode.redis.internal.collections;
 
-import static 
org.apache.geode.redis.internal.collections.SizeableObjectOpenCustomHashSet.BACKING_ARRAY_LENGTH_COEFFICIENT;
-import static 
org.apache.geode.redis.internal.collections.SizeableObjectOpenCustomHashSet.BACKING_ARRAY_OVERHEAD_CONSTANT;
 import static org.assertj.core.api.Assertions.assertThat;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Random;
 
 import it.unimi.dsi.fastutil.bytes.ByteArrays;
-import org.assertj.core.api.SoftAssertions;
 import org.junit.Test;
 
 import org.apache.geode.cache.util.ObjectSizer;
 import org.apache.geode.internal.size.ReflectionObjectSizer;
-import org.apache.geode.internal.size.ReflectionSingleObjectSizer;
-import org.apache.geode.internal.size.SingleObjectSizer;
+import org.apache.geode.redis.internal.data.RedisSet;
 
 public class SizeableObjectOpenCustomHashSetTest {
   private final ObjectSizer sizer = ReflectionObjectSizer.getInstance();
-  private final SingleObjectSizer elementSizer = new 
ReflectionSingleObjectSizer();
 
-  // This test can be used to derive the formula for calculating overhead 
associated with resizing
-  // the backing array of the set. If it fails examine the output of this test 
and determine if the
-  // constant or the formula needs to be adjusted. If all the assertions fail 
with a constant
-  // difference between the expected and actual, adjust the constant. If they 
fail with inconsistent
-  // differences, adjust the formula
-  @Test
-  public void backingArrayOverheadCalculationTest() {
-    SizeableObjectOpenCustomHashSet<byte[]> set =
-        new SizeableObjectOpenCustomHashSet<>(0, ByteArrays.HASH_STRATEGY);
-    int backingArrayOverhead;
-    SoftAssertions softly = new SoftAssertions();
-    for (int i = 0; i < 250; ++i) {
-      set.add(new byte[i]);
-
-      // Calculate the overhead associated only with the backing array
-      backingArrayOverhead = sizer.sizeof(set) - set.getMemberOverhead();
-      int expected = BACKING_ARRAY_OVERHEAD_CONSTANT
-          + BACKING_ARRAY_LENGTH_COEFFICIENT * set.getBackingArrayLength();
-      softly.assertThat(backingArrayOverhead)
-          .as("Expecting backing array overhead to = "
-              + "backing array constant + (backing array length coefficient * 
backing array length)"
-              + " = " + BACKING_ARRAY_OVERHEAD_CONSTANT + " + (" + 
BACKING_ARRAY_LENGTH_COEFFICIENT
-              + " * " + set.getBackingArrayLength() + ") but was off by "
-              + (expected - backingArrayOverhead))
-          .isEqualTo(expected);
-    }
-    softly.assertAll();
-  }
-
-  @Test
-  public void addIncreasesMemberOverheadByCorrectAmount() {
-    SizeableObjectOpenCustomHashSet<byte[]> set =
-        new SizeableObjectOpenCustomHashSet<>(ByteArrays.HASH_STRATEGY);
-    int initialSize = set.getMemberOverhead();
-    List<byte[]> members = new ArrayList<>();
-    for (int i = 0; i < 100; ++i) {
-      members.add(new byte[i]);
-    }
-
-    // Add a duplicate member to check that member overhead isn't increased if 
a member isn't added
-    // to the set
-    members.add(new byte[100]);
-
-    for (byte[] bytes : members) {
-      boolean added = set.add(bytes);
-      if (added) {
-        long expectedOverhead = elementSizer.sizeof(bytes);
-        assertThat(expectedOverhead).isEqualTo(set.getMemberOverhead() - 
initialSize);
-        initialSize = set.getMemberOverhead();
-      } else {
-        assertThat(set.getMemberOverhead() - initialSize).isZero();
-      }
-    }
-  }
-
-  @Test
-  public void removeDecreasesMemberOverheadByCorrectAmount() {
-    SizeableObjectOpenCustomHashSet<byte[]> set =
-        new SizeableObjectOpenCustomHashSet<>(ByteArrays.HASH_STRATEGY);
-    List<byte[]> members = new ArrayList<>();
-    for (int i = 0; i < 100; ++i) {
-      members.add(new byte[i]);
-    }
-    set.addAll(members);
-
-    // Add a byte to the list that isn't present in the set to ensure that 
member overhead isn't
-    // decreased when a member isn't actually removed
-    members.add(new byte[101]);
-
-    int initialSize = set.getMemberOverhead();
-
-    for (byte[] bytes : members) {
-      boolean removed = set.remove(bytes);
-      if (removed) {
-        long expectedOverhead = elementSizer.sizeof(bytes);
-        assertThat(expectedOverhead).isEqualTo(initialSize - 
set.getMemberOverhead());
-        initialSize = set.getMemberOverhead();
-      } else {
-        assertThat(set.getMemberOverhead() - initialSize).isZero();
-      }
-    }
-  }
-
-  @Test
-  public void calculateBackingArrayOverheadForDifferentInitialSizes() {
-    for (int i = 0; i < 1000; ++i) {
-      SizeableObjectOpenCustomHashSet<byte[]> set =
-          new SizeableObjectOpenCustomHashSet<>(i, ByteArrays.HASH_STRATEGY);
-      
assertThat(set.calculateBackingArrayOverhead()).isEqualTo(sizer.sizeof(set));
-    }
-  }
-
-  @Test
-  public void 
calculateBackingArrayOverheadForDifferentLoadFactorsAndInitialSizes() {
-    Random random = new Random(42);
-    for (int i = 0; i < 1000; ++i) {
-      float loadFactor = random.nextFloat();
-      int initialSize = random.nextInt(1000);
-      SizeableObjectOpenCustomHashSet<byte[]> set =
-          new SizeableObjectOpenCustomHashSet<>(initialSize, loadFactor, 
ByteArrays.HASH_STRATEGY);
-      assertThat(set.calculateBackingArrayOverhead())
-          .as("load factor = " + loadFactor + ", initial size = " + 
initialSize)
-          .isEqualTo(sizer.sizeof(set));
-    }
+  private int expectedSize(RedisSet.MemberSet set) {
+    return sizer.sizeof(set) - sizer.sizeof(ByteArrays.HASH_STRATEGY);
   }
 
   @Test
@@ -150,21 +42,20 @@ public class SizeableObjectOpenCustomHashSetTest {
       initialElements.add(new byte[] {(byte) i});
     }
     // Create a set with an initial size and confirm that it correctly reports 
its size
-    SizeableObjectOpenCustomHashSet<byte[]> set =
-        new SizeableObjectOpenCustomHashSet<>(initialElements, 
ByteArrays.HASH_STRATEGY);
-    assertThat(set.getSizeInBytes()).isEqualTo(sizer.sizeof(set));
+    RedisSet.MemberSet set = new RedisSet.MemberSet(initialElements);
+    assertThat(set.getSizeInBytes()).isEqualTo(expectedSize(set));
 
     // Add enough members to force a resize and assert that the size is 
correct after each add
     for (int i = initialNumberOfElements; i < initialNumberOfElements + 
elementsToAdd; ++i) {
       set.add(new byte[] {(byte) i});
-      assertThat(set.getSizeInBytes()).isEqualTo(sizer.sizeof(set));
+      assertThat(set.getSizeInBytes()).isEqualTo(expectedSize(set));
     }
     assertThat(set.size()).isEqualTo(initialNumberOfElements + elementsToAdd);
 
     // Remove all the members and assert that the size is correct after each 
remove
     for (int i = 0; i < initialNumberOfElements + elementsToAdd; ++i) {
       set.remove(new byte[] {(byte) i});
-      assertThat(set.getSizeInBytes()).isEqualTo(sizer.sizeof(set));
+      assertThat(set.getSizeInBytes()).isEqualTo(expectedSize(set));
     }
     assertThat(set.size()).isEqualTo(0);
   }
diff --git 
a/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/data/RedisHashTest.java
 
b/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/data/RedisHashTest.java
index 1862cb1..db8531c 100644
--- 
a/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/data/RedisHashTest.java
+++ 
b/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/data/RedisHashTest.java
@@ -16,7 +16,7 @@
 
 package org.apache.geode.redis.internal.data;
 
-import static 
org.apache.geode.redis.internal.data.RedisHash.BASE_REDIS_HASH_OVERHEAD;
+import static 
org.apache.geode.redis.internal.data.RedisHash.REDIS_HASH_OVERHEAD;
 import static org.apache.geode.redis.internal.netty.Coder.stringToBytes;
 import static org.apache.geode.util.internal.UncheckedUtils.uncheckedCast;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -52,7 +52,6 @@ import 
org.apache.geode.internal.serialization.DataSerializableFixedID;
 import org.apache.geode.internal.serialization.SerializationContext;
 import org.apache.geode.internal.size.ReflectionObjectSizer;
 import org.apache.geode.internal.size.ReflectionSingleObjectSizer;
-import 
org.apache.geode.redis.internal.collections.SizeableObject2ObjectOpenCustomHashMapWithCursor;
 import org.apache.geode.redis.internal.netty.Coder;
 
 public class RedisHashTest {
@@ -224,10 +223,9 @@ public class RedisHashTest {
   @Test
   public void constantBaseRedisHashOverhead_shouldEqualCalculatedOverhead() {
     RedisHash hash = new RedisHash(Collections.emptyList());
-    SizeableObject2ObjectOpenCustomHashMapWithCursor<byte[], byte[]> 
backingHash =
-        new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(0, 
ByteArrays.HASH_STRATEGY);
+    RedisHash.Hash backingHash = new RedisHash.Hash(0);
 
-    assertThat(sizer.sizeof(hash) - 
sizer.sizeof(backingHash)).isEqualTo(BASE_REDIS_HASH_OVERHEAD);
+    assertThat(sizer.sizeof(hash) - 
sizer.sizeof(backingHash)).isEqualTo(REDIS_HASH_OVERHEAD);
   }
 
   /******* constructor *******/
@@ -240,7 +238,7 @@ public class RedisHashTest {
 
     RedisHash redisHash = new RedisHash(data);
 
-    final int expected = sizer.sizeof(redisHash);
+    final int expected = sizer.sizeof(redisHash) - 
sizer.sizeof(ByteArrays.HASH_STRATEGY);
     final int actual = redisHash.getSizeInBytes();
 
     assertThat(actual).isEqualTo(expected);
@@ -251,12 +249,16 @@ public class RedisHashTest {
     RedisHash redisHash =
         createRedisHash("aSuperLongField", "value", "field", 
"aSuperLongValue");
 
-    final int expected = sizer.sizeof(redisHash);
+    final int expected = expectedSize(redisHash);
     final int actual = redisHash.getSizeInBytes();
 
     assertThat(actual).isEqualTo(expected);
   }
 
+  private int expectedSize(RedisHash hash) {
+    return sizer.sizeof(hash) - sizer.sizeof(ByteArrays.HASH_STRATEGY);
+  }
+
   @Test
   public void should_calculateSize_equalToROSSize_withManyEntries() {
     final String baseField = "longerbase";
@@ -270,7 +272,7 @@ public class RedisHashTest {
     RedisHash hash = new RedisHash(elements);
 
     Integer actual = hash.getSizeInBytes();
-    int expected = sizer.sizeof(hash);
+    int expected = expectedSize(hash);
 
     assertThat(actual).isEqualTo(expected);
   }
@@ -343,7 +345,7 @@ public class RedisHashTest {
     hash.hset(region, key, initialData, false);
     hash.clearDelta();
 
-    long initialSize = sizer.sizeof(hash);
+    long initialSize = expectedSize(hash);
 
     List<byte[]> finalData = new ArrayList<>();
     finalData.add(stringToBytes(field));
@@ -352,7 +354,7 @@ public class RedisHashTest {
     hash.hset(region, key, finalData, false);
     hash.clearDelta();
 
-    assertThat(hash.getSizeInBytes()).isEqualTo(sizer.sizeof(hash));
+    assertThat(hash.getSizeInBytes()).isEqualTo(expectedSize(hash));
 
     long expectedSizeChange = 
elementSizer.sizeof(Coder.stringToBytes(finalValue))
         - elementSizer.sizeof(Coder.stringToBytes(initialValue));
@@ -477,7 +479,7 @@ public class RedisHashTest {
     int finalSize = redisHash.getSizeInBytes();
     assertThat(finalSize).isLessThan(initialSize);
 
-    assertThat(finalSize).isEqualTo(sizer.sizeof(redisHash));
+    assertThat(finalSize).isEqualTo(expectedSize(redisHash));
   }
 
   private RedisHash createRedisHash(String... keysAndValues) {
diff --git 
a/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/data/RedisSetTest.java
 
b/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/data/RedisSetTest.java
index 413c7e0..f01d5f1 100644
--- 
a/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/data/RedisSetTest.java
+++ 
b/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/data/RedisSetTest.java
@@ -17,7 +17,6 @@
 package org.apache.geode.redis.internal.data;
 
 import static 
org.apache.geode.redis.internal.data.NullRedisDataStructures.NULL_REDIS_SET;
-import static 
org.apache.geode.redis.internal.data.RedisSet.BASE_REDIS_SET_OVERHEAD;
 import static org.apache.geode.redis.internal.netty.Coder.stringToBytes;
 import static org.apache.geode.util.internal.UncheckedUtils.uncheckedCast;
 import static org.assertj.core.api.Assertions.assertThat;
@@ -48,7 +47,6 @@ import 
org.apache.geode.internal.serialization.ByteArrayDataInput;
 import org.apache.geode.internal.serialization.DataSerializableFixedID;
 import org.apache.geode.internal.serialization.SerializationContext;
 import org.apache.geode.internal.size.ReflectionObjectSizer;
-import 
org.apache.geode.redis.internal.collections.SizeableObjectOpenCustomHashSet;
 import org.apache.geode.redis.internal.netty.Coder;
 
 public class RedisSetTest {
@@ -189,7 +187,7 @@ public class RedisSetTest {
     Set<byte[]> members = new 
ObjectOpenCustomHashSet<>(ByteArrays.HASH_STRATEGY);
     RedisSet set = new RedisSet(members);
 
-    int expected = sizer.sizeof(set);
+    int expected = expectedSize(set);
     int actual = set.getSizeInBytes();
 
     assertThat(actual).isEqualTo(expected);
@@ -202,7 +200,7 @@ public class RedisSetTest {
     RedisSet set = new RedisSet(members);
 
     int actual = set.getSizeInBytes();
-    int expected = sizer.sizeof(set);
+    int expected = expectedSize(set);
 
     assertThat(actual).isEqualTo(expected);
   }
@@ -212,18 +210,22 @@ public class RedisSetTest {
     for (int i = 0; i < 1024; i += 16) {
       RedisSet set = createRedisSetOfSpecifiedSize(i);
 
-      int expected = sizer.sizeof(set);
+      int expected = expectedSize(set);
       int actual = set.getSizeInBytes();
 
       assertThat(actual).isEqualTo(expected);
     }
   }
 
+  private int expectedSize(RedisSet set) {
+    return sizer.sizeof(set) - sizer.sizeof(ByteArrays.HASH_STRATEGY);
+  }
+
   @Test
   public void should_calculateSize_equalToROS_withVaryingMemberSize() {
     for (int i = 0; i < 1024; i += 16) {
       RedisSet set = createRedisSetWithMemberOfSpecifiedSize(i * 64);
-      int expected = sizer.sizeof(set);
+      int expected = expectedSize(set);
       int actual = set.getSizeInBytes();
 
       assertThat(actual).isEqualTo(expected);
@@ -248,7 +250,7 @@ public class RedisSetTest {
 
     set.clearDelta(); // because the region is mocked, the delta has to be 
explicitly cleared
 
-    assertThat(set.getSizeInBytes()).isEqualTo(sizer.sizeof(set));
+    assertThat(set.getSizeInBytes()).isEqualTo(expectedSize(set));
   }
 
   @Test
@@ -270,7 +272,7 @@ public class RedisSetTest {
       set.clearDelta(); // because the region is mocked, the delta has to be 
explicitly cleared
 
       long actual = set.getSizeInBytes();
-      long expected = sizer.sizeof(set);
+      long expected = expectedSize(set);
 
       assertThat(actual).isEqualTo(expected);
     }
@@ -298,7 +300,7 @@ public class RedisSetTest {
     set.clearDelta(); // because the region is mocked, the delta has to be 
explicitly cleared
 
     long finalSize = set.getSizeInBytes();
-    long expectedSize = sizer.sizeof(set);
+    long expectedSize = expectedSize(set);
 
     assertThat(finalSize).isEqualTo(expectedSize);
   }
@@ -318,7 +320,7 @@ public class RedisSetTest {
 
     RedisSet set = new RedisSet(initialMembers);
 
-    assertThat(set.getSizeInBytes()).isEqualTo(sizer.sizeof(set));
+    assertThat(set.getSizeInBytes()).isEqualTo(expectedSize(set));
 
     int membersToAdd = numOfInitialMembers * 3;
     doAddsAndAssertSize(set, membersToAdd);
@@ -328,21 +330,6 @@ public class RedisSetTest {
     doAddsAndAssertSize(set, membersToAdd);
   }
 
-  /******** constants *******/
-  // This test contains the math that is used to derive the constant in 
RedisSet. If this test
-  // starts failing, it is because the overhead of RedisSet has changed. If it 
has decreased, good
-  // job! You can change the constant in RedisSet to reflect that. If it has 
increased, carefully
-  // consider that increase before changing the constants.
-  @Test
-  public void baseOverheadConstant_shouldMatchReflectedSize() {
-    RedisSet set = new RedisSet(Collections.emptyList());
-    SizeableObjectOpenCustomHashSet<byte[]> backingSet =
-        new SizeableObjectOpenCustomHashSet<>(0, ByteArrays.HASH_STRATEGY);
-    int baseRedisSetOverhead = sizer.sizeof(set) - sizer.sizeof(backingSet);
-
-    assertThat(baseRedisSetOverhead).isEqualTo(BASE_REDIS_SET_OVERHEAD);
-  }
-
   /******* helper methods *******/
   private RedisSet createRedisSetOfSpecifiedSize(int setSize) {
     List<byte[]> arrayList = new ArrayList<>();
@@ -388,7 +375,7 @@ public class RedisSetTest {
 
       assertThat(calculatedOH).isEqualTo(actualOverhead);
     }
-    assertThat(set.getSizeInBytes()).isEqualTo(sizer.sizeof(set));
+    assertThat(set.getSizeInBytes()).isEqualTo(expectedSize(set));
   }
 
   void doRemovesAndAssertSize(RedisSet set, int membersToRemove) {
@@ -405,6 +392,6 @@ public class RedisSetTest {
 
       assertThat(calculatedOH).isEqualTo(actualOverhead);
     }
-    assertThat(set.getSizeInBytes()).isEqualTo(sizer.sizeof(set));
+    assertThat(set.getSizeInBytes()).isEqualTo(expectedSize(set));
   }
 }
diff --git 
a/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/data/RedisSortedSetTest.java
 
b/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/data/RedisSortedSetTest.java
index 0e3bc3e..499b01a 100644
--- 
a/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/data/RedisSortedSetTest.java
+++ 
b/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/data/RedisSortedSetTest.java
@@ -16,8 +16,8 @@
 
 package org.apache.geode.redis.internal.data;
 
-import static 
org.apache.geode.redis.internal.data.RedisSortedSet.BASE_REDIS_SORTED_SET_OVERHEAD;
-import static 
org.apache.geode.redis.internal.data.RedisSortedSet.OrderedSetEntry.BASE_ORDERED_SET_ENTRY_SIZE;
+import static 
org.apache.geode.redis.internal.data.RedisSortedSet.OrderedSetEntry.ORDERED_SET_ENTRY_OVERHEAD;
+import static 
org.apache.geode.redis.internal.data.RedisSortedSet.REDIS_SORTED_SET_OVERHEAD;
 import static org.apache.geode.redis.internal.netty.Coder.doubleToBytes;
 import static org.apache.geode.redis.internal.netty.Coder.stringToBytes;
 import static 
org.apache.geode.redis.internal.netty.StringBytesGlossary.bGREATEST_MEMBER_NAME;
@@ -55,9 +55,6 @@ import 
org.apache.geode.internal.serialization.ByteArrayDataInput;
 import org.apache.geode.internal.serialization.DataSerializableFixedID;
 import org.apache.geode.internal.serialization.SerializationContext;
 import org.apache.geode.internal.size.ReflectionObjectSizer;
-import org.apache.geode.redis.internal.collections.OrderStatisticsSet;
-import org.apache.geode.redis.internal.collections.OrderStatisticsTree;
-import 
org.apache.geode.redis.internal.collections.SizeableObject2ObjectOpenCustomHashMapWithCursor;
 import org.apache.geode.redis.internal.delta.RemsDeltaInfo;
 import org.apache.geode.redis.internal.executor.sortedset.ZAddOptions;
 import org.apache.geode.redis.internal.netty.Coder;
@@ -549,13 +546,12 @@ public class RedisSortedSetTest {
   @Test
   public void baseRedisSortedSetOverheadConstant_shouldMatchReflectedSize() {
     RedisSortedSet set = new RedisSortedSet(Collections.emptyList());
-    SizeableObject2ObjectOpenCustomHashMapWithCursor<byte[], 
RedisSortedSet.OrderedSetEntry> backingMap =
-        new SizeableObject2ObjectOpenCustomHashMapWithCursor<>(0, 
ByteArrays.HASH_STRATEGY);
-    OrderStatisticsSet<RedisSortedSet.OrderedSetEntry> backingTree = new 
OrderStatisticsTree<>();
+    RedisSortedSet.MemberMap backingMap = new RedisSortedSet.MemberMap(0);
+    RedisSortedSet.ScoreSet backingTree = new RedisSortedSet.ScoreSet();
     int baseRedisSetOverhead =
         sizer.sizeof(set) - sizer.sizeof(backingMap) - 
sizer.sizeof(backingTree);
 
-    assertThat(BASE_REDIS_SORTED_SET_OVERHEAD).isEqualTo(baseRedisSetOverhead);
+    assertThat(REDIS_SORTED_SET_OVERHEAD).isEqualTo(baseRedisSetOverhead);
   }
 
   @Test
@@ -566,7 +562,7 @@ public class RedisSortedSetTest {
         new RedisSortedSet.OrderedSetEntry(memberBytes, scoreBytes);
     int expectedSize = sizer.sizeof(entry) - sizer.sizeof(scoreBytes) - 
sizer.sizeof(memberBytes);
 
-    assertThat(BASE_ORDERED_SET_ENTRY_SIZE).isEqualTo(expectedSize);
+    assertThat(ORDERED_SET_ENTRY_OVERHEAD).isEqualTo(expectedSize);
   }
 
   /****************** Size ******************/
@@ -578,19 +574,19 @@ public class RedisSortedSetTest {
     ZAddOptions options = new ZAddOptions(ZAddOptions.Exists.NONE, false, 
false);
     RedisSortedSet sortedSet = new RedisSortedSet(Collections.emptyList());
 
-    int actualSize = sizer.sizeof(sortedSet);
-    int calculatedSize = sortedSet.getSizeInBytes();
-    assertThat(calculatedSize).isEqualTo(actualSize);
+    int expectedSize = sizer.sizeof(sortedSet) - 
sizer.sizeof(ByteArrays.HASH_STRATEGY);
+    int actualSize = sortedSet.getSizeInBytes();
+    assertThat(actualSize).isEqualTo(expectedSize);
 
-    // Add members and scores and confirm that the calculated size is accurate 
after each operation
+    // Add members and scores and confirm that the actual size is accurate 
after each operation
     int numberOfEntries = 100;
     for (int i = 0; i < numberOfEntries; ++i) {
       List<byte[]> scoreAndMember = Arrays.asList(doubleToBytes(i), new 
byte[i]);
       sortedSet.zadd(mockRegion, mockKey, scoreAndMember, options);
       sortedSet.clearDelta();
-      actualSize = sizer.sizeof(sortedSet);
-      calculatedSize = sortedSet.getSizeInBytes();
-      assertThat(calculatedSize).isEqualTo(actualSize);
+      expectedSize = sizer.sizeof(sortedSet) - 
sizer.sizeof(ByteArrays.HASH_STRATEGY);
+      actualSize = sortedSet.getSizeInBytes();
+      assertThat(actualSize).isEqualTo(expectedSize);
     }
   }
 
@@ -607,12 +603,14 @@ public class RedisSortedSetTest {
       sortedSet.zadd(mockRegion, mockKey, scoreAndMember, options);
     }
 
-    // Update half the scores and confirm that the calculated size is accurate 
after each operation
+    // Update half the scores and confirm that the actual size is accurate 
after each operation
     for (int i = 0; i < numberOfEntries / 2; ++i) {
       List<byte[]> scoreAndMember = Arrays.asList(doubleToBytes(i * 2), new 
byte[i]);
       sortedSet.zadd(mockRegion, mockKey, scoreAndMember, options);
       sortedSet.clearDelta();
-      
assertThat(sizer.sizeof(sortedSet)).isEqualTo(sortedSet.getSizeInBytes());
+      int expectedSize = sizer.sizeof(sortedSet) - 
sizer.sizeof(ByteArrays.HASH_STRATEGY);
+      int actualSize = sortedSet.getSizeInBytes();
+      assertThat(actualSize).isEqualTo(expectedSize);
     }
   }
 
@@ -629,12 +627,14 @@ public class RedisSortedSetTest {
       sortedSet.zadd(mockRegion, mockKey, scoreAndMember, options);
     }
 
-    // Remove all members and confirm that the calculated size is accurate 
after each operation
+    // Remove all members and confirm that the actual size is accurate after 
each operation
     for (int i = 0; i < numberOfEntries; ++i) {
       List<byte[]> memberToRemove = Collections.singletonList(new byte[i]);
       sortedSet.zrem(mockRegion, mockKey, memberToRemove);
       sortedSet.clearDelta();
-      
assertThat(sizer.sizeof(sortedSet)).isEqualTo(sortedSet.getSizeInBytes());
+      int expectedSize = sizer.sizeof(sortedSet) - 
sizer.sizeof(ByteArrays.HASH_STRATEGY);
+      int actualSize = sortedSet.getSizeInBytes();
+      assertThat(actualSize).isEqualTo(expectedSize);
     }
   }
 
@@ -656,7 +656,9 @@ public class RedisSortedSetTest {
     for (int i = 0; i < numberOfEntries; ++i) {
       sortedSet.zpopmax(mockRegion, mockKey, 1);
       sortedSet.clearDelta();
-      
assertThat(sizer.sizeof(sortedSet)).isEqualTo(sortedSet.getSizeInBytes());
+      int expectedSize = sizer.sizeof(sortedSet) - 
sizer.sizeof(ByteArrays.HASH_STRATEGY);
+      int actualSize = sortedSet.getSizeInBytes();
+      assertThat(actualSize).isEqualTo(expectedSize);
     }
   }
 
@@ -669,7 +671,7 @@ public class RedisSortedSetTest {
       member = new byte[random.nextInt(50_000)];
       scoreBytes = String.valueOf(random.nextDouble()).getBytes();
       RedisSortedSet.OrderedSetEntry entry = new 
RedisSortedSet.OrderedSetEntry(member, scoreBytes);
-      assertThat(entry.getSizeInBytes()).isEqualTo(sizer.sizeof(entry));
+      assertThat(entry.getSizeInBytes()).isEqualTo(sizer.sizeof(entry) - 
sizer.sizeof(member));
     }
   }
 
diff --git 
a/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/data/RedisStringTest.java
 
b/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/data/RedisStringTest.java
index 7585341..572cd3b 100644
--- 
a/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/data/RedisStringTest.java
+++ 
b/geode-apis-compatible-with-redis/src/test/java/org/apache/geode/redis/internal/data/RedisStringTest.java
@@ -16,7 +16,6 @@
 
 package org.apache.geode.redis.internal.data;
 
-import static 
org.apache.geode.redis.internal.data.RedisString.BASE_REDIS_STRING_OVERHEAD;
 import static org.apache.geode.redis.internal.netty.Coder.longToBytes;
 import static org.apache.geode.redis.internal.netty.Coder.stringToBytes;
 import static org.apache.geode.util.internal.UncheckedUtils.uncheckedCast;
@@ -356,16 +355,18 @@ public class RedisStringTest {
   @Test
   public void changingStringValue_toShorterString_shouldDecreaseSizeInBytes() {
     String baseString = "baseString";
-    String stringToRemove = "asdf";
+    String stringToRemove = "asdf1234567890";
     RedisString string = new RedisString(stringToBytes((baseString + 
stringToRemove)));
 
     int initialSize = string.getSizeInBytes();
+    assertThat(initialSize).isEqualTo(reflectionObjectSizer.sizeof(string));
 
     string.set(stringToBytes(baseString));
 
     int finalSize = string.getSizeInBytes();
+    assertThat(finalSize).isEqualTo(reflectionObjectSizer.sizeof(string));
 
-    assertThat(finalSize).isEqualTo(initialSize - stringToRemove.length());
+    assertThat(finalSize).isLessThan(initialSize);
   }
 
   @Test
@@ -374,38 +375,30 @@ public class RedisStringTest {
     RedisString string = new RedisString(stringToBytes(baseString));
 
     int initialSize = string.getSizeInBytes();
+    assertThat(initialSize).isEqualTo(reflectionObjectSizer.sizeof(string));
 
-    String addedString = "asdf";
+    String addedString = "asdf1234567890";
     string.set(stringToBytes((baseString + addedString)));
 
     int finalSize = string.getSizeInBytes();
+    assertThat(finalSize).isEqualTo(reflectionObjectSizer.sizeof(string));
 
-    assertThat(finalSize).isEqualTo(initialSize + addedString.length());
+    assertThat(finalSize).isGreaterThan(initialSize);
   }
 
   @Test
-  public void 
changingStringValue_toEmptyString_shouldDecreaseSizeInBytes_toBaseSize() {
-    String baseString = "baseString";
-    RedisString string = new RedisString(stringToBytes((baseString + "asdf")));
+  public void changingStringValue_toEmptyString_shouldDecreaseSizeInBytes() {
+    String baseString = "baseString1234567890";
+    final int emptySize = reflectionObjectSizer.sizeof(new 
RedisString(stringToBytes("")));
+    RedisString string = new RedisString(stringToBytes((baseString)));
+    int baseSize = string.getSizeInBytes();
 
     string.set(stringToBytes(""));
 
     int finalSize = string.getSizeInBytes();
 
-    assertThat(finalSize).isEqualTo(BASE_REDIS_STRING_OVERHEAD);
-  }
-
-  /******* constants *******/
-  // this test contains the math that was used to derive the constants in 
RedisString. If this test
-  // starts failing, it is because the overhead of RedisString has changed. If 
it has decreased,
-  // good job! You can change the constant in RedisString to reflect that. If 
it has increased,
-  // carefully consider that increase before changing the constant.
-  @Test
-  public void overheadConstants_shouldMatchCalculatedValue() {
-    RedisString redisString = new RedisString(stringToBytes(""));
-    int calculatedSize = reflectionObjectSizer.sizeof(redisString);
-
-    assertThat(BASE_REDIS_STRING_OVERHEAD).isEqualTo(calculatedSize);
+    assertThat(finalSize).isEqualTo(emptySize);
+    assertThat(finalSize).isLessThan(baseSize);
   }
 
   /******* helper methods *******/
diff --git 
a/geode-core/src/distributedTest/java/org/apache/geode/cache30/MemLRUEvictionControllerDUnitTest.java
 
b/geode-core/src/distributedTest/java/org/apache/geode/cache30/MemLRUEvictionControllerDUnitTest.java
index 92221f2..ab00d20 100644
--- 
a/geode-core/src/distributedTest/java/org/apache/geode/cache30/MemLRUEvictionControllerDUnitTest.java
+++ 
b/geode-core/src/distributedTest/java/org/apache/geode/cache30/MemLRUEvictionControllerDUnitTest.java
@@ -14,6 +14,8 @@
  */
 package org.apache.geode.cache30;
 
+import static org.apache.geode.internal.JvmSizeUtils.getObjectHeaderSize;
+import static org.apache.geode.internal.JvmSizeUtils.roundUpSize;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
@@ -100,16 +102,16 @@ public class MemLRUEvictionControllerDUnitTest extends 
JUnit4CacheTestCase {
     assertNotNull(lruStats);
 
     String sampleKey = new String("10000");
-    int stringSize = JvmSizeUtils.getObjectHeaderSize() // String object
+    int stringSize = getObjectHeaderSize() // String object
         + (2 * 4) + JvmSizeUtils.getReferenceSize(); // 2 ints and a reference 
on a string
-    stringSize = (int) ReflectionSingleObjectSizer.roundUpSize(stringSize);
+    stringSize = roundUpSize(stringSize);
 
-    int charArraySize = sampleKey.length() * 2 + 
JvmSizeUtils.getObjectHeaderSize() // char array
-                                                                               
     // object
+    int charArraySize = sampleKey.length() * 2 + getObjectHeaderSize() // char 
array
+                                                                       // 
object
         + 4; // length of char array
-    charArraySize = (int) 
ReflectionSingleObjectSizer.roundUpSize(charArraySize);
+    charArraySize = roundUpSize(charArraySize);
     assertEquals(stringSize, ReflectionSingleObjectSizer.sizeof(String.class));
-    
assertEquals(ReflectionSingleObjectSizer.roundUpSize(JvmSizeUtils.getObjectHeaderSize()
 + 4),
+    assertEquals(roundUpSize(getObjectHeaderSize() + 4),
         (new ReflectionSingleObjectSizer()).sizeof(new char[0]));
     assertEquals(charArraySize, (new ReflectionSingleObjectSizer()).sizeof(new 
char[5]));
 
@@ -119,9 +121,9 @@ public class MemLRUEvictionControllerDUnitTest extends 
JUnit4CacheTestCase {
     // now that keys are inlined the keySize is 0
     keySize = 0;
     byte[] sampleValue = new byte[1000];
-    int valueSize = sampleValue.length + JvmSizeUtils.getObjectHeaderSize() // 
byte array object;
+    int valueSize = sampleValue.length + getObjectHeaderSize() // byte array 
object;
         + 4; // length of byte array
-    valueSize = (int) ReflectionSingleObjectSizer.roundUpSize(valueSize);
+    valueSize = roundUpSize(valueSize);
     int entrySize = keySize + valueSize + getEntryOverhead(region);
     assertEquals(valueSize, ObjectSizer.DEFAULT.sizeof(sampleValue));
 
diff --git 
a/geode-core/src/distributedTest/java/org/apache/geode/internal/cache/eviction/EvictionObjectSizerDUnitTest.java
 
b/geode-core/src/distributedTest/java/org/apache/geode/internal/cache/eviction/EvictionObjectSizerDUnitTest.java
index 0792797..49eea79 100644
--- 
a/geode-core/src/distributedTest/java/org/apache/geode/internal/cache/eviction/EvictionObjectSizerDUnitTest.java
+++ 
b/geode-core/src/distributedTest/java/org/apache/geode/internal/cache/eviction/EvictionObjectSizerDUnitTest.java
@@ -14,6 +14,7 @@
  */
 package org.apache.geode.internal.cache.eviction;
 
+import static org.apache.geode.internal.JvmSizeUtils.roundUpSize;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 
@@ -44,7 +45,6 @@ import org.apache.geode.internal.cache.RegionMap;
 import org.apache.geode.internal.cache.TestNonSizerObject;
 import org.apache.geode.internal.cache.TestObjectSizerImpl;
 import org.apache.geode.internal.cache.entries.AbstractLRURegionEntry;
-import org.apache.geode.internal.size.ReflectionSingleObjectSizer;
 import org.apache.geode.internal.size.Sizeable;
 import org.apache.geode.logging.internal.OSProcess;
 import org.apache.geode.test.dunit.LogWriterUtils;
@@ -89,7 +89,7 @@ public class EvictionObjectSizerDUnitTest extends 
CacheTestCase {
     int keySize = 0;
     int valueSize =
         JvmSizeUtils.getObjectHeaderSize() + 4 /* array length */ + (1024 * 
1024) /* bytes */;
-    valueSize = (int) ReflectionSingleObjectSizer.roundUpSize(valueSize);
+    valueSize = roundUpSize(valueSize);
     int entrySize = keySize + valueSize
         + ((HeapLRUController) ((PartitionedRegion) 
region).getEvictionController())
             .getPerEntryOverhead();
@@ -117,7 +117,7 @@ public class EvictionObjectSizerDUnitTest extends 
CacheTestCase {
       int keySize = 0;
       int valueSize =
           JvmSizeUtils.getObjectHeaderSize() + 4 /* array length */ + (1024 * 
1024) /* bytes */;
-      valueSize = (int) ReflectionSingleObjectSizer.roundUpSize(valueSize);
+      valueSize = roundUpSize(valueSize);
       int entrySize = keySize + valueSize
           + ((HeapLRUController) ((PartitionedRegion) 
region).getEvictionController())
               .getPerEntryOverhead();
@@ -134,7 +134,7 @@ public class EvictionObjectSizerDUnitTest extends 
CacheTestCase {
       int keySize = 0;
       int valueSize = JvmSizeUtils.getObjectHeaderSize() + 4 /* array length */
           + (1024 * 1024 * 2) /* bytes */;
-      valueSize = (int) ReflectionSingleObjectSizer.roundUpSize(valueSize);
+      valueSize = roundUpSize(valueSize);
       int entrySize = keySize + valueSize
           + ((HeapLRUController) ((PartitionedRegion) 
region).getEvictionController())
               .getPerEntryOverhead();
@@ -158,7 +158,7 @@ public class EvictionObjectSizerDUnitTest extends 
CacheTestCase {
     {
       int keySize = 0;
       int valueSize = JvmSizeUtils.getObjectHeaderSize() + 4 /* array length 
*/ + 0 /* bytes */;
-      valueSize = (int) ReflectionSingleObjectSizer.roundUpSize(valueSize);
+      valueSize = roundUpSize(valueSize);
       int entrySize = keySize + valueSize
           + ((HeapLRUController) ((PartitionedRegion) 
region).getEvictionController())
               .getPerEntryOverhead();
@@ -173,7 +173,7 @@ public class EvictionObjectSizerDUnitTest extends 
CacheTestCase {
     {
       int keySize = 0;
       int valueSize = JvmSizeUtils.getObjectHeaderSize() + 4 /* array length 
*/ + 4 /* bytes */;
-      valueSize = (int) ReflectionSingleObjectSizer.roundUpSize(valueSize);
+      valueSize = roundUpSize(valueSize);
       int entrySize = keySize + valueSize
           + ((HeapLRUController) ((PartitionedRegion) 
region).getEvictionController())
               .getPerEntryOverhead();
diff --git 
a/geode-core/src/main/java/org/apache/geode/internal/JvmSizeUtils.java 
b/geode-core/src/main/java/org/apache/geode/internal/JvmSizeUtils.java
index 1d90d7a..260a8d6 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/JvmSizeUtils.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/JvmSizeUtils.java
@@ -15,6 +15,8 @@
 package org.apache.geode.internal;
 
 
+import static 
org.apache.geode.internal.size.ReflectionSingleObjectSizer.sizeof;
+
 import org.apache.commons.lang3.JavaVersion;
 
 import org.apache.geode.annotations.Immutable;
@@ -132,4 +134,59 @@ public class JvmSizeUtils {
   public static int getObjectHeaderSize() {
     return objectHeaderSize;
   }
+
+  public static long roundUpSize(long size) {
+    // Round up to the nearest 8 bytes. Experimentally, this
+    // is what we've seen the sun 32 bit VM do with object size.
+    // See https://wiki.gemstone.com/display/rusage/Per+Entry+Overhead
+    long remainder = size % 8;
+    if (remainder != 0) {
+      size += 8 - remainder;
+    }
+    return size;
+  }
+
+  public static int roundUpSize(int size) {
+    return (int) roundUpSize((long) size);
+  }
+
+  /**
+   * Returns the amount of memory used to store the given
+   * byte array. Note that this does include the
+   * memory used to store the bytes in the array.
+   */
+  public static int memoryOverhead(byte[] byteArray) {
+    if (byteArray == null) {
+      return 0;
+    }
+    int result = getObjectHeaderSize();
+    result += 4; // array length field
+    result += byteArray.length;
+    return roundUpSize(result);
+  }
+
+  /**
+   * Returns the amount of memory used to store the given
+   * object array. Note that this does not include the
+   * memory used by objects referenced by the array.
+   */
+  public static int memoryOverhead(Object[] objectArray) {
+    if (objectArray == null) {
+      return 0;
+    }
+    int result = getObjectHeaderSize();
+    result += 4; // array length field
+    result += objectArray.length * getReferenceSize();
+    return roundUpSize(result);
+  }
+
+  /**
+   * Returns the amount of memory used to store an instance
+   * of the given class. Note that this does not include
+   * memory used by objects referenced from fields of the
+   * instance.
+   */
+  public static int memoryOverhead(Class<?> clazz) {
+    return (int) sizeof(clazz);
+  }
 }
diff --git 
a/geode-core/src/main/java/org/apache/geode/internal/size/ReflectionSingleObjectSizer.java
 
b/geode-core/src/main/java/org/apache/geode/internal/size/ReflectionSingleObjectSizer.java
index f1602e0..c88d71c 100644
--- 
a/geode-core/src/main/java/org/apache/geode/internal/size/ReflectionSingleObjectSizer.java
+++ 
b/geode-core/src/main/java/org/apache/geode/internal/size/ReflectionSingleObjectSizer.java
@@ -14,6 +14,8 @@
  */
 package org.apache.geode.internal.size;
 
+import static org.apache.geode.internal.JvmSizeUtils.roundUpSize;
+
 import java.lang.reflect.Array;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
@@ -143,17 +145,6 @@ public class ReflectionSingleObjectSizer implements 
SingleObjectSizer {
     return size;
   }
 
-  public static long roundUpSize(long size) {
-    // Round up to the nearest 8 bytes. Experimentally, this
-    // is what we've seen the sun 32 bit VM do with object size.
-    // See https://wiki.gemstone.com/display/rusage/Per+Entry+Overhead
-    long remainder = size % 8;
-    if (remainder != 0) {
-      size = size - remainder + 8;
-    }
-    return size;
-  }
-
   private static int sizeType(Class<?> t) {
 
     if (t == Boolean.TYPE) {
diff --git 
a/geode-core/src/main/java/org/apache/geode/internal/size/WellKnownClassSizer.java
 
b/geode-core/src/main/java/org/apache/geode/internal/size/WellKnownClassSizer.java
index 96b3a97..513ab3c 100644
--- 
a/geode-core/src/main/java/org/apache/geode/internal/size/WellKnownClassSizer.java
+++ 
b/geode-core/src/main/java/org/apache/geode/internal/size/WellKnownClassSizer.java
@@ -15,6 +15,9 @@
 package org.apache.geode.internal.size;
 
 
+
+import static org.apache.geode.internal.JvmSizeUtils.roundUpSize;
+
 /**
  * An efficient sizer for some commonly used classes.
  *
@@ -38,7 +41,7 @@ public class WellKnownClassSizer {
   }
 
   public static int sizeof(Object o) {
-    int size = 0;
+    int size;
 
     if (o instanceof byte[]) {
       size = BYTE_ARRAY_OVERHEAD + ((byte[]) o).length;
@@ -48,8 +51,7 @@ public class WellKnownClassSizer {
       return 0;
     }
 
-    size = (int) ReflectionSingleObjectSizer.roundUpSize(size);
-    return size;
+    return roundUpSize(size);
   }
 
 }

Reply via email to