You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by kl...@apache.org on 2021/03/29 17:19:33 UTC

[geode] branch develop updated: GEODE-6143: Remove PowerMock from offheap tests (#6179)

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

klund 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 47c3b96  GEODE-6143: Remove PowerMock from offheap tests (#6179)
47c3b96 is described below

commit 47c3b96f46cd36a23724e47a13a7f352574dd773
Author: Kirk Lund <kl...@apache.org>
AuthorDate: Mon Mar 29 10:18:33 2021 -0700

    GEODE-6143: Remove PowerMock from offheap tests (#6179)
    
    * Clean-up offheap code and tests
    * Extract OffHeapClearRequired from OffHeapStoredObject
    * Extract ReferenceCounter from OffHeapStoredObject
    * Move remaining static methods to bottom of OffHeapStoredObject
    * Extract non-static delegate OffHeapRegionEntryHelperInstance from
      OffHeapRegionEntryHelper
    * Extract non-static delegate ReferenceCounterInstance from
      ReferenceCounter
    * Remove PowerMock from offheap tests
    * Remove PowerMock from Geode build files
    * Rename OffHeapRegionEntryHelperInstanceTest
    * Rename ReferenceCountHelperImplTest
---
 .../src/test/resources/expected-pom.xml            |   26 +-
 .../gradle/plugins/DependencyConstraints.groovy    |    6 -
 geode-core/build.gradle                            |    6 -
 .../internal/cache/AbstractBucketRegionQueue.java  |    6 +-
 .../geode/internal/cache/AbstractRegionMap.java    |    4 +-
 .../geode/internal/cache/BucketRegionQueue.java    |    6 +-
 .../cache/wan/serial/SerialGatewaySenderQueue.java |    6 +-
 .../geode/internal/offheap/FreeListManager.java    |    2 +-
 .../internal/offheap/OffHeapClearRequired.java     |   34 +
 .../internal/offheap/OffHeapRegionEntryHelper.java |  342 +----
 ....java => OffHeapRegionEntryHelperInstance.java} |  328 ++---
 .../internal/offheap/OffHeapStoredObject.java      |  178 +--
 .../internal/offheap/ReferenceCountHelper.java     |   64 +-
 .../internal/offheap/ReferenceCountHelperImpl.java |  173 +--
 .../geode/internal/offheap/ReferenceCounter.java   |   50 +
 .../internal/offheap/ReferenceCounterInstance.java |  118 ++
 .../concurrent/CustomEntryConcurrentHashMap.java   |    6 +-
 .../internal/offheap/FreeListManagerTest.java      |   60 +-
 .../OffHeapRegionEntryHelperInstanceTest.java      |  907 ++++++++++++++
 .../offheap/OffHeapRegionEntryHelperJUnitTest.java |  926 --------------
 .../offheap/ReferenceCountHelperImplTest.java      | 1320 ++++++++++----------
 .../offheap/ReferenceCountHelperJUnitTest.java     |  205 ---
 .../ParallelGatewaySenderOperationsDUnitTest.java  |    4 +-
 23 files changed, 2200 insertions(+), 2577 deletions(-)

diff --git a/boms/geode-all-bom/src/test/resources/expected-pom.xml b/boms/geode-all-bom/src/test/resources/expected-pom.xml
index 83987b1..6f650cc 100644
--- a/boms/geode-all-bom/src/test/resources/expected-pom.xml
+++ b/boms/geode-all-bom/src/test/resources/expected-pom.xml
@@ -698,24 +698,6 @@
         <scope>compile</scope>
       </dependency>
       <dependency>
-        <groupId>org.powermock</groupId>
-        <artifactId>powermock-api-mockito2</artifactId>
-        <version>2.0.9</version>
-        <scope>compile</scope>
-      </dependency>
-      <dependency>
-        <groupId>org.powermock</groupId>
-        <artifactId>powermock-core</artifactId>
-        <version>2.0.9</version>
-        <scope>compile</scope>
-      </dependency>
-      <dependency>
-        <groupId>org.powermock</groupId>
-        <artifactId>powermock-module-junit4</artifactId>
-        <version>2.0.9</version>
-        <scope>compile</scope>
-      </dependency>
-      <dependency>
         <groupId>org.seleniumhq.selenium</groupId>
         <artifactId>selenium-api</artifactId>
         <version>3.141.59</version>
@@ -825,25 +807,25 @@
       </dependency>
       <dependency>
         <groupId>org.springframework</groupId>
-        <artifactId>spring-tx</artifactId>
+        <artifactId>spring-test</artifactId>
         <version>5.3.5</version>
         <scope>compile</scope>
       </dependency>
       <dependency>
         <groupId>org.springframework</groupId>
-        <artifactId>spring-web</artifactId>
+        <artifactId>spring-tx</artifactId>
         <version>5.3.5</version>
         <scope>compile</scope>
       </dependency>
       <dependency>
         <groupId>org.springframework</groupId>
-        <artifactId>spring-webmvc</artifactId>
+        <artifactId>spring-web</artifactId>
         <version>5.3.5</version>
         <scope>compile</scope>
       </dependency>
       <dependency>
         <groupId>org.springframework</groupId>
-        <artifactId>spring-test</artifactId>
+        <artifactId>spring-webmvc</artifactId>
         <version>5.3.5</version>
         <scope>compile</scope>
       </dependency>
diff --git a/buildSrc/src/main/groovy/org/apache/geode/gradle/plugins/DependencyConstraints.groovy b/buildSrc/src/main/groovy/org/apache/geode/gradle/plugins/DependencyConstraints.groovy
index a059ef8..515e003 100644
--- a/buildSrc/src/main/groovy/org/apache/geode/gradle/plugins/DependencyConstraints.groovy
+++ b/buildSrc/src/main/groovy/org/apache/geode/gradle/plugins/DependencyConstraints.groovy
@@ -232,12 +232,6 @@ class DependencyConstraints implements Plugin<Project> {
       entry('hamcrest')
     }
 
-    dependencySet(group: 'org.powermock', version: '2.0.9') {
-      entry('powermock-api-mockito2')
-      entry('powermock-core')
-      entry('powermock-module-junit4')
-    }
-
     dependencySet(group: 'org.seleniumhq.selenium', version: '3.141.59') {
       entry('selenium-api')
       entry('selenium-chrome-driver')
diff --git a/geode-core/build.gradle b/geode-core/build.gradle
index 78380fe..5292150 100755
--- a/geode-core/build.gradle
+++ b/geode-core/build.gradle
@@ -333,9 +333,6 @@ dependencies {
     testImplementation('org.assertj:assertj-core')
     testImplementation('org.mockito:mockito-core')
     testImplementation('com.pholser:junit-quickcheck-core')
-    testImplementation('org.powermock:powermock-core')
-    testImplementation('org.powermock:powermock-module-junit4')
-    testImplementation('org.powermock:powermock-api-mockito2')
     testImplementation('pl.pragmatists:JUnitParams')
     testImplementation('com.tngtech.archunit:archunit-junit4')
     testImplementation(project(path: ':geode-core', configuration: 'raOutput'))
@@ -357,9 +354,6 @@ dependencies {
     integrationTestImplementation(project(':geode-concurrency-test'))
     integrationTestImplementation('org.apache.bcel:bcel')
     integrationTestImplementation('org.apache.logging.log4j:log4j-core')
-    integrationTestImplementation('org.powermock:powermock-core')
-    integrationTestImplementation('org.powermock:powermock-module-junit4')
-    integrationTestImplementation('org.powermock:powermock-api-mockito2')
     integrationTestImplementation('pl.pragmatists:JUnitParams')
     integrationTestImplementation('com.tngtech.archunit:archunit-junit4')
 
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/AbstractBucketRegionQueue.java b/geode-core/src/main/java/org/apache/geode/internal/cache/AbstractBucketRegionQueue.java
index 0813eab..bce5b07 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/AbstractBucketRegionQueue.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/AbstractBucketRegionQueue.java
@@ -39,7 +39,7 @@ import org.apache.geode.internal.cache.wan.AbstractGatewaySenderEventProcessor;
 import org.apache.geode.internal.cache.wan.GatewaySenderEventImpl;
 import org.apache.geode.internal.cache.wan.GatewaySenderStats;
 import org.apache.geode.internal.cache.wan.parallel.ConcurrentParallelGatewaySenderQueue;
-import org.apache.geode.internal.offheap.OffHeapRegionEntryHelper;
+import org.apache.geode.internal.offheap.OffHeapClearRequired;
 import org.apache.geode.internal.statistics.StatisticsClock;
 import org.apache.geode.logging.internal.log4j.api.LogService;
 import org.apache.geode.util.internal.GeodeGlossary;
@@ -401,7 +401,7 @@ public abstract class AbstractBucketRegionQueue extends BucketRegion {
 
   @Override
   public void closeEntries() {
-    OffHeapRegionEntryHelper.doWithOffHeapClear(new Runnable() {
+    OffHeapClearRequired.doWithOffHeapClear(new Runnable() {
       @Override
       public void run() {
         AbstractBucketRegionQueue.super.closeEntries();
@@ -414,7 +414,7 @@ public abstract class AbstractBucketRegionQueue extends BucketRegion {
   @Override
   public Set<VersionSource> clearEntries(final RegionVersionVector rvv) {
     final AtomicReference<Set<VersionSource>> result = new AtomicReference<Set<VersionSource>>();
-    OffHeapRegionEntryHelper.doWithOffHeapClear(new Runnable() {
+    OffHeapClearRequired.doWithOffHeapClear(new Runnable() {
       @Override
       public void run() {
         result.set(AbstractBucketRegionQueue.super.clearEntries(rvv));
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/AbstractRegionMap.java b/geode-core/src/main/java/org/apache/geode/internal/cache/AbstractRegionMap.java
index 9a0d8b6..1993b85 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/AbstractRegionMap.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/AbstractRegionMap.java
@@ -63,8 +63,8 @@ import org.apache.geode.internal.cache.versions.VersionStamp;
 import org.apache.geode.internal.cache.versions.VersionTag;
 import org.apache.geode.internal.cache.wan.GatewaySenderEventImpl;
 import org.apache.geode.internal.logging.log4j.LogMarker;
+import org.apache.geode.internal.offheap.OffHeapClearRequired;
 import org.apache.geode.internal.offheap.OffHeapHelper;
-import org.apache.geode.internal.offheap.OffHeapRegionEntryHelper;
 import org.apache.geode.internal.offheap.StoredObject;
 import org.apache.geode.internal.offheap.annotations.Released;
 import org.apache.geode.internal.offheap.annotations.Retained;
@@ -433,7 +433,7 @@ public abstract class AbstractRegionMap extends BaseRegionMap
               boolean tombstone = re.isTombstone();
               // note: it.remove() did not reliably remove the entry so we use remove(K,V) here
               if (getEntryMap().remove(re.getKey(), re)) {
-                if (OffHeapRegionEntryHelper.doesClearNeedToCheckForOffHeap()) {
+                if (OffHeapClearRequired.doesClearNeedToCheckForOffHeap()) {
                   GatewaySenderEventImpl.release(re.getValue()); // OFFHEAP _getValue ok
                 }
                 // If this is an overflow only region, we need to free the entry on
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/BucketRegionQueue.java b/geode-core/src/main/java/org/apache/geode/internal/cache/BucketRegionQueue.java
index a4e11fd..859de57 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/BucketRegionQueue.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/BucketRegionQueue.java
@@ -47,7 +47,7 @@ import org.apache.geode.internal.cache.wan.InternalGatewayQueueEvent;
 import org.apache.geode.internal.cache.wan.parallel.BucketRegionQueueUnavailableException;
 import org.apache.geode.internal.cache.wan.parallel.ConcurrentParallelGatewaySenderQueue;
 import org.apache.geode.internal.concurrent.Atomics;
-import org.apache.geode.internal.offheap.OffHeapRegionEntryHelper;
+import org.apache.geode.internal.offheap.OffHeapClearRequired;
 import org.apache.geode.internal.offheap.annotations.Released;
 import org.apache.geode.internal.statistics.StatisticsClock;
 import org.apache.geode.logging.internal.log4j.api.LogService;
@@ -209,7 +209,7 @@ public class BucketRegionQueue extends AbstractBucketRegionQueue {
 
   @Override
   public void closeEntries() {
-    OffHeapRegionEntryHelper.doWithOffHeapClear(new Runnable() {
+    OffHeapClearRequired.doWithOffHeapClear(new Runnable() {
       @Override
       public void run() {
         BucketRegionQueue.super.closeEntries();
@@ -222,7 +222,7 @@ public class BucketRegionQueue extends AbstractBucketRegionQueue {
   @Override
   public Set<VersionSource> clearEntries(final RegionVersionVector rvv) {
     final AtomicReference<Set<VersionSource>> result = new AtomicReference<Set<VersionSource>>();
-    OffHeapRegionEntryHelper.doWithOffHeapClear(new Runnable() {
+    OffHeapClearRequired.doWithOffHeapClear(new Runnable() {
       @Override
       public void run() {
         result.set(BucketRegionQueue.super.clearEntries(rvv));
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/wan/serial/SerialGatewaySenderQueue.java b/geode-core/src/main/java/org/apache/geode/internal/cache/wan/serial/SerialGatewaySenderQueue.java
index a50caca..89537e6 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/wan/serial/SerialGatewaySenderQueue.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/wan/serial/SerialGatewaySenderQueue.java
@@ -75,7 +75,7 @@ import org.apache.geode.internal.cache.wan.AbstractGatewaySender;
 import org.apache.geode.internal.cache.wan.GatewaySenderEventImpl;
 import org.apache.geode.internal.cache.wan.GatewaySenderStats;
 import org.apache.geode.internal.cache.wan.InternalGatewayQueueEvent;
-import org.apache.geode.internal.offheap.OffHeapRegionEntryHelper;
+import org.apache.geode.internal.offheap.OffHeapClearRequired;
 import org.apache.geode.internal.statistics.StatisticsClock;
 import org.apache.geode.logging.internal.log4j.api.LogService;
 import org.apache.geode.management.ManagementService;
@@ -1385,7 +1385,7 @@ public class SerialGatewaySenderQueue implements RegionQueue {
 
     @Override
     public void closeEntries() {
-      OffHeapRegionEntryHelper.doWithOffHeapClear(new Runnable() {
+      OffHeapClearRequired.doWithOffHeapClear(new Runnable() {
         @Override
         public void run() {
           SerialGatewaySenderQueueMetaRegion.super.closeEntries();
@@ -1396,7 +1396,7 @@ public class SerialGatewaySenderQueue implements RegionQueue {
     @Override
     public Set<VersionSource> clearEntries(final RegionVersionVector rvv) {
       final AtomicReference<Set<VersionSource>> result = new AtomicReference<Set<VersionSource>>();
-      OffHeapRegionEntryHelper.doWithOffHeapClear(new Runnable() {
+      OffHeapClearRequired.doWithOffHeapClear(new Runnable() {
         @Override
         public void run() {
           result.set(SerialGatewaySenderQueueMetaRegion.super.clearEntries(rvv));
diff --git a/geode-core/src/main/java/org/apache/geode/internal/offheap/FreeListManager.java b/geode-core/src/main/java/org/apache/geode/internal/offheap/FreeListManager.java
index 9e1f445..21b14d3 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/offheap/FreeListManager.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/offheap/FreeListManager.java
@@ -75,7 +75,7 @@ public class FreeListManager {
         addr = f.getAddress() + f.getSize();
       } else {
         int curChunkSize = OffHeapStoredObject.getSize(addr);
-        int refCount = OffHeapStoredObject.getRefCount(addr);
+        int refCount = ReferenceCounter.getRefCount(addr);
         if (refCount > 0) {
           result.add(new OffHeapStoredObject(addr));
         }
diff --git a/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapClearRequired.java b/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapClearRequired.java
new file mode 100644
index 0000000..b6d8c4d
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapClearRequired.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.offheap;
+
+public class OffHeapClearRequired {
+
+  private static final ThreadLocal<Object> clearNeedsToCheckForOffHeap =
+      new ThreadLocal<>();
+
+  public static boolean doesClearNeedToCheckForOffHeap() {
+    return clearNeedsToCheckForOffHeap.get() != null;
+  }
+
+  public static void doWithOffHeapClear(Runnable r) {
+    clearNeedsToCheckForOffHeap.set(Boolean.TRUE);
+    try {
+      r.run();
+    } finally {
+      clearNeedsToCheckForOffHeap.remove();
+    }
+  }
+}
diff --git a/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapRegionEntryHelper.java b/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapRegionEntryHelper.java
index 0d10311..01f68f6 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapRegionEntryHelper.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapRegionEntryHelper.java
@@ -15,17 +15,12 @@
 package org.apache.geode.internal.offheap;
 
 import org.apache.geode.annotations.Immutable;
-import org.apache.geode.internal.cache.CachedDeserializableFactory;
-import org.apache.geode.internal.cache.DiskId;
-import org.apache.geode.internal.cache.EntryEventImpl;
 import org.apache.geode.internal.cache.RegionEntryContext;
 import org.apache.geode.internal.cache.Token;
-import org.apache.geode.internal.cache.entries.DiskEntry;
 import org.apache.geode.internal.cache.entries.OffHeapRegionEntry;
 import org.apache.geode.internal.offheap.annotations.Released;
 import org.apache.geode.internal.offheap.annotations.Retained;
 import org.apache.geode.internal.offheap.annotations.Unretained;
-import org.apache.geode.internal.serialization.DSCODE;
 
 /**
  * The class just has static methods that operate on instances of {@link OffHeapRegionEntry}. It
@@ -36,44 +31,18 @@ import org.apache.geode.internal.serialization.DSCODE;
  */
 public class OffHeapRegionEntryHelper {
 
-  protected static final long NULL_ADDRESS = 0L << 1;
-  protected static final long INVALID_ADDRESS = 1L << 1;
-  protected static final long LOCAL_INVALID_ADDRESS = 2L << 1;
-  protected static final long DESTROYED_ADDRESS = 3L << 1;
-  protected static final long REMOVED_PHASE1_ADDRESS = 4L << 1;
-  protected static final long REMOVED_PHASE2_ADDRESS = 5L << 1;
-  protected static final long END_OF_STREAM_ADDRESS = 6L << 1;
-  protected static final long NOT_AVAILABLE_ADDRESS = 7L << 1;
-  protected static final long TOMBSTONE_ADDRESS = 8L << 1;
-  public static final int MAX_LENGTH_FOR_DATA_AS_ADDRESS = 8;
-
   @Immutable
-  private static final Token[] addrToObj =
-      new Token[] {null, Token.INVALID, Token.LOCAL_INVALID, Token.DESTROYED, Token.REMOVED_PHASE1,
-          Token.REMOVED_PHASE2, Token.END_OF_STREAM, Token.NOT_AVAILABLE, Token.TOMBSTONE,};
+  private static final OffHeapRegionEntryHelper INSTANCE = new OffHeapRegionEntryHelper(
+      new OffHeapRegionEntryHelperInstance());
+
+  private final OffHeapRegionEntryHelperInstance delegate;
+
+  private OffHeapRegionEntryHelper(OffHeapRegionEntryHelperInstance delegate) {
+    this.delegate = delegate;
+  }
 
-  private static long objectToAddress(@Unretained Object v) {
-    if (v instanceof StoredObject)
-      return ((StoredObject) v).getAddress();
-    if (v == null)
-      return NULL_ADDRESS;
-    if (v == Token.TOMBSTONE)
-      return TOMBSTONE_ADDRESS;
-    if (v == Token.INVALID)
-      return INVALID_ADDRESS;
-    if (v == Token.LOCAL_INVALID)
-      return LOCAL_INVALID_ADDRESS;
-    if (v == Token.DESTROYED)
-      return DESTROYED_ADDRESS;
-    if (v == Token.REMOVED_PHASE1)
-      return REMOVED_PHASE1_ADDRESS;
-    if (v == Token.REMOVED_PHASE2)
-      return REMOVED_PHASE2_ADDRESS;
-    if (v == Token.END_OF_STREAM)
-      return END_OF_STREAM_ADDRESS;
-    if (v == Token.NOT_AVAILABLE)
-      return NOT_AVAILABLE_ADDRESS;
-    throw new IllegalStateException("Can not convert " + v + " to an off heap address.");
+  private static OffHeapRegionEntryHelperInstance delegate() {
+    return INSTANCE.delegate;
   }
 
   /**
@@ -89,216 +58,48 @@ public class OffHeapRegionEntryHelper {
   @Retained
   public static Object addressToObject(@Released @Retained long ohAddress, boolean decompress,
       RegionEntryContext context) {
-    if (isOffHeap(ohAddress)) {
-      @Unretained
-      OffHeapStoredObject chunk = new OffHeapStoredObject(ohAddress);
-      @Unretained
-      Object result = chunk;
-      if (decompress && chunk.isCompressed()) {
-        try {
-          // to fix bug 47982 need to:
-          byte[] decompressedBytes = chunk.getDecompressedBytes(context);
-          if (chunk.isSerialized()) {
-            // return a VMCachedDeserializable with the decompressed serialized bytes since chunk is
-            // serialized
-            result = CachedDeserializableFactory.create(decompressedBytes, context.getCache());
-          } else {
-            // return a byte[] since chunk is not serialized
-            result = decompressedBytes;
-          }
-        } finally {
-          // decompress is only true when this method is called by _getValueRetain.
-          // In that case the caller has already retained ohAddress because it thought
-          // we would return it. But we have unwrapped it and are returning the decompressed
-          // results.
-          // So we need to release the chunk here.
-          chunk.release();
-        }
-      }
-      return result;
-    } else if ((ohAddress & ENCODED_BIT) != 0) {
-      TinyStoredObject daa = new TinyStoredObject(ohAddress);
-      Object result = daa;
-      if (decompress && daa.isCompressed()) {
-        byte[] decompressedBytes = daa.getDecompressedBytes(context);
-        if (daa.isSerialized()) {
-          // return a VMCachedDeserializable with the decompressed serialized bytes since daa is
-          // serialized
-          result = CachedDeserializableFactory.create(decompressedBytes, context.getCache());
-        } else {
-          // return a byte[] since daa is not serialized
-          result = decompressedBytes;
-        }
-      }
-      return result;
-    } else {
-      return addrToObj[(int) ohAddress >> 1];
-    }
+    return delegate().addressToObject(ohAddress, decompress, context);
   }
 
   public static int getSerializedLength(TinyStoredObject dataAsAddress) {
-    final long ohAddress = dataAsAddress.getAddress();
-
-    if ((ohAddress & ENCODED_BIT) != 0) {
-      boolean isLong = (ohAddress & LONG_BIT) != 0;
-      if (isLong) {
-        return 9;
-      } else {
-        return (int) ((ohAddress & SIZE_MASK) >> SIZE_SHIFT);
-      }
-    } else {
-      return 0;
-    }
-  }
-
-  /*
-   * This method is optimized for cases where if the caller wants to convert address to a Token
-   * compared to addressToObject which would deserialize the value.
-   */
-  private static Token addressToToken(long ohAddress) {
-    if (isOffHeap(ohAddress) || (ohAddress & ENCODED_BIT) != 0) {
-      return Token.NOT_A_TOKEN;
-    } else {
-      return addrToObj[(int) ohAddress >> 1];
-    }
-  }
-
-  private static void releaseAddress(@Released long ohAddress) {
-    if (isOffHeap(ohAddress)) {
-      OffHeapStoredObject.release(ohAddress);
-    }
+    return delegate().getSerializedLength(dataAsAddress);
   }
 
   /**
    * The address in 're' will be @Released.
    */
   public static void releaseEntry(@Released OffHeapRegionEntry re) {
-    if (re instanceof DiskEntry) {
-      DiskId did = ((DiskEntry) re).getDiskId();
-      if (did != null && did.isPendingAsync()) {
-        synchronized (did) {
-          // This may not be needed so remove this call if it causes problems.
-          // We no longer need this entry to be written to disk so unschedule it
-          // before we change its value to REMOVED_PHASE2.
-          did.setPendingAsync(false);
-          setValue(re, Token.REMOVED_PHASE2);
-          return;
-        }
-      }
-    }
-    setValue(re, Token.REMOVED_PHASE2);
+    delegate().releaseEntry(re);
   }
 
   public static void releaseEntry(@Unretained OffHeapRegionEntry re,
       @Released StoredObject expectedValue) {
-    long oldAddress = objectToAddress(expectedValue);
-    final long newAddress = objectToAddress(Token.REMOVED_PHASE2);
-    if (re.setAddress(oldAddress, newAddress)) {
-      releaseAddress(oldAddress);
-    } /*
-       * else { if (!calledSetValue || re.getAddress() != newAddress) { expectedValue.release(); } }
-       */
+    delegate().releaseEntry(re, expectedValue);
   }
 
   /**
-   * This bit is set to indicate that this address has data encoded in it.
-   */
-  private static final long ENCODED_BIT = 1L;
-  /**
-   * This bit is set to indicate that the encoded data is serialized.
-   */
-  static final long SERIALIZED_BIT = 2L;
-  /**
-   * This bit is set to indicate that the encoded data is compressed.
-   */
-  static final long COMPRESSED_BIT = 4L;
-  /**
-   * This bit is set to indicate that the encoded data is a long whose value fits in 7 bytes.
-   */
-  private static final long LONG_BIT = 8L;
-  /**
-   * size is in the range 0..7 so we only need 3 bits.
-   */
-  private static final long SIZE_MASK = 0x70L;
-  /**
-   * number of bits to shift the size by.
-   */
-  private static final int SIZE_SHIFT = 4;
-  // the msb of this byte is currently unused
-
-  /**
    * Returns 0 if the data could not be encoded as an address.
    */
-  public static long encodeDataAsAddress(byte[] v, boolean isSerialized, boolean isCompressed) {
-    if (v.length < MAX_LENGTH_FOR_DATA_AS_ADDRESS) {
-      long result = 0L;
-      for (int i = 0; i < v.length; i++) {
-        result |= v[i] & 0x00ff;
-        result <<= 8;
-      }
-      result |= (v.length << SIZE_SHIFT) | ENCODED_BIT;
-      if (isSerialized) {
-        result |= SERIALIZED_BIT;
-      }
-      if (isCompressed) {
-        result |= COMPRESSED_BIT;
-      }
-      return result;
-    } else if (isSerialized && !isCompressed) {
-      // Check for some special types that take more than 7 bytes to serialize
-      // but that might be able to be inlined with less than 8 bytes.
-      if (v[0] == DSCODE.LONG.toByte()) {
-        // A long is currently always serialized as 8 bytes (9 if you include the dscode).
-        // But many long values will actually be small enough for is to encode in 7 bytes.
-        if ((v[1] == 0 && (v[2] & 0x80) == 0) || (v[1] == -1 && (v[2] & 0x80) != 0)) {
-          // The long can be encoded as 7 bytes since the most signification byte
-          // is simply an extension of the sign byte on the second most signification byte.
-          long result = 0L;
-          for (int i = 2; i < v.length; i++) {
-            result |= v[i] & 0x00ff;
-            result <<= 8;
-          }
-          result |= (7 << SIZE_SHIFT) | LONG_BIT | SERIALIZED_BIT | ENCODED_BIT;
-          return result;
-        }
-      }
-    }
-    return 0L;
+  static long encodeDataAsAddress(byte[] v, boolean isSerialized, boolean isCompressed) {
+    return delegate().encodeDataAsAddress(v, isSerialized, isCompressed);
   }
 
   static Object decodeAddressToObject(long ohAddress) {
-    byte[] bytes = decodeUncompressedAddressToBytes(ohAddress);
-
-    boolean isSerialized = (ohAddress & SERIALIZED_BIT) != 0;
-    if (isSerialized) {
-      return EntryEventImpl.deserialize(bytes);
-    } else {
-      return bytes;
-    }
+    return delegate().decodeAddressToObject(ohAddress);
   }
 
   static int decodeAddressToDataSize(long addr) {
-    if ((addr & ENCODED_BIT) == 0) {
-      throw new AssertionError("Invalid address: " + addr);
-    }
-    boolean isLong = (addr & LONG_BIT) != 0;
-    if (isLong) {
-      return 9;
-    }
-    return (int) ((addr & SIZE_MASK) >> SIZE_SHIFT);
+    return delegate().decodeAddressToDataSize(addr);
   }
 
   /**
    * Returns the bytes encoded in the given address. Note that compressed addresses are not
    * supported by this method.
    *
-   * @throws UnsupportedOperationException if the address has compressed data
+   * @throws AssertionError if the address has compressed data
    */
   static byte[] decodeUncompressedAddressToBytes(long addr) {
-    if ((addr & COMPRESSED_BIT) != 0) {
-      throw new AssertionError("Did not expect encoded address to be compressed");
-    }
-    return decodeAddressToRawBytes(addr);
+    return delegate().decodeUncompressedAddressToBytes(addr);
   }
 
   /**
@@ -306,32 +107,7 @@ public class OffHeapRegionEntryHelper {
    * compressed then the raw bytes are the compressed bytes.
    */
   static byte[] decodeAddressToRawBytes(long addr) {
-    if ((addr & ENCODED_BIT) == 0) {
-      throw new AssertionError("Invalid address: " + addr);
-    }
-    int size = (int) ((addr & SIZE_MASK) >> SIZE_SHIFT);
-    boolean isLong = (addr & LONG_BIT) != 0;
-    byte[] bytes;
-    if (isLong) {
-      bytes = new byte[9];
-      bytes[0] = DSCODE.LONG.toByte();
-      for (int i = 8; i >= 2; i--) {
-        addr >>= 8;
-        bytes[i] = (byte) (addr & 0x00ff);
-      }
-      if ((bytes[2] & 0x80) != 0) {
-        bytes[1] = -1;
-      } else {
-        bytes[1] = 0;
-      }
-    } else {
-      bytes = new byte[size];
-      for (int i = size - 1; i >= 0; i--) {
-        addr >>= 8;
-        bytes[i] = (byte) (addr & 0x00ff);
-      }
-    }
-    return bytes;
+    return delegate().decodeAddressToRawBytes(addr);
   }
 
   /**
@@ -339,35 +115,20 @@ public class OffHeapRegionEntryHelper {
    * be set to the @Unretained address of 'v'.
    */
   public static void setValue(@Released OffHeapRegionEntry re, @Unretained Object v) {
-    // setValue is called when synced so I don't need to worry
-    // about oldAddress being released by someone else.
-    final long newAddress = objectToAddress(v);
-    long oldAddress;
-    do {
-      oldAddress = re.getAddress();
-    } while (!re.setAddress(oldAddress, newAddress));
-    ReferenceCountHelper.setReferenceCountOwner(re);
-    releaseAddress(oldAddress);
-    ReferenceCountHelper.setReferenceCountOwner(null);
+    delegate().setValue(re, v);
   }
 
   public static Token getValueAsToken(@Unretained OffHeapRegionEntry re) {
-    return addressToToken(re.getAddress());
+    return delegate().getValueAsToken(re);
   }
 
   @Unretained
   public static Object _getValue(@Unretained OffHeapRegionEntry re) {
-    return addressToObject(re.getAddress(), false, null); // no context needed so decompress is
-                                                          // false
+    return delegate()._getValue(re);
   }
 
   public static boolean isOffHeap(long addr) {
-    if ((addr & ENCODED_BIT) != 0)
-      return false;
-    if (addr < 0)
-      return true;
-    addr >>= 1; // shift right 1 to convert to array index;
-    return addr >= addrToObj.length;
+    return delegate().isOffHeap(addr);
   }
 
   /**
@@ -381,61 +142,14 @@ public class OffHeapRegionEntryHelper {
   @Retained
   public static Object _getValueRetain(@Retained @Unretained OffHeapRegionEntry re,
       boolean decompress, RegionEntryContext context) {
-    int retryCount = 0;
-    @Retained
-    long addr = re.getAddress();
-    while (isOffHeap(addr)) {
-      if (OffHeapStoredObject.retain(addr)) {
-        @Unretained
-        long addr2 = re.getAddress();
-        if (addr != addr2) {
-          retryCount = 0;
-          OffHeapStoredObject.release(addr);
-          // spin around and try again.
-          addr = addr2;
-        } else {
-          return addressToObject(addr, decompress, context);
-        }
-      } else {
-        // spin around and try again
-        long addr2 = re.getAddress();
-        retryCount++;
-        if (retryCount > 100) {
-          throw new IllegalStateException("retain failed addr=" + addr + " addr2=" + addr
-              + " 100 times" + " history=" + ReferenceCountHelper.getFreeRefCountInfo(addr));
-        }
-        addr = addr2;
-        // Since retain returned false our region entry should have a different
-        // value in it. However the actual address could be the exact same one
-        // because addr was released, then reallocated from the free list and set
-        // back into this region entry. See bug 47782
-      }
-    }
-    return addressToObject(addr, decompress, context);
+    return delegate()._getValueRetain(re, decompress, context);
   }
 
-
-
   public static boolean isSerialized(long address) {
-    return (address & SERIALIZED_BIT) != 0;
+    return delegate().isSerialized(address);
   }
 
   public static boolean isCompressed(long address) {
-    return (address & COMPRESSED_BIT) != 0;
-  }
-
-  private static final ThreadLocal<Object> clearNeedsToCheckForOffHeap = new ThreadLocal<Object>();
-
-  public static boolean doesClearNeedToCheckForOffHeap() {
-    return clearNeedsToCheckForOffHeap.get() != null;
-  }
-
-  public static void doWithOffHeapClear(Runnable r) {
-    clearNeedsToCheckForOffHeap.set(Boolean.TRUE);
-    try {
-      r.run();
-    } finally {
-      clearNeedsToCheckForOffHeap.remove();
-    }
+    return delegate().isCompressed(address);
   }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapRegionEntryHelper.java b/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapRegionEntryHelperInstance.java
similarity index 54%
copy from geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapRegionEntryHelper.java
copy to geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapRegionEntryHelperInstance.java
index 0d10311..26ddfde 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapRegionEntryHelper.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapRegionEntryHelperInstance.java
@@ -14,7 +14,10 @@
  */
 package org.apache.geode.internal.offheap;
 
+import java.util.function.Function;
+
 import org.apache.geode.annotations.Immutable;
+import org.apache.geode.annotations.VisibleForTesting;
 import org.apache.geode.internal.cache.CachedDeserializableFactory;
 import org.apache.geode.internal.cache.DiskId;
 import org.apache.geode.internal.cache.EntryEventImpl;
@@ -34,46 +37,31 @@ import org.apache.geode.internal.serialization.DSCODE;
  *
  * @since Geode 1.0
  */
-public class OffHeapRegionEntryHelper {
-
-  protected static final long NULL_ADDRESS = 0L << 1;
-  protected static final long INVALID_ADDRESS = 1L << 1;
-  protected static final long LOCAL_INVALID_ADDRESS = 2L << 1;
-  protected static final long DESTROYED_ADDRESS = 3L << 1;
-  protected static final long REMOVED_PHASE1_ADDRESS = 4L << 1;
-  protected static final long REMOVED_PHASE2_ADDRESS = 5L << 1;
-  protected static final long END_OF_STREAM_ADDRESS = 6L << 1;
-  protected static final long NOT_AVAILABLE_ADDRESS = 7L << 1;
-  protected static final long TOMBSTONE_ADDRESS = 8L << 1;
-  public static final int MAX_LENGTH_FOR_DATA_AS_ADDRESS = 8;
-
-  @Immutable
-  private static final Token[] addrToObj =
-      new Token[] {null, Token.INVALID, Token.LOCAL_INVALID, Token.DESTROYED, Token.REMOVED_PHASE1,
-          Token.REMOVED_PHASE2, Token.END_OF_STREAM, Token.NOT_AVAILABLE, Token.TOMBSTONE,};
-
-  private static long objectToAddress(@Unretained Object v) {
-    if (v instanceof StoredObject)
-      return ((StoredObject) v).getAddress();
-    if (v == null)
-      return NULL_ADDRESS;
-    if (v == Token.TOMBSTONE)
-      return TOMBSTONE_ADDRESS;
-    if (v == Token.INVALID)
-      return INVALID_ADDRESS;
-    if (v == Token.LOCAL_INVALID)
-      return LOCAL_INVALID_ADDRESS;
-    if (v == Token.DESTROYED)
-      return DESTROYED_ADDRESS;
-    if (v == Token.REMOVED_PHASE1)
-      return REMOVED_PHASE1_ADDRESS;
-    if (v == Token.REMOVED_PHASE2)
-      return REMOVED_PHASE2_ADDRESS;
-    if (v == Token.END_OF_STREAM)
-      return END_OF_STREAM_ADDRESS;
-    if (v == Token.NOT_AVAILABLE)
-      return NOT_AVAILABLE_ADDRESS;
-    throw new IllegalStateException("Can not convert " + v + " to an off heap address.");
+class OffHeapRegionEntryHelperInstance {
+
+  static final long NULL_ADDRESS = 0L;
+  static final long INVALID_ADDRESS = 1L << 1;
+  static final long LOCAL_INVALID_ADDRESS = 2L << 1;
+  static final long DESTROYED_ADDRESS = 3L << 1;
+  static final long REMOVED_PHASE1_ADDRESS = 4L << 1;
+  static final long REMOVED_PHASE2_ADDRESS = 5L << 1;
+  static final long END_OF_STREAM_ADDRESS = 6L << 1;
+  static final long NOT_AVAILABLE_ADDRESS = 7L << 1;
+  static final long TOMBSTONE_ADDRESS = 8L << 1;
+  static final int MAX_LENGTH_FOR_DATA_AS_ADDRESS = 8;
+
+  private final Function<Long, OffHeapStoredObject> offHeapStoredObjectFactory;
+  private final ReferenceCounterInstance referenceCounter;
+
+  OffHeapRegionEntryHelperInstance() {
+    this(OffHeapStoredObject::new, new ReferenceCounterInstance());
+  }
+
+  @VisibleForTesting
+  OffHeapRegionEntryHelperInstance(Function<Long, OffHeapStoredObject> offHeapStoredObjectFactory,
+      ReferenceCounterInstance referenceCounter) {
+    this.offHeapStoredObjectFactory = offHeapStoredObjectFactory;
+    this.referenceCounter = referenceCounter;
   }
 
   /**
@@ -87,11 +75,11 @@ public class OffHeapRegionEntryHelper {
    */
   @Unretained
   @Retained
-  public static Object addressToObject(@Released @Retained long ohAddress, boolean decompress,
+  Object addressToObject(@Released @Retained long ohAddress, boolean decompress,
       RegionEntryContext context) {
     if (isOffHeap(ohAddress)) {
       @Unretained
-      OffHeapStoredObject chunk = new OffHeapStoredObject(ohAddress);
+      OffHeapStoredObject chunk = offHeapStoredObjectFactory.apply(ohAddress);
       @Unretained
       Object result = chunk;
       if (decompress && chunk.isCompressed()) {
@@ -107,16 +95,17 @@ public class OffHeapRegionEntryHelper {
             result = decompressedBytes;
           }
         } finally {
-          // decompress is only true when this method is called by _getValueRetain.
-          // In that case the caller has already retained ohAddress because it thought
-          // we would return it. But we have unwrapped it and are returning the decompressed
-          // results.
-          // So we need to release the chunk here.
+          // Decompress is only true when this method is called by _getValueRetain. In that case the
+          // caller has already retained ohAddress because it thought we would return it. But we
+          // have unwrapped it and are returning the decompressed results. So we need to release the
+          // chunk here.
           chunk.release();
         }
       }
       return result;
-    } else if ((ohAddress & ENCODED_BIT) != 0) {
+    }
+
+    if ((ohAddress & ENCODED_BIT) != 0) {
       TinyStoredObject daa = new TinyStoredObject(ohAddress);
       Object result = daa;
       if (decompress && daa.isCompressed()) {
@@ -131,73 +120,71 @@ public class OffHeapRegionEntryHelper {
         }
       }
       return result;
-    } else {
-      return addrToObj[(int) ohAddress >> 1];
     }
+
+    return TokenAddress.ADDRESS_TO_OBJECT[(int) ohAddress >> 1];
   }
 
-  public static int getSerializedLength(TinyStoredObject dataAsAddress) {
+  int getSerializedLength(TinyStoredObject dataAsAddress) {
     final long ohAddress = dataAsAddress.getAddress();
 
     if ((ohAddress & ENCODED_BIT) != 0) {
       boolean isLong = (ohAddress & LONG_BIT) != 0;
       if (isLong) {
         return 9;
-      } else {
-        return (int) ((ohAddress & SIZE_MASK) >> SIZE_SHIFT);
       }
-    } else {
-      return 0;
+      return (int) ((ohAddress & SIZE_MASK) >> SIZE_SHIFT);
     }
+
+    return 0;
   }
 
   /*
    * This method is optimized for cases where if the caller wants to convert address to a Token
    * compared to addressToObject which would deserialize the value.
    */
-  private static Token addressToToken(long ohAddress) {
+  private Token addressToToken(long ohAddress) {
     if (isOffHeap(ohAddress) || (ohAddress & ENCODED_BIT) != 0) {
       return Token.NOT_A_TOKEN;
-    } else {
-      return addrToObj[(int) ohAddress >> 1];
     }
+
+    return TokenAddress.ADDRESS_TO_OBJECT[(int) ohAddress >> 1];
   }
 
-  private static void releaseAddress(@Released long ohAddress) {
+  private void releaseAddress(@Released long ohAddress) {
     if (isOffHeap(ohAddress)) {
-      OffHeapStoredObject.release(ohAddress);
+      referenceCounter.release(ohAddress);
     }
   }
 
   /**
-   * The address in 're' will be @Released.
+   * The address in 'regionEntry' will be @Released.
    */
-  public static void releaseEntry(@Released OffHeapRegionEntry re) {
-    if (re instanceof DiskEntry) {
-      DiskId did = ((DiskEntry) re).getDiskId();
-      if (did != null && did.isPendingAsync()) {
-        synchronized (did) {
+  public void releaseEntry(@Released OffHeapRegionEntry regionEntry) {
+    if (regionEntry instanceof DiskEntry) {
+      DiskId diskId = ((DiskEntry) regionEntry).getDiskId();
+      if (diskId != null && diskId.isPendingAsync()) {
+        synchronized (diskId) {
           // This may not be needed so remove this call if it causes problems.
           // We no longer need this entry to be written to disk so unschedule it
           // before we change its value to REMOVED_PHASE2.
-          did.setPendingAsync(false);
-          setValue(re, Token.REMOVED_PHASE2);
+          diskId.setPendingAsync(false);
+          setValue(regionEntry, Token.REMOVED_PHASE2);
           return;
         }
       }
     }
-    setValue(re, Token.REMOVED_PHASE2);
+
+    setValue(regionEntry, Token.REMOVED_PHASE2);
   }
 
-  public static void releaseEntry(@Unretained OffHeapRegionEntry re,
+  public void releaseEntry(@Unretained OffHeapRegionEntry regionEntry,
       @Released StoredObject expectedValue) {
-    long oldAddress = objectToAddress(expectedValue);
-    final long newAddress = objectToAddress(Token.REMOVED_PHASE2);
-    if (re.setAddress(oldAddress, newAddress)) {
+    long oldAddress = TokenAddress.objectToAddress(expectedValue);
+    final long newAddress = TokenAddress.objectToAddress(Token.REMOVED_PHASE2);
+    if (regionEntry.setAddress(oldAddress, newAddress)) {
       releaseAddress(oldAddress);
-    } /*
-       * else { if (!calledSetValue || re.getAddress() != newAddress) { expectedValue.release(); } }
-       */
+    }
   }
 
   /**
@@ -229,14 +216,14 @@ public class OffHeapRegionEntryHelper {
   /**
    * Returns 0 if the data could not be encoded as an address.
    */
-  public static long encodeDataAsAddress(byte[] v, boolean isSerialized, boolean isCompressed) {
-    if (v.length < MAX_LENGTH_FOR_DATA_AS_ADDRESS) {
+  long encodeDataAsAddress(byte[] bytes, boolean isSerialized, boolean isCompressed) {
+    if (bytes.length < MAX_LENGTH_FOR_DATA_AS_ADDRESS) {
       long result = 0L;
-      for (int i = 0; i < v.length; i++) {
-        result |= v[i] & 0x00ff;
+      for (byte aByte : bytes) {
+        result |= aByte & 0x00ff;
         result <<= 8;
       }
-      result |= (v.length << SIZE_SHIFT) | ENCODED_BIT;
+      result |= bytes.length << SIZE_SHIFT | ENCODED_BIT;
       if (isSerialized) {
         result |= SERIALIZED_BIT;
       }
@@ -244,57 +231,60 @@ public class OffHeapRegionEntryHelper {
         result |= COMPRESSED_BIT;
       }
       return result;
-    } else if (isSerialized && !isCompressed) {
-      // Check for some special types that take more than 7 bytes to serialize
-      // but that might be able to be inlined with less than 8 bytes.
-      if (v[0] == DSCODE.LONG.toByte()) {
-        // A long is currently always serialized as 8 bytes (9 if you include the dscode).
-        // But many long values will actually be small enough for is to encode in 7 bytes.
-        if ((v[1] == 0 && (v[2] & 0x80) == 0) || (v[1] == -1 && (v[2] & 0x80) != 0)) {
-          // The long can be encoded as 7 bytes since the most signification byte
-          // is simply an extension of the sign byte on the second most signification byte.
+    }
+
+    if (isSerialized && !isCompressed) {
+      // Check for some special types that take more than 7 bytes to serialize but that might be
+      // able to be inlined with less than 8 bytes.
+      if (bytes[0] == DSCODE.LONG.toByte()) {
+        // A long is currently always serialized as 8 bytes (9 if you include the dscode). But many
+        // long values will actually be small enough for is to encode in 7 bytes.
+        if (bytes[1] == 0 && (bytes[2] & 0x80) == 0 || bytes[1] == -1 && (bytes[2] & 0x80) != 0) {
+          // The long can be encoded as 7 bytes since the most signification byte is simply an
+          // extension of the sign byte on the second most signification byte.
           long result = 0L;
-          for (int i = 2; i < v.length; i++) {
-            result |= v[i] & 0x00ff;
+          for (int i = 2; i < bytes.length; i++) {
+            result |= bytes[i] & 0x00ff;
             result <<= 8;
           }
-          result |= (7 << SIZE_SHIFT) | LONG_BIT | SERIALIZED_BIT | ENCODED_BIT;
+          result |= 7 << SIZE_SHIFT | LONG_BIT | SERIALIZED_BIT | ENCODED_BIT;
           return result;
         }
       }
     }
+
     return 0L;
   }
 
-  static Object decodeAddressToObject(long ohAddress) {
+  Object decodeAddressToObject(long ohAddress) {
     byte[] bytes = decodeUncompressedAddressToBytes(ohAddress);
 
     boolean isSerialized = (ohAddress & SERIALIZED_BIT) != 0;
     if (isSerialized) {
       return EntryEventImpl.deserialize(bytes);
-    } else {
-      return bytes;
     }
+
+    return bytes;
   }
 
-  static int decodeAddressToDataSize(long addr) {
-    if ((addr & ENCODED_BIT) == 0) {
-      throw new AssertionError("Invalid address: " + addr);
+  int decodeAddressToDataSize(long address) {
+    if ((address & ENCODED_BIT) == 0) {
+      throw new AssertionError("Invalid address: " + address);
     }
-    boolean isLong = (addr & LONG_BIT) != 0;
+    boolean isLong = (address & LONG_BIT) != 0;
     if (isLong) {
       return 9;
     }
-    return (int) ((addr & SIZE_MASK) >> SIZE_SHIFT);
+    return (int) ((address & SIZE_MASK) >> SIZE_SHIFT);
   }
 
   /**
    * Returns the bytes encoded in the given address. Note that compressed addresses are not
    * supported by this method.
    *
-   * @throws UnsupportedOperationException if the address has compressed data
+   * @throws AssertionError if the address has compressed data
    */
-  static byte[] decodeUncompressedAddressToBytes(long addr) {
+  byte[] decodeUncompressedAddressToBytes(long addr) {
     if ((addr & COMPRESSED_BIT) != 0) {
       throw new AssertionError("Did not expect encoded address to be compressed");
     }
@@ -305,13 +295,15 @@ public class OffHeapRegionEntryHelper {
    * Returns the "raw" bytes that have been encoded in the given address. Note that if address is
    * compressed then the raw bytes are the compressed bytes.
    */
-  static byte[] decodeAddressToRawBytes(long addr) {
+  byte[] decodeAddressToRawBytes(long addr) {
     if ((addr & ENCODED_BIT) == 0) {
       throw new AssertionError("Invalid address: " + addr);
     }
+
     int size = (int) ((addr & SIZE_MASK) >> SIZE_SHIFT);
     boolean isLong = (addr & LONG_BIT) != 0;
     byte[] bytes;
+
     if (isLong) {
       bytes = new byte[9];
       bytes[0] = DSCODE.LONG.toByte();
@@ -331,6 +323,7 @@ public class OffHeapRegionEntryHelper {
         bytes[i] = (byte) (addr & 0x00ff);
       }
     }
+
     return bytes;
   }
 
@@ -338,36 +331,42 @@ public class OffHeapRegionEntryHelper {
    * The previous value at the address in 're' will be @Released and then the address in 're' will
    * be set to the @Unretained address of 'v'.
    */
-  public static void setValue(@Released OffHeapRegionEntry re, @Unretained Object v) {
+  public void setValue(@Released OffHeapRegionEntry regionEntry, @Unretained Object value) {
     // setValue is called when synced so I don't need to worry
     // about oldAddress being released by someone else.
-    final long newAddress = objectToAddress(v);
+    final long newAddress = TokenAddress.objectToAddress(value);
     long oldAddress;
+
     do {
-      oldAddress = re.getAddress();
-    } while (!re.setAddress(oldAddress, newAddress));
-    ReferenceCountHelper.setReferenceCountOwner(re);
+      oldAddress = regionEntry.getAddress();
+    } while (!regionEntry.setAddress(oldAddress, newAddress));
+
+    ReferenceCountHelper.setReferenceCountOwner(regionEntry);
     releaseAddress(oldAddress);
     ReferenceCountHelper.setReferenceCountOwner(null);
   }
 
-  public static Token getValueAsToken(@Unretained OffHeapRegionEntry re) {
-    return addressToToken(re.getAddress());
+  public Token getValueAsToken(@Unretained OffHeapRegionEntry regionEntry) {
+    return addressToToken(regionEntry.getAddress());
   }
 
   @Unretained
-  public static Object _getValue(@Unretained OffHeapRegionEntry re) {
-    return addressToObject(re.getAddress(), false, null); // no context needed so decompress is
-                                                          // false
+  public Object _getValue(@Unretained OffHeapRegionEntry regionEntry) {
+    // no context needed so decompress is false
+    return addressToObject(regionEntry.getAddress(), false, null);
   }
 
-  public static boolean isOffHeap(long addr) {
-    if ((addr & ENCODED_BIT) != 0)
+  public boolean isOffHeap(long address) {
+    if ((address & ENCODED_BIT) != 0) {
       return false;
-    if (addr < 0)
+    }
+    if (address < 0) {
       return true;
-    addr >>= 1; // shift right 1 to convert to array index;
-    return addr >= addrToObj.length;
+    }
+
+    // shift right 1 to convert to array index;
+    address >>= 1;
+    return address >= TokenAddress.ADDRESS_TO_OBJECT.length;
   }
 
   /**
@@ -379,63 +378,90 @@ public class OffHeapRegionEntryHelper {
    * @return possible OFF_HEAP_OBJECT (caller must release)
    */
   @Retained
-  public static Object _getValueRetain(@Retained @Unretained OffHeapRegionEntry re,
+  public Object _getValueRetain(@Retained @Unretained OffHeapRegionEntry regionEntry,
       boolean decompress, RegionEntryContext context) {
     int retryCount = 0;
+
     @Retained
-    long addr = re.getAddress();
-    while (isOffHeap(addr)) {
-      if (OffHeapStoredObject.retain(addr)) {
+    long address = regionEntry.getAddress();
+
+    while (isOffHeap(address)) {
+      if (referenceCounter.retain(address)) {
         @Unretained
-        long addr2 = re.getAddress();
-        if (addr != addr2) {
+        long address2 = regionEntry.getAddress();
+        if (address != address2) {
           retryCount = 0;
-          OffHeapStoredObject.release(addr);
+          referenceCounter.release(address);
           // spin around and try again.
-          addr = addr2;
+          address = address2;
         } else {
-          return addressToObject(addr, decompress, context);
+          return addressToObject(address, decompress, context);
         }
       } else {
         // spin around and try again
-        long addr2 = re.getAddress();
+        long address2 = regionEntry.getAddress();
         retryCount++;
         if (retryCount > 100) {
-          throw new IllegalStateException("retain failed addr=" + addr + " addr2=" + addr
-              + " 100 times" + " history=" + ReferenceCountHelper.getFreeRefCountInfo(addr));
+          throw new IllegalStateException("retain failed address=" + address + " addr2=" + address
+              + " 100 times" + " history=" + ReferenceCountHelper.getFreeRefCountInfo(address));
         }
-        addr = addr2;
-        // Since retain returned false our region entry should have a different
-        // value in it. However the actual address could be the exact same one
-        // because addr was released, then reallocated from the free list and set
-        // back into this region entry. See bug 47782
+        address = address2;
+        // Since retain returned false our region entry should have a different value in it. However
+        // the actual address could be the exact same one because address was released, then
+        // reallocated from the free list and set back into this region entry.
       }
     }
-    return addressToObject(addr, decompress, context);
+    return addressToObject(address, decompress, context);
   }
 
-
-
-  public static boolean isSerialized(long address) {
+  public boolean isSerialized(long address) {
     return (address & SERIALIZED_BIT) != 0;
   }
 
-  public static boolean isCompressed(long address) {
+  public boolean isCompressed(long address) {
     return (address & COMPRESSED_BIT) != 0;
   }
 
-  private static final ThreadLocal<Object> clearNeedsToCheckForOffHeap = new ThreadLocal<Object>();
+  private static class TokenAddress {
 
-  public static boolean doesClearNeedToCheckForOffHeap() {
-    return clearNeedsToCheckForOffHeap.get() != null;
-  }
+    @Immutable
+    private static final Token[] ADDRESS_TO_OBJECT =
+        new Token[] {null, Token.INVALID, Token.LOCAL_INVALID, Token.DESTROYED,
+            Token.REMOVED_PHASE1,
+            Token.REMOVED_PHASE2, Token.END_OF_STREAM, Token.NOT_AVAILABLE, Token.TOMBSTONE,};
 
-  public static void doWithOffHeapClear(Runnable r) {
-    clearNeedsToCheckForOffHeap.set(Boolean.TRUE);
-    try {
-      r.run();
-    } finally {
-      clearNeedsToCheckForOffHeap.remove();
+    private static long objectToAddress(@Unretained Object v) {
+      if (v instanceof StoredObject) {
+        return ((StoredObject) v).getAddress();
+      }
+      if (v == null) {
+        return NULL_ADDRESS;
+      }
+      if (v == Token.TOMBSTONE) {
+        return TOMBSTONE_ADDRESS;
+      }
+      if (v == Token.INVALID) {
+        return INVALID_ADDRESS;
+      }
+      if (v == Token.LOCAL_INVALID) {
+        return LOCAL_INVALID_ADDRESS;
+      }
+      if (v == Token.DESTROYED) {
+        return DESTROYED_ADDRESS;
+      }
+      if (v == Token.REMOVED_PHASE1) {
+        return REMOVED_PHASE1_ADDRESS;
+      }
+      if (v == Token.REMOVED_PHASE2) {
+        return REMOVED_PHASE2_ADDRESS;
+      }
+      if (v == Token.END_OF_STREAM) {
+        return END_OF_STREAM_ADDRESS;
+      }
+      if (v == Token.NOT_AVAILABLE) {
+        return NOT_AVAILABLE_ADDRESS;
+      }
+      throw new IllegalStateException("Can not convert " + v + " to an off heap address.");
     }
   }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapStoredObject.java b/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapStoredObject.java
index b7b1af0..2fa57d7 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapStoredObject.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/offheap/OffHeapStoredObject.java
@@ -260,13 +260,6 @@ public class OffHeapStoredObject extends AbstractStoredObject
     return getDataSize(this.memoryAddress);
   }
 
-  protected static int getDataSize(long memoryAdress) {
-    int dataSizeDelta = AddressableMemoryManager.readInt(memoryAdress + REF_COUNT_OFFSET);
-    dataSizeDelta &= DATA_SIZE_DELTA_MASK;
-    dataSizeDelta >>= DATA_SIZE_SHIFT;
-    return getSize(memoryAdress) - dataSizeDelta;
-  }
-
   protected long getBaseDataAddress() {
     return this.memoryAddress + HEADER_SIZE;
   }
@@ -370,7 +363,7 @@ public class OffHeapStoredObject extends AbstractStoredObject
 
   @Override
   public void release() {
-    release(this.memoryAddress);
+    ReferenceCounter.release(this.memoryAddress);
   }
 
   @Override
@@ -481,44 +474,12 @@ public class OffHeapStoredObject extends AbstractStoredObject
 
   @Override
   public boolean retain() {
-    return retain(this.memoryAddress);
+    return ReferenceCounter.retain(this.memoryAddress);
   }
 
   @Override
   public int getRefCount() {
-    return getRefCount(this.memoryAddress);
-  }
-
-  public static int getSize(long memAddr) {
-    MemoryAllocatorImpl.validateAddress(memAddr);
-    return AddressableMemoryManager.readInt(memAddr + CHUNK_SIZE_OFFSET);
-  }
-
-  public static void setSize(long memAddr, int size) {
-    MemoryAllocatorImpl.validateAddressAndSize(memAddr, size);
-    AddressableMemoryManager.writeInt(memAddr + CHUNK_SIZE_OFFSET, size);
-  }
-
-  public static long getNext(long memAddr) {
-    MemoryAllocatorImpl.validateAddress(memAddr);
-    return AddressableMemoryManager.readLong(memAddr + HEADER_SIZE);
-  }
-
-  public static void setNext(long memAddr, long next) {
-    MemoryAllocatorImpl.validateAddress(memAddr);
-    AddressableMemoryManager.writeLong(memAddr + HEADER_SIZE, next);
-  }
-
-  /**
-   * Fills the chunk with a repeated byte fill pattern.
-   *
-   * @param baseAddress the starting address for a {@link OffHeapStoredObject}.
-   */
-  public static void fill(long baseAddress) {
-    long startAddress = baseAddress + MIN_CHUNK_SIZE;
-    int size = getSize(baseAddress) - MIN_CHUNK_SIZE;
-
-    AddressableMemoryManager.fill(startAddress, size, FILL_BYTE);
+    return ReferenceCounter.getRefCount(this.memoryAddress);
   }
 
   /**
@@ -617,98 +578,6 @@ public class OffHeapStoredObject extends AbstractStoredObject
         rawBits, rawBits + 1));
   }
 
-  public static int getRefCount(long memAddr) {
-    return AddressableMemoryManager.readInt(memAddr + REF_COUNT_OFFSET) & REF_COUNT_MASK;
-  }
-
-  public static boolean retain(long memAddr) {
-    MemoryAllocatorImpl.validateAddress(memAddr);
-    int uc;
-    int rawBits;
-    int retryCount = 0;
-    do {
-      rawBits = AddressableMemoryManager.readIntVolatile(memAddr + REF_COUNT_OFFSET);
-      if ((rawBits & MAGIC_MASK) != MAGIC_NUMBER) {
-        // same as uc == 0
-        // TODO MAGIC_NUMBER rethink its use and interaction with compactor fragments
-        return false;
-      }
-      uc = rawBits & REF_COUNT_MASK;
-      if (uc == MAX_REF_COUNT) {
-        throw new IllegalStateException(
-            "Maximum use count exceeded. rawBits=" + Integer.toHexString(rawBits));
-      } else if (uc == 0) {
-        return false;
-      }
-      retryCount++;
-      if (retryCount > 1000) {
-        throw new IllegalStateException("tried to write " + (rawBits + 1) + " to @"
-            + Long.toHexString(memAddr) + " 1,000 times.");
-      }
-    } while (!AddressableMemoryManager.writeIntVolatile(memAddr + REF_COUNT_OFFSET, rawBits,
-        rawBits + 1));
-    // debugLog("use inced ref count " + (uc+1) + " @" + Long.toHexString(memAddr), true);
-    if (ReferenceCountHelper.trackReferenceCounts()) {
-      ReferenceCountHelper.refCountChanged(memAddr, false, uc + 1);
-    }
-
-    return true;
-  }
-
-  public static void release(final long memAddr) {
-    release(memAddr, null);
-  }
-
-  static void release(final long memAddr, FreeListManager freeListManager) {
-    MemoryAllocatorImpl.validateAddress(memAddr);
-    int newCount;
-    int rawBits;
-    boolean returnToAllocator;
-    do {
-      returnToAllocator = false;
-      rawBits = AddressableMemoryManager.readIntVolatile(memAddr + REF_COUNT_OFFSET);
-      if ((rawBits & MAGIC_MASK) != MAGIC_NUMBER) {
-        String msg = "It looks like off heap memory @" + Long.toHexString(memAddr)
-            + " was already freed. rawBits=" + Integer.toHexString(rawBits) + " history="
-            + ReferenceCountHelper.getFreeRefCountInfo(memAddr);
-        // debugLog(msg, true);
-        throw new IllegalStateException(msg);
-      }
-      int curCount = rawBits & REF_COUNT_MASK;
-      if ((curCount) == 0) {
-        // debugLog("too many frees @" + Long.toHexString(memAddr), true);
-        throw new IllegalStateException(
-            "Memory has already been freed." + " history=" + ReferenceCountHelper
-                .getFreeRefCountInfo(memAddr) /* + System.identityHashCode(this) */);
-      }
-      if (curCount == 1) {
-        newCount = 0; // clear the use count, bits, and the delta size since it will be freed.
-        returnToAllocator = true;
-      } else {
-        newCount = rawBits - 1;
-      }
-    } while (!AddressableMemoryManager.writeIntVolatile(memAddr + REF_COUNT_OFFSET, rawBits,
-        newCount));
-    // debugLog("free deced ref count " + (newCount&USE_COUNT_MASK) + " @" +
-    // Long.toHexString(memAddr), true);
-    if (returnToAllocator) {
-      if (ReferenceCountHelper.trackReferenceCounts()) {
-        if (ReferenceCountHelper.trackFreedReferenceCounts()) {
-          ReferenceCountHelper.refCountChanged(memAddr, true, newCount & REF_COUNT_MASK);
-        }
-        ReferenceCountHelper.freeRefCountInfo(memAddr);
-      }
-      if (freeListManager == null) {
-        freeListManager = MemoryAllocatorImpl.getAllocator().getFreeListManager();
-      }
-      freeListManager.free(memAddr);
-    } else {
-      if (ReferenceCountHelper.trackReferenceCounts()) {
-        ReferenceCountHelper.refCountChanged(memAddr, true, newCount & REF_COUNT_MASK);
-      }
-    }
-  }
-
   @Override
   public String toString() {
     return super.toString() + ":<dataSize=" + getDataSize() + " refCount=" + getRefCount()
@@ -763,4 +632,45 @@ public class OffHeapStoredObject extends AbstractStoredObject
   public boolean hasRefCount() {
     return true;
   }
+
+  // ---------------------------------- STATICS --------------------------------------
+
+  protected static int getDataSize(long memoryAdress) {
+    int dataSizeDelta = AddressableMemoryManager.readInt(memoryAdress + REF_COUNT_OFFSET);
+    dataSizeDelta &= DATA_SIZE_DELTA_MASK;
+    dataSizeDelta >>= DATA_SIZE_SHIFT;
+    return getSize(memoryAdress) - dataSizeDelta;
+  }
+
+  public static int getSize(long memAddr) {
+    MemoryAllocatorImpl.validateAddress(memAddr);
+    return AddressableMemoryManager.readInt(memAddr + CHUNK_SIZE_OFFSET);
+  }
+
+  public static void setSize(long memAddr, int size) {
+    MemoryAllocatorImpl.validateAddressAndSize(memAddr, size);
+    AddressableMemoryManager.writeInt(memAddr + CHUNK_SIZE_OFFSET, size);
+  }
+
+  public static long getNext(long memAddr) {
+    MemoryAllocatorImpl.validateAddress(memAddr);
+    return AddressableMemoryManager.readLong(memAddr + HEADER_SIZE);
+  }
+
+  public static void setNext(long memAddr, long next) {
+    MemoryAllocatorImpl.validateAddress(memAddr);
+    AddressableMemoryManager.writeLong(memAddr + HEADER_SIZE, next);
+  }
+
+  /**
+   * Fills the chunk with a repeated byte fill pattern.
+   *
+   * @param baseAddress the starting address for a {@link OffHeapStoredObject}.
+   */
+  public static void fill(long baseAddress) {
+    long startAddress = baseAddress + MIN_CHUNK_SIZE;
+    int size = getSize(baseAddress) - MIN_CHUNK_SIZE;
+
+    AddressableMemoryManager.fill(startAddress, size, FILL_BYTE);
+  }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/internal/offheap/ReferenceCountHelper.java b/geode-core/src/main/java/org/apache/geode/internal/offheap/ReferenceCountHelper.java
index 41c845b..62b67ff 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/offheap/ReferenceCountHelper.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/offheap/ReferenceCountHelper.java
@@ -18,7 +18,7 @@ package org.apache.geode.internal.offheap;
 import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
-import org.apache.geode.annotations.internal.MakeNotStatic;
+import org.apache.geode.annotations.Immutable;
 import org.apache.geode.util.internal.GeodeGlossary;
 
 /**
@@ -28,34 +28,38 @@ import org.apache.geode.util.internal.GeodeGlossary;
  */
 public class ReferenceCountHelper {
 
-  public static final String TRACK_OFFHEAP_REFERENCES =
+  private static final String TRACK_OFFHEAP_REFERENCES =
       GeodeGlossary.GEMFIRE_PREFIX + "trackOffHeapRefCounts";
-  public static final String TRACK_OFFHEAP_FREES =
+  private static final String TRACK_OFFHEAP_FREES =
       GeodeGlossary.GEMFIRE_PREFIX + "trackOffHeapFreedRefCounts";
 
-  @MakeNotStatic
-  private static final ReferenceCountHelperImpl inst = new ReferenceCountHelperImpl(
-      Boolean.getBoolean(TRACK_OFFHEAP_REFERENCES), Boolean.getBoolean(TRACK_OFFHEAP_FREES));
+  @Immutable
+  private static final ReferenceCountHelper INSTANCE = new ReferenceCountHelper(
+      new ReferenceCountHelperImpl(Boolean.getBoolean(TRACK_OFFHEAP_REFERENCES),
+          Boolean.getBoolean(TRACK_OFFHEAP_FREES)));
 
-  /* Do not allow any instances */
-  private ReferenceCountHelper() {}
+  private final ReferenceCountHelperImpl delegate;
 
-  static ReferenceCountHelperImpl getInstance() {
-    return inst;
+  private ReferenceCountHelper(ReferenceCountHelperImpl delegate) {
+    this.delegate = delegate;
+  }
+
+  private static ReferenceCountHelperImpl delegate() {
+    return INSTANCE.delegate;
   }
 
   /**
    * Returns true if reference count tracking is enabled.
    */
   public static boolean trackReferenceCounts() {
-    return getInstance().trackReferenceCounts();
+    return delegate().trackReferenceCounts();
   }
 
   /**
    * Returns true if free operation tracking is enabled.
    */
-  public static boolean trackFreedReferenceCounts() {
-    return getInstance().trackFreedReferenceCounts();
+  static boolean trackFreedReferenceCounts() {
+    return delegate().trackFreedReferenceCounts();
   }
 
   /**
@@ -64,7 +68,7 @@ public class ReferenceCountHelper {
    * responsible for decrementing it. Calling this method is a noop if !trackReferenceCounts.
    */
   public static void setReferenceCountOwner(Object owner) {
-    getInstance().setReferenceCountOwner(owner);
+    delegate().setReferenceCountOwner(owner);
   }
 
   /**
@@ -72,7 +76,7 @@ public class ReferenceCountHelper {
    * and returns null if !trackReferenceCounts.
    */
   public static Object createReferenceCountOwner() {
-    return getInstance().createReferenceCountOwner();
+    return delegate().createReferenceCountOwner();
   }
 
   /**
@@ -81,57 +85,57 @@ public class ReferenceCountHelper {
    * of this method must also call unskipRefCountTracking after the allocation or free is done.
    */
   public static void skipRefCountTracking() {
-    getInstance().skipRefCountTracking();
+    delegate().skipRefCountTracking();
   }
 
   /**
    * Returns true if currently tracking reference counts.
    */
   public static boolean isRefCountTracking() {
-    return getInstance().isRefCountTracking();
+    return delegate().isRefCountTracking();
   }
 
   /**
    * Call this method to undo a call to skipRefCountTracking.
    */
   public static void unskipRefCountTracking() {
-    getInstance().unskipRefCountTracking();
+    delegate().unskipRefCountTracking();
   }
 
   /**
    * Returns a list of any reference count tracking information for the given Chunk address.
    */
   public static List<RefCountChangeInfo> getRefCountInfo(long address) {
-    return getInstance().getRefCountInfo(address);
+    return delegate().getRefCountInfo(address);
   }
 
   /**
    * Used internally to report that a reference count has changed.
    */
-  static void refCountChanged(Long address, boolean decRefCount, int rc) {
-    getInstance().refCountChanged(address, decRefCount, rc);
+  public static void refCountChanged(Long address, boolean decRefCount, int rc) {
+    delegate().refCountChanged(address, decRefCount, rc);
   }
 
   /**
    * Called internally when free operations are tracked to record that a free has happened of the
    * given address.
    */
-  static void freeRefCountInfo(Long address) {
-    getInstance().freeRefCountInfo(address);
+  public static void freeRefCountInfo(Long address) {
+    delegate().freeRefCountInfo(address);
   }
 
   /**
    * Returns the thread local owner
    */
-  static Object getReferenceCountOwner() {
-    return getInstance().getReferenceCountOwner();
+  public static Object getReferenceCountOwner() {
+    return delegate().getReferenceCountOwner();
   }
 
   /**
    * Returns the thread local count of the number of times ref count has been updated
    */
-  static AtomicInteger getReenterCount() {
-    return getInstance().getReenterCount();
+  public static AtomicInteger getReenterCount() {
+    return delegate().getReenterCount();
   }
 
   /**
@@ -139,14 +143,14 @@ public class ReferenceCountHelper {
    * previous free(s) when an extra one ends up being done and fails.
    */
   public static List<RefCountChangeInfo> getFreeRefCountInfo(long address) {
-    return getInstance().getFreeRefCountInfo(address);
+    return delegate().getFreeRefCountInfo(address);
   }
 
   /**
    * Returns a list of any reference count tracking information for the given Chunk address without
    * locking.
    */
-  static List<RefCountChangeInfo> peekRefCountInfo(long address) {
-    return getInstance().peekRefCountInfo(address);
+  public static List<RefCountChangeInfo> peekRefCountInfo(long address) {
+    return delegate().peekRefCountInfo(address);
   }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/internal/offheap/ReferenceCountHelperImpl.java b/geode-core/src/main/java/org/apache/geode/internal/offheap/ReferenceCountHelperImpl.java
index 5671077..1214ea2 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/offheap/ReferenceCountHelperImpl.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/offheap/ReferenceCountHelperImpl.java
@@ -12,64 +12,78 @@
  * or implied. See the License for the specific language governing permissions and limitations under
  * the License.
  */
-
 package org.apache.geode.internal.offheap;
 
+import static java.util.Collections.emptyList;
+
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.List;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiConsumer;
 
-import org.apache.geode.annotations.internal.MakeNotStatic;
+import org.apache.geode.annotations.Immutable;
 import org.apache.geode.internal.cache.RegionEntry;
 
 /**
  * All access to this class should be done through the static methods of ReferenceCountHelper.
  */
 class ReferenceCountHelperImpl {
-  private boolean trackRefCounts;
-  private boolean trackFreedRefCounts;
-  private ConcurrentMap<Long, List<RefCountChangeInfo>> stacktraces;
-  private ConcurrentMap<Long, List<RefCountChangeInfo>> freedStacktraces;
-  private ThreadLocal<Object> refCountOwner;
-  private ThreadLocal<AtomicInteger> refCountReenterCount;
+
   private static final Object SKIP_REF_COUNT_TRACKING = new Object();
-  @MakeNotStatic
-  private static final List<RefCountChangeInfo> LOCKED = Collections.emptyList();
+
+  @Immutable
+  private static final List<RefCountChangeInfo> LOCKED = emptyList();
+
+  // TODO: ThreadLocals should be static final
+
+  private final ThreadLocal<Object> refCountOwner;
+  private final ThreadLocal<AtomicInteger> refCountReenterCount;
+
+  private final boolean trackRefCounts;
+  private final boolean trackFreedRefCounts;
+  private final ConcurrentMap<Long, List<RefCountChangeInfo>> stacktraces;
+  private final ConcurrentMap<Long, List<RefCountChangeInfo>> freedStacktraces;
+  private final BiConsumer<String, Boolean> debugLogger;
 
   ReferenceCountHelperImpl(boolean trackRefCounts, boolean trackFreedRefCounts) {
+    this(trackRefCounts, trackFreedRefCounts, MemoryAllocatorImpl::debugLog);
+  }
+
+  ReferenceCountHelperImpl(boolean trackRefCounts, boolean trackFreedRefCounts,
+      BiConsumer<String, Boolean> debugLogger) {
     this.trackRefCounts = trackRefCounts;
     this.trackFreedRefCounts = trackFreedRefCounts;
     if (trackRefCounts) {
-      stacktraces = new ConcurrentHashMap<Long, List<RefCountChangeInfo>>();
+      stacktraces = new ConcurrentHashMap<>();
       if (trackFreedRefCounts) {
-        freedStacktraces = new ConcurrentHashMap<Long, List<RefCountChangeInfo>>();
+        freedStacktraces = new ConcurrentHashMap<>();
       } else {
         freedStacktraces = null;
       }
-      refCountOwner = new ThreadLocal<Object>();
-      refCountReenterCount = new ThreadLocal<AtomicInteger>();
+      refCountOwner = new ThreadLocal<>();
+      refCountReenterCount = new ThreadLocal<>();
     } else {
       stacktraces = null;
       freedStacktraces = null;
       refCountOwner = null;
       refCountReenterCount = null;
     }
+    this.debugLogger = debugLogger;
   }
 
   /**
    * Returns true if reference count tracking is enabled.
    */
-  public boolean trackReferenceCounts() {
+  boolean trackReferenceCounts() {
     return trackRefCounts;
   }
 
   /**
    * Returns true if free operation tracking is enabled.
    */
-  public boolean trackFreedReferenceCounts() {
+  boolean trackFreedReferenceCounts() {
     return trackFreedRefCounts;
   }
 
@@ -78,28 +92,28 @@ class ReferenceCountHelperImpl {
    * could set the particular EntryEventImpl instance that incremented the reference count and is
    * responsible for decrementing it. Calling this method is a noop if !trackReferenceCounts.
    */
-  public void setReferenceCountOwner(Object owner) {
+  void setReferenceCountOwner(Object owner) {
     if (trackReferenceCounts()) {
       if (refCountOwner.get() != null) {
-        AtomicInteger ai = refCountReenterCount.get();
+        AtomicInteger reenterCount = refCountReenterCount.get();
         if (owner != null) {
-          ai.incrementAndGet();
+          reenterCount.incrementAndGet();
         } else {
-          if (ai.decrementAndGet() <= 0) {
+          if (reenterCount.decrementAndGet() <= 0) {
             refCountOwner.set(null);
-            ai.set(0);
+            reenterCount.set(0);
           }
         }
       } else {
-        AtomicInteger ai = refCountReenterCount.get();
-        if (ai == null) {
-          ai = new AtomicInteger(0);
-          refCountReenterCount.set(ai);
+        AtomicInteger reenterCount = refCountReenterCount.get();
+        if (reenterCount == null) {
+          reenterCount = new AtomicInteger(0);
+          refCountReenterCount.set(reenterCount);
         }
         if (owner != null) {
-          ai.set(1);
+          reenterCount.set(1);
         } else {
-          ai.set(0);
+          reenterCount.set(0);
         }
         refCountOwner.set(owner);
       }
@@ -110,7 +124,7 @@ class ReferenceCountHelperImpl {
    * Create, set, and return a generic reference count owner object. Calling this method is a noop
    * and returns null if !trackReferenceCounts.
    */
-  public Object createReferenceCountOwner() {
+  Object createReferenceCountOwner() {
     Object result = null;
     if (trackReferenceCounts()) {
       result = new Object();
@@ -131,25 +145,28 @@ class ReferenceCountHelperImpl {
   /**
    * Returns true if currently tracking reference counts.
    */
-  public boolean isRefCountTracking() {
-    if (!trackReferenceCounts())
+  boolean isRefCountTracking() {
+    if (!trackReferenceCounts()) {
       return false;
-    return !(getReferenceCountOwner() == SKIP_REF_COUNT_TRACKING);
+    }
+    return getReferenceCountOwner() != SKIP_REF_COUNT_TRACKING;
   }
 
   /**
    * Call this method to undo a call to skipRefCountTracking.
    */
-  public void unskipRefCountTracking() {
+  void unskipRefCountTracking() {
     setReferenceCountOwner(null);
   }
 
   /**
    * Returns a list of any reference count tracking information for the given Chunk address.
    */
-  public List<RefCountChangeInfo> getRefCountInfo(long address) {
-    if (!trackReferenceCounts())
+  List<RefCountChangeInfo> getRefCountInfo(Long address) {
+    if (!trackReferenceCounts()) {
       return null;
+    }
+
     List<RefCountChangeInfo> result = stacktraces.get(address);
 
     getReferenceCountInfoTestHook(stacktraces, address);
@@ -160,20 +177,14 @@ class ReferenceCountHelperImpl {
     return result;
   }
 
-  /*
-   * This method is overridden during testing to allow simulation of a concurrent update occurring
-   * between stacktraces.get and stacktraces.replace
-   */
-  protected void getReferenceCountInfoTestHook(
-      ConcurrentMap<Long, List<RefCountChangeInfo>> stacktraces, long address) {}
-
   /**
    * Returns a list of any reference count tracking information for the given Chunk address without
    * locking.
    */
-  public List<RefCountChangeInfo> peekRefCountInfo(long address) {
-    if (!trackReferenceCounts())
+  List<RefCountChangeInfo> peekRefCountInfo(long address) {
+    if (!trackReferenceCounts()) {
       return null;
+    }
     return stacktraces.get(address);
   }
 
@@ -181,18 +192,21 @@ class ReferenceCountHelperImpl {
    * Used internally to report that a reference count has changed.
    */
   void refCountChanged(Long address, boolean decRefCount, int rc) {
-    if (!trackReferenceCounts())
+    if (!trackReferenceCounts()) {
       return;
+    }
+
     final Object owner = refCountOwner.get();
     if (owner == SKIP_REF_COUNT_TRACKING) {
       return;
     }
+
     List<RefCountChangeInfo> list = stacktraces.get(address);
-    if (list == null) {
-      List<RefCountChangeInfo> newList = new ArrayList<RefCountChangeInfo>();
 
+    if (list == null) {
       refCountChangedTestHook(address, decRefCount, rc);
 
+      List<RefCountChangeInfo> newList = new ArrayList<>();
       List<RefCountChangeInfo> old = stacktraces.putIfAbsent(address, newList);
       if (old == null) {
         list = newList;
@@ -200,6 +214,7 @@ class ReferenceCountHelperImpl {
         list = old;
       }
     }
+
     if (decRefCount) {
       if (owner != null) {
         synchronized (list) {
@@ -226,45 +241,42 @@ class ReferenceCountHelperImpl {
         }
       }
     }
+
     if (list == LOCKED) {
-      MemoryAllocatorImpl.debugLog("refCount " + (decRefCount ? "deced" : "inced")
-          + " after orphan detected for @" + Long.toHexString(address), true);
+      String message = "refCount " + (decRefCount ? "deced" : "inced")
+          + " after orphan detected for @" + Long.toHexString(address);
+      debugLogger.accept(message, true);
       return;
     }
-    RefCountChangeInfo info = new RefCountChangeInfo(decRefCount, rc, owner);
+
+    RefCountChangeInfo refCountChangeInfo = new RefCountChangeInfo(decRefCount, rc, owner);
+
     synchronized (list) {
-      // if (list.size() == 16) {
-      // debugLog("dumping @" + Long.toHexString(address) + " history=" + list, false);
-      // list.clear();
-      // }
-      for (RefCountChangeInfo e : list) {
-        if (e.isSameCaller(info)) {
+      for (RefCountChangeInfo otherRefCountChangeInfo : list) {
+        if (otherRefCountChangeInfo.isSameCaller(refCountChangeInfo)) {
           // No need to add it just increment useCount
-          e.incUseCount();
+          otherRefCountChangeInfo.incUseCount();
           return;
         }
       }
-      list.add(info);
+      list.add(refCountChangeInfo);
     }
   }
 
-  /*
-   * This method is overridden during testing to allow simulation of a race to be the first to
-   * reference a given address
-   */
-  protected void refCountChangedTestHook(Long address, boolean decRefCount, int rc) {}
-
   /**
    * Called internally when free operations are tracked to record that a free has happened of the
    * given address.
    */
   void freeRefCountInfo(Long address) {
-    if (!trackReferenceCounts())
+    if (!trackReferenceCounts()) {
       return;
+    }
+
     List<RefCountChangeInfo> freedInfo = stacktraces.remove(address);
+
     if (freedInfo == LOCKED) {
-      MemoryAllocatorImpl.debugLog("freed after orphan detected for @" + Long.toHexString(address),
-          true);
+      debugLogger.accept("freed after orphan detected for @" + Long.toHexString(address), true);
+
     } else if (trackFreedReferenceCounts()) {
       if (freedInfo != null) {
         freedStacktraces.put(address, freedInfo);
@@ -278,8 +290,9 @@ class ReferenceCountHelperImpl {
    * Returns the thread local owner
    */
   Object getReferenceCountOwner() {
-    if (!trackReferenceCounts())
+    if (!trackReferenceCounts()) {
       return null;
+    }
     return refCountOwner.get();
   }
 
@@ -287,8 +300,9 @@ class ReferenceCountHelperImpl {
    * Returns the thread local count of the number of times ref count has been updated
    */
   AtomicInteger getReenterCount() {
-    if (!trackReferenceCounts())
+    if (!trackReferenceCounts()) {
       return null;
+    }
     return refCountReenterCount.get();
   }
 
@@ -296,10 +310,27 @@ class ReferenceCountHelperImpl {
    * Returns a list of any free operation tracking information. This is used to describe who did the
    * previous free(s) when an extra one ends up being done and fails.
    */
-  public List<RefCountChangeInfo> getFreeRefCountInfo(long address) {
-    if (!trackReferenceCounts() || !trackFreedReferenceCounts())
+  List<RefCountChangeInfo> getFreeRefCountInfo(long address) {
+    if (!trackReferenceCounts() || !trackFreedReferenceCounts()) {
       return null;
+    }
     return freedStacktraces.get(address);
   }
 
+  /**
+   * This method is overridden during testing to allow simulation of a concurrent update occurring
+   * between stacktraces.get and stacktraces.replace
+   */
+  void getReferenceCountInfoTestHook(ConcurrentMap<Long, List<RefCountChangeInfo>> stacktraces,
+      long address) {
+    // nothing
+  }
+
+  /**
+   * This method is overridden during testing to allow simulation of a race to be the first to
+   * reference a given address
+   */
+  void refCountChangedTestHook(Long address, boolean decRefCount, int rc) {
+    // nothing
+  }
 }
diff --git a/geode-core/src/main/java/org/apache/geode/internal/offheap/ReferenceCounter.java b/geode-core/src/main/java/org/apache/geode/internal/offheap/ReferenceCounter.java
new file mode 100644
index 0000000..5f3155e
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/offheap/ReferenceCounter.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.offheap;
+
+import org.apache.geode.annotations.Immutable;
+
+class ReferenceCounter {
+
+  @Immutable
+  private static final ReferenceCounter INSTANCE =
+      new ReferenceCounter(new ReferenceCounterInstance());
+
+  private final ReferenceCounterInstance delegate;
+
+  private static ReferenceCounterInstance delegate() {
+    return INSTANCE.delegate;
+  }
+
+  private ReferenceCounter(ReferenceCounterInstance delegate) {
+    this.delegate = delegate;
+  }
+
+  static int getRefCount(long memAddr) {
+    return delegate().getRefCount(memAddr);
+  }
+
+  static boolean retain(long memAddr) {
+    return delegate().retain(memAddr);
+  }
+
+  static void release(final long memAddr) {
+    delegate().release(memAddr);
+  }
+
+  static void release(final long memAddr, FreeListManager freeListManager) {
+    delegate().release(memAddr, freeListManager);
+  }
+}
diff --git a/geode-core/src/main/java/org/apache/geode/internal/offheap/ReferenceCounterInstance.java b/geode-core/src/main/java/org/apache/geode/internal/offheap/ReferenceCounterInstance.java
new file mode 100644
index 0000000..663be5f
--- /dev/null
+++ b/geode-core/src/main/java/org/apache/geode/internal/offheap/ReferenceCounterInstance.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.offheap;
+
+class ReferenceCounterInstance {
+
+  int getRefCount(long address) {
+    return AddressableMemoryManager.readInt(address + OffHeapStoredObject.REF_COUNT_OFFSET)
+        & OffHeapStoredObject.REF_COUNT_MASK;
+  }
+
+  boolean retain(long address) {
+    MemoryAllocatorImpl.validateAddress(address);
+    int usageCount;
+    int rawBits;
+    int retryCount = 0;
+
+    do {
+      rawBits =
+          AddressableMemoryManager.readIntVolatile(address + OffHeapStoredObject.REF_COUNT_OFFSET);
+      if ((rawBits & OffHeapStoredObject.MAGIC_MASK) != OffHeapStoredObject.MAGIC_NUMBER) {
+        return false;
+      }
+      usageCount = rawBits & OffHeapStoredObject.REF_COUNT_MASK;
+      if (usageCount == OffHeapStoredObject.MAX_REF_COUNT) {
+        throw new IllegalStateException(
+            "Maximum use count exceeded. rawBits=" + Integer.toHexString(rawBits));
+      }
+      if (usageCount == 0) {
+        return false;
+      }
+      retryCount++;
+      if (retryCount > 1000) {
+        throw new IllegalStateException("tried to write " + (rawBits + 1) + " to @"
+            + Long.toHexString(address) + " 1,000 times.");
+      }
+    } while (!AddressableMemoryManager
+        .writeIntVolatile(address + OffHeapStoredObject.REF_COUNT_OFFSET, rawBits, rawBits + 1));
+
+    if (ReferenceCountHelper.trackReferenceCounts()) {
+      ReferenceCountHelper.refCountChanged(address, false, usageCount + 1);
+    }
+
+    return true;
+  }
+
+  void release(final long address) {
+    release(address, null);
+  }
+
+  void release(final long address, FreeListManager freeListManager) {
+    MemoryAllocatorImpl.validateAddress(address);
+    int newCount;
+    int rawBits;
+    boolean returnToAllocator;
+
+    do {
+      returnToAllocator = false;
+      rawBits =
+          AddressableMemoryManager.readIntVolatile(address + OffHeapStoredObject.REF_COUNT_OFFSET);
+      if ((rawBits & OffHeapStoredObject.MAGIC_MASK) != OffHeapStoredObject.MAGIC_NUMBER) {
+        String message = "It looks like off heap memory @" + Long.toHexString(address)
+            + " was already freed. rawBits=" + Integer.toHexString(rawBits) + " history="
+            + ReferenceCountHelper.getFreeRefCountInfo(address);
+        // debugLog(msg, true);
+        throw new IllegalStateException(message);
+      }
+
+      int currentCount = rawBits & OffHeapStoredObject.REF_COUNT_MASK;
+      if (currentCount == 0) {
+        throw new IllegalStateException("Memory has already been freed. history="
+            + ReferenceCountHelper.getFreeRefCountInfo(address));
+      }
+
+      if (currentCount == 1) {
+        // clear the use count, bits, and the delta size since it will be freed.
+        newCount = 0;
+        returnToAllocator = true;
+      } else {
+        newCount = rawBits - 1;
+      }
+    } while (!AddressableMemoryManager
+        .writeIntVolatile(address + OffHeapStoredObject.REF_COUNT_OFFSET, rawBits, newCount));
+
+    if (returnToAllocator) {
+      if (ReferenceCountHelper.trackReferenceCounts()) {
+        if (ReferenceCountHelper.trackFreedReferenceCounts()) {
+          ReferenceCountHelper.refCountChanged(address, true,
+              newCount & OffHeapStoredObject.REF_COUNT_MASK);
+        }
+        ReferenceCountHelper.freeRefCountInfo(address);
+      }
+
+      if (freeListManager == null) {
+        freeListManager = MemoryAllocatorImpl.getAllocator().getFreeListManager();
+      }
+
+      freeListManager.free(address);
+    } else {
+      if (ReferenceCountHelper.trackReferenceCounts()) {
+        ReferenceCountHelper.refCountChanged(address, true,
+            newCount & OffHeapStoredObject.REF_COUNT_MASK);
+      }
+    }
+  }
+}
diff --git a/geode-core/src/main/java/org/apache/geode/internal/util/concurrent/CustomEntryConcurrentHashMap.java b/geode-core/src/main/java/org/apache/geode/internal/util/concurrent/CustomEntryConcurrentHashMap.java
index 51fe988..7551661 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/util/concurrent/CustomEntryConcurrentHashMap.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/util/concurrent/CustomEntryConcurrentHashMap.java
@@ -61,7 +61,7 @@ import org.apache.geode.CancelException;
 import org.apache.geode.internal.cache.RegionEntry;
 import org.apache.geode.internal.cache.entries.OffHeapRegionEntry;
 import org.apache.geode.internal.cache.wan.GatewaySenderEventImpl;
-import org.apache.geode.internal.offheap.OffHeapRegionEntryHelper;
+import org.apache.geode.internal.offheap.OffHeapClearRequired;
 import org.apache.geode.internal.size.SingleObjectSizer;
 import org.apache.geode.internal.util.ArrayUtils;
 import org.apache.geode.logging.internal.executors.LoggingThread;
@@ -1020,7 +1020,7 @@ public class CustomEntryConcurrentHashMap<K, V> extends AbstractMap<K, V>
           // Geode changes BEGIN
           if (clearedEntries == null) {
             final boolean checkForGatewaySenderEvent =
-                OffHeapRegionEntryHelper.doesClearNeedToCheckForOffHeap();
+                OffHeapClearRequired.doesClearNeedToCheckForOffHeap();
             if (checkForGatewaySenderEvent) {
               clearedEntries = new ArrayList<HashEntry<?, ?>>();
             } else {
@@ -1805,7 +1805,7 @@ public class CustomEntryConcurrentHashMap<K, V> extends AbstractMap<K, V>
       if (entries != null) {
         final ArrayList<HashEntry<?, ?>> clearedEntries = entries;
         Runnable runnable;
-        if (OffHeapRegionEntryHelper.doesClearNeedToCheckForOffHeap()) {
+        if (OffHeapClearRequired.doesClearNeedToCheckForOffHeap()) {
           runnable = new Runnable() {
             @Override
             public void run() {
diff --git a/geode-core/src/test/java/org/apache/geode/internal/offheap/FreeListManagerTest.java b/geode-core/src/test/java/org/apache/geode/internal/offheap/FreeListManagerTest.java
index 2d83b39..4c4a6fd 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/offheap/FreeListManagerTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/offheap/FreeListManagerTest.java
@@ -113,7 +113,7 @@ public class FreeListManagerTest {
     int tinySize = 10;
 
     OffHeapStoredObject c = this.freeListManager.allocate(tinySize);
-    OffHeapStoredObject.release(c.getAddress(), this.freeListManager);
+    ReferenceCounter.release(c.getAddress(), this.freeListManager);
     c = this.freeListManager.allocate(tinySize);
 
     validateChunkSizes(c, tinySize);
@@ -125,7 +125,7 @@ public class FreeListManagerTest {
     int dataSize = 10;
 
     OffHeapStoredObject c = this.freeListManager.allocate(dataSize);
-    OffHeapStoredObject.release(c.getAddress(), this.freeListManager);
+    ReferenceCounter.release(c.getAddress(), this.freeListManager);
     this.freeListManager.allocate(dataSize);
     // free list will now be empty
     c = this.freeListManager.allocate(dataSize);
@@ -149,7 +149,7 @@ public class FreeListManagerTest {
     int dataSize = FreeListManager.MAX_TINY + 1;
 
     OffHeapStoredObject c = this.freeListManager.allocate(dataSize);
-    OffHeapStoredObject.release(c.getAddress(), this.freeListManager);
+    ReferenceCounter.release(c.getAddress(), this.freeListManager);
     this.freeListManager.allocate(dataSize);
     // free list will now be empty
     c = this.freeListManager.allocate(dataSize);
@@ -163,7 +163,7 @@ public class FreeListManagerTest {
     int dataSize = FreeListManager.MAX_TINY + 1 + 1024;
 
     OffHeapStoredObject c = this.freeListManager.allocate(dataSize);
-    OffHeapStoredObject.release(c.getAddress(), this.freeListManager);
+    ReferenceCounter.release(c.getAddress(), this.freeListManager);
     dataSize = FreeListManager.MAX_TINY + 1;
     c = this.freeListManager.allocate(dataSize);
 
@@ -183,7 +183,7 @@ public class FreeListManagerTest {
     int dataSize = 10;
 
     OffHeapStoredObject c = this.freeListManager.allocate(dataSize);
-    OffHeapStoredObject.release(c.getAddress(), this.freeListManager);
+    ReferenceCounter.release(c.getAddress(), this.freeListManager);
 
     assertThat(this.freeListManager.getFreeTinyMemory()).isEqualTo(computeExpectedSize(dataSize));
   }
@@ -194,12 +194,12 @@ public class FreeListManagerTest {
     int dataSize = 10;
 
     OffHeapStoredObject c = this.freeListManager.allocate(dataSize);
-    OffHeapStoredObject.release(c.getAddress(), this.freeListManager);
+    ReferenceCounter.release(c.getAddress(), this.freeListManager);
 
     int dataSize2 = 100;
 
     OffHeapStoredObject c2 = this.freeListManager.allocate(dataSize2);
-    OffHeapStoredObject.release(c2.getAddress(), this.freeListManager);
+    ReferenceCounter.release(c2.getAddress(), this.freeListManager);
 
     assertThat(this.freeListManager.getFreeTinyMemory())
         .isEqualTo(computeExpectedSize(dataSize) + computeExpectedSize(dataSize2));
@@ -218,7 +218,7 @@ public class FreeListManagerTest {
     int dataSize = FreeListManager.MAX_TINY + 1 + 1024;
 
     OffHeapStoredObject c = this.freeListManager.allocate(dataSize);
-    OffHeapStoredObject.release(c.getAddress(), this.freeListManager);
+    ReferenceCounter.release(c.getAddress(), this.freeListManager);
 
     assertThat(this.freeListManager.getFreeHugeMemory()).isEqualTo(computeExpectedSize(dataSize));
   }
@@ -287,7 +287,7 @@ public class FreeListManagerTest {
     chunks.add(this.freeListManager.allocate(DEFAULT_SLAB_SIZE / 2 - 8));
     chunks.add(this.freeListManager.allocate(SMALL_SLAB - 8 + 1));
     for (OffHeapStoredObject c : chunks) {
-      OffHeapStoredObject.release(c.getAddress(), this.freeListManager);
+      ReferenceCounter.release(c.getAddress(), this.freeListManager);
     }
     this.freeListManager.firstDefragmentation = false;
     assertThat(this.freeListManager.defragment(DEFAULT_SLAB_SIZE + 1)).isFalse();
@@ -314,7 +314,7 @@ public class FreeListManagerTest {
     chunks.add(this.freeListManager.allocate(DEFAULT_SLAB_SIZE / 2 - 8));
     chunks.add(this.freeListManager.allocate(SMALL_SLAB - 8 + 1));
     for (OffHeapStoredObject c : chunks) {
-      OffHeapStoredObject.release(c.getAddress(), this.freeListManager);
+      ReferenceCounter.release(c.getAddress(), this.freeListManager);
     }
 
     this.freeListManager.firstDefragmentation = false;
@@ -335,7 +335,7 @@ public class FreeListManagerTest {
     chunks.add(this.freeListManager.allocate(DEFAULT_SLAB_SIZE / 2 - 8));
     this.freeListManager.allocate(SMALL_SLAB - 8 + 1);
     for (OffHeapStoredObject c : chunks) {
-      OffHeapStoredObject.release(c.getAddress(), this.freeListManager);
+      ReferenceCounter.release(c.getAddress(), this.freeListManager);
     }
 
     this.freeListManager.firstDefragmentation = false;
@@ -355,7 +355,7 @@ public class FreeListManagerTest {
     chunks.add(this.freeListManager.allocate(DEFAULT_SLAB_SIZE / 2 - 8));
     this.freeListManager.allocate(SMALL_SLAB - 8 + 1);
     for (OffHeapStoredObject c : chunks) {
-      OffHeapStoredObject.release(c.getAddress(), this.freeListManager);
+      ReferenceCounter.release(c.getAddress(), this.freeListManager);
     }
 
     this.freeListManager.firstDefragmentation = false;
@@ -398,26 +398,26 @@ public class FreeListManagerTest {
     OffHeapStoredObject mediumChunk1 = freeListManager.allocate(128 - 8);
     OffHeapStoredObject mediumChunk2 = freeListManager.allocate(128 - 8);
 
-    OffHeapStoredObject.release(bigChunk.getAddress(), freeListManager);
+    ReferenceCounter.release(bigChunk.getAddress(), freeListManager);
     int s = chunksToFree.size();
     for (int i = s / 2; i >= 0; i--) {
       OffHeapStoredObject c = chunksToFree.get(i);
-      OffHeapStoredObject.release(c.getAddress(), freeListManager);
+      ReferenceCounter.release(c.getAddress(), freeListManager);
     }
     for (int i = (s / 2) + 1; i < s; i++) {
       OffHeapStoredObject c = chunksToFree.get(i);
-      OffHeapStoredObject.release(c.getAddress(), freeListManager);
+      ReferenceCounter.release(c.getAddress(), freeListManager);
     }
-    OffHeapStoredObject.release(c3.getAddress(), freeListManager);
-    OffHeapStoredObject.release(c1.getAddress(), freeListManager);
-    OffHeapStoredObject.release(c2.getAddress(), freeListManager);
-    OffHeapStoredObject.release(c4.getAddress(), freeListManager);
-    OffHeapStoredObject.release(mediumChunk1.getAddress(), freeListManager);
-    OffHeapStoredObject.release(mediumChunk2.getAddress(), freeListManager);
+    ReferenceCounter.release(c3.getAddress(), freeListManager);
+    ReferenceCounter.release(c1.getAddress(), freeListManager);
+    ReferenceCounter.release(c2.getAddress(), freeListManager);
+    ReferenceCounter.release(c4.getAddress(), freeListManager);
+    ReferenceCounter.release(mediumChunk1.getAddress(), freeListManager);
+    ReferenceCounter.release(mediumChunk2.getAddress(), freeListManager);
     this.freeListManager.firstDefragmentation = false;
     assertThat(freeListManager.defragment(DEFAULT_SLAB_SIZE - (ALLOCATE_COUNT * 32))).isFalse();
     for (int i = 0; i < ((256 * 2) / 96); i++) {
-      OffHeapStoredObject.release(chunksToFreeLater.get(i).getAddress(), freeListManager);
+      ReferenceCounter.release(chunksToFreeLater.get(i).getAddress(), freeListManager);
     }
     assertThat(freeListManager.defragment(DEFAULT_SLAB_SIZE - (ALLOCATE_COUNT * 32))).isTrue();
   }
@@ -431,9 +431,9 @@ public class FreeListManagerTest {
     OffHeapStoredObject bigChunk3 = freeListManager.allocate(slabSize / 3 - 8);
     this.freeListManager.firstDefragmentation = false;
     assertThat(freeListManager.defragment(1)).isFalse();
-    OffHeapStoredObject.release(bigChunk3.getAddress(), freeListManager);
-    OffHeapStoredObject.release(bigChunk2.getAddress(), freeListManager);
-    OffHeapStoredObject.release(bigChunk1.getAddress(), freeListManager);
+    ReferenceCounter.release(bigChunk3.getAddress(), freeListManager);
+    ReferenceCounter.release(bigChunk2.getAddress(), freeListManager);
+    ReferenceCounter.release(bigChunk1.getAddress(), freeListManager);
     assertThat(freeListManager.defragment(slabSize)).isTrue();
   }
 
@@ -442,7 +442,7 @@ public class FreeListManagerTest {
     setUpSingleSlabManager();
     Fragment originalFragment = this.freeListManager.getFragmentList().get(0);
     OffHeapStoredObject c = freeListManager.allocate(16);
-    OffHeapStoredObject.release(c.getAddress(), this.freeListManager);
+    ReferenceCounter.release(c.getAddress(), this.freeListManager);
     c = freeListManager.allocate(16);
     this.freeListManager.firstDefragmentation = false;
     assertThat(this.freeListManager.defragment(1)).isTrue();
@@ -714,7 +714,7 @@ public class FreeListManagerTest {
     this.freeListManager = createFreeListManager(ma, new Slab[] {chunk});
     OffHeapStoredObject c = this.freeListManager.allocate(24);
     OffHeapStoredObject c2 = this.freeListManager.allocate(24);
-    OffHeapStoredObject.release(c.getAddress(), this.freeListManager);
+    ReferenceCounter.release(c.getAddress(), this.freeListManager);
 
     List<MemoryBlock> ob = this.freeListManager.getOrderedBlocks();
     assertThat(ob).hasSize(3);
@@ -733,7 +733,7 @@ public class FreeListManagerTest {
     Slab chunk = new SlabImpl(96);
     this.freeListManager = createFreeListManager(ma, new Slab[] {chunk});
     OffHeapStoredObject c = this.freeListManager.allocate(24);
-    OffHeapStoredObject.release(c.getAddress(), this.freeListManager);
+    ReferenceCounter.release(c.getAddress(), this.freeListManager);
     List<MemoryBlock> ob = this.freeListManager.getAllocatedBlocks();
     assertThat(ob).hasSize(0);
   }
@@ -773,8 +773,8 @@ public class FreeListManagerTest {
     this.freeListManager = createFreeListManager(ma, new Slab[] {chunk, chunk2});
     OffHeapStoredObject c = this.freeListManager.allocate(24);
     OffHeapStoredObject c2 = this.freeListManager.allocate(1024 * 1024);
-    OffHeapStoredObject.release(c.getAddress(), this.freeListManager);
-    OffHeapStoredObject.release(c2.getAddress(), this.freeListManager);
+    ReferenceCounter.release(c.getAddress(), this.freeListManager);
+    ReferenceCounter.release(c2.getAddress(), this.freeListManager);
 
     Logger lw = mock(Logger.class);
     this.freeListManager.logOffHeapState(lw, 1024);
diff --git a/geode-core/src/test/java/org/apache/geode/internal/offheap/OffHeapRegionEntryHelperInstanceTest.java b/geode-core/src/test/java/org/apache/geode/internal/offheap/OffHeapRegionEntryHelperInstanceTest.java
new file mode 100644
index 0000000..d32cb8b
--- /dev/null
+++ b/geode-core/src/test/java/org/apache/geode/internal/offheap/OffHeapRegionEntryHelperInstanceTest.java
@@ -0,0 +1,907 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.offheap;
+
+import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.COMPRESSED_BIT;
+import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.DESTROYED_ADDRESS;
+import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.END_OF_STREAM_ADDRESS;
+import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.INVALID_ADDRESS;
+import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.LOCAL_INVALID_ADDRESS;
+import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.NOT_AVAILABLE_ADDRESS;
+import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.NULL_ADDRESS;
+import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.REMOVED_PHASE1_ADDRESS;
+import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.REMOVED_PHASE2_ADDRESS;
+import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.SERIALIZED_BIT;
+import static org.apache.geode.internal.offheap.OffHeapRegionEntryHelperInstance.TOMBSTONE_ADDRESS;
+import static org.apache.geode.util.internal.UncheckedUtils.uncheckedCast;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.catchThrowable;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.when;
+
+import java.nio.ByteBuffer;
+import java.util.function.Function;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.geode.compression.Compressor;
+import org.apache.geode.internal.cache.CachePerfStats;
+import org.apache.geode.internal.cache.CachedDeserializable;
+import org.apache.geode.internal.cache.DiskId;
+import org.apache.geode.internal.cache.EntryEventImpl;
+import org.apache.geode.internal.cache.RegionEntryContext;
+import org.apache.geode.internal.cache.Token;
+import org.apache.geode.internal.cache.VMCachedDeserializable;
+import org.apache.geode.internal.cache.entries.DiskEntry;
+import org.apache.geode.internal.cache.entries.OffHeapRegionEntry;
+import org.apache.geode.internal.cache.entries.VersionedStatsDiskRegionEntryOffHeap;
+import org.apache.geode.internal.serialization.DSCODE;
+
+public class OffHeapRegionEntryHelperInstanceTest {
+
+  private static final long VALUE_IS_NOT_ENCODABLE = 0L;
+
+  private MemoryAllocator memoryAllocator;
+  private ReferenceCounterInstance referenceCounter;
+  private OffHeapStoredObject offHeapStoredObject;
+
+  private OffHeapRegionEntryHelperInstance offHeapRegionEntryHelperInstance;
+
+  @Before
+  public void setUp() {
+    OutOfOffHeapMemoryListener listener = mock(OutOfOffHeapMemoryListener.class);
+    OffHeapMemoryStats stats = mock(OffHeapMemoryStats.class);
+    Function<Long, OffHeapStoredObject> offHeapStoredObjectFactory =
+        uncheckedCast(mock(Function.class));
+    offHeapStoredObject = mock(OffHeapStoredObject.class);
+    referenceCounter = mock(ReferenceCounterInstance.class);
+
+    when(offHeapStoredObjectFactory.apply(anyLong()))
+        .thenReturn(offHeapStoredObject);
+
+    memoryAllocator =
+        MemoryAllocatorImpl.create(listener, stats, 1, OffHeapStorage.MIN_SLAB_SIZE,
+            OffHeapStorage.MIN_SLAB_SIZE);
+
+    offHeapRegionEntryHelperInstance =
+        spy(new OffHeapRegionEntryHelperInstance(ohAddress -> offHeapStoredObject,
+            referenceCounter));
+  }
+
+  @After
+  public void tearDown() {
+    MemoryAllocatorImpl.freeOffHeapMemory();
+  }
+
+  @Test
+  public void encodeDataAsAddressShouldReturnZeroIfValueIsGreaterThanSevenBytes() {
+    byte[] valueInBytes =
+        ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(Long.MAX_VALUE).array();
+
+    assertThat(valueInBytes.length)
+        .isGreaterThanOrEqualTo(OffHeapRegionEntryHelperInstance.MAX_LENGTH_FOR_DATA_AS_ADDRESS);
+
+    long encodedAddress =
+        offHeapRegionEntryHelperInstance.encodeDataAsAddress(valueInBytes, false, false);
+
+    assertThat(encodedAddress)
+        .isEqualTo(VALUE_IS_NOT_ENCODABLE);
+  }
+
+  @Test
+  public void encodeDataAsAddressShouldEncodeLongIfItsSerializedAndIfItsNotTooBig() {
+    byte[] valueInBytes = EntryEventImpl.serialize(0L);
+    boolean isSerialized = true;
+    boolean isCompressed = false;
+
+    long encodedAddress =
+        offHeapRegionEntryHelperInstance.encodeDataAsAddress(valueInBytes, isSerialized,
+            isCompressed);
+
+    long expectedEncodedAddress = 123L;
+    assertThat(encodedAddress)
+        .isEqualTo(expectedEncodedAddress);
+
+    assertSerializedAndCompressedBits(encodedAddress, isSerialized, isCompressed);
+  }
+
+  @Test
+  public void encodeDataAsAddressShouldReturnZeroIfValueIsLongAndItIsSerializedAndBig() {
+    byte[] valueInBytes = EntryEventImpl.serialize(Long.MAX_VALUE);
+
+    long encodedAddress =
+        offHeapRegionEntryHelperInstance.encodeDataAsAddress(valueInBytes, true,
+            false);
+
+    assertThat(encodedAddress)
+        .isEqualTo(VALUE_IS_NOT_ENCODABLE);
+  }
+
+  @Test
+  public void encodeDataAsAddressShouldReturnZeroIfValueIsLargerThanEightBytesAndNotLong() {
+    byte[] someValue = new byte[8];
+    someValue[0] = DSCODE.CLASS.toByte();
+
+    long encodedAddress =
+        offHeapRegionEntryHelperInstance.encodeDataAsAddress(someValue, true, false);
+
+    assertThat(encodedAddress)
+        .isEqualTo(VALUE_IS_NOT_ENCODABLE);
+  }
+
+  @Test
+  public void encodeDataAsAddressShouldReturnValidAddressIfValueIsLesserThanSevenBytes() {
+    byte[] valueInBytes =
+        ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
+    boolean isSerialized = false;
+    boolean isCompressed = false;
+
+    long encodedAddress =
+        offHeapRegionEntryHelperInstance.encodeDataAsAddress(valueInBytes, isSerialized,
+            isCompressed);
+
+    long expectedAddress = 549755813697L;
+    assertThat(encodedAddress)
+        .isEqualTo(expectedAddress);
+
+    assertSerializedAndCompressedBits(encodedAddress, isSerialized, isCompressed);
+  }
+
+  @Test
+  public void encodeDataAsAddressShouldSetSerializedBitIfSerialized() {
+    byte[] valueInBytes = EntryEventImpl.serialize(Integer.MAX_VALUE);
+    boolean isSerialized = true;
+    boolean isCompressed = false;
+
+    long encodedAddress =
+        offHeapRegionEntryHelperInstance.encodeDataAsAddress(valueInBytes, isSerialized,
+            isCompressed);
+
+    long expectedAddress = 63221918596947L;
+    assertThat(expectedAddress)
+        .isEqualTo(encodedAddress);
+
+    assertSerializedAndCompressedBits(encodedAddress, isSerialized, isCompressed);
+  }
+
+  @Test
+  public void encodeDataAsAddressShouldSetSerializedBitIfCompressed() {
+    byte[] valueInBytes =
+        ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
+    boolean isSerialized = false;
+    boolean isCompressed = true;
+
+    long encodedAddress =
+        offHeapRegionEntryHelperInstance.encodeDataAsAddress(valueInBytes, isSerialized,
+            isCompressed);
+
+    long expectedAddress = 549755813701L;
+    assertThat(encodedAddress)
+        .isEqualTo(expectedAddress);
+
+    assertSerializedAndCompressedBits(encodedAddress, isSerialized, isCompressed);
+  }
+
+  @Test
+  public void encodeDataAsAddressShouldSetBothSerializedAndCompressedBitsIfSerializedAndCompressed() {
+    byte[] valueInBytes = EntryEventImpl.serialize(Integer.MAX_VALUE);
+    boolean isSerialized = true;
+    boolean isCompressed = true;
+
+    long encodedAddress =
+        offHeapRegionEntryHelperInstance.encodeDataAsAddress(valueInBytes, isSerialized,
+            isCompressed);
+
+    long expectedAddress = 63221918596951L;
+    assertThat(expectedAddress)
+        .isEqualTo(encodedAddress);
+
+    assertSerializedAndCompressedBits(encodedAddress, isSerialized, isCompressed);
+  }
+
+  @Test
+  public void decodeUncompressedAddressToBytesShouldReturnActualBytes() {
+    long encodedAddress = 549755813697L;
+    int value = Integer.MAX_VALUE;
+
+    byte[] actual =
+        offHeapRegionEntryHelperInstance.decodeUncompressedAddressToBytes(encodedAddress);
+
+    byte[] expectedValue = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(value).array();
+    assertThat(actual)
+        .isEqualTo(expectedValue);
+  }
+
+  @Test
+  public void decodeUncompressedAddressToBytesShouldDecodeLongIfItsSerializedAndIfItsNotTooBig() {
+    byte[] actual = offHeapRegionEntryHelperInstance.decodeUncompressedAddressToBytes(123L);
+
+    byte[] expectedValue = EntryEventImpl.serialize(0L);
+    assertThat(actual)
+        .isEqualTo(expectedValue);
+  }
+
+  @Test
+  public void decodeUncompressedAddressToBytesWithCompressedAddressShouldThrowException() {
+    long encodedAddress = 549755813703L;
+
+    Throwable thrown = catchThrowable(() -> {
+      offHeapRegionEntryHelperInstance.decodeUncompressedAddressToBytes(encodedAddress);
+    });
+
+    assertThat(thrown)
+        .isInstanceOf(AssertionError.class);
+  }
+
+  @Test
+  public void decodeCompressedDataAsAddressToRawBytes() {
+    long encodedAddress = 549755813703L;
+    byte[] expected = new byte[] {127, -1, -1, -1};
+
+    byte[] bytes = offHeapRegionEntryHelperInstance.decodeAddressToRawBytes(encodedAddress);
+
+    assertThat(bytes)
+        .isEqualTo(expected);
+  }
+
+  @Test
+  public void encodedAddressShouldBeDecodableEvenIfValueIsSerialized() {
+    int value = Integer.MAX_VALUE;
+    byte[] serializedValue = EntryEventImpl.serialize(value);
+
+    long encodedAddress =
+        offHeapRegionEntryHelperInstance.encodeDataAsAddress(serializedValue, true, false);
+
+    int actualValue =
+        (int) offHeapRegionEntryHelperInstance.decodeAddressToObject(encodedAddress);
+
+    assertThat(actualValue)
+        .isEqualTo(value);
+  }
+
+  @Test
+  public void encodedAddressShouldBeDecodableEvenIfValueIsUnserialized() {
+    int value = Integer.MAX_VALUE;
+    byte[] unSerializedValue = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(value).array();
+    long encodedAddress =
+        offHeapRegionEntryHelperInstance.encodeDataAsAddress(unSerializedValue, false, false);
+
+    byte[] actualValue =
+        (byte[]) offHeapRegionEntryHelperInstance.decodeAddressToObject(encodedAddress);
+
+    assertThat(actualValue)
+        .isEqualTo(unSerializedValue);
+  }
+
+  @Test
+  public void isSerializedShouldReturnTrueIfSerialized() {
+    assertThat(offHeapRegionEntryHelperInstance.isSerialized(1000010L)).isTrue();
+  }
+
+  @Test
+  public void isSerializedShouldReturnFalseIfNotSerialized() {
+    assertThat(offHeapRegionEntryHelperInstance.isSerialized(1000000L)).isFalse();
+  }
+
+  @Test
+  public void isCompressedShouldReturnTrueIfCompressed() {
+    assertThat(offHeapRegionEntryHelperInstance.isCompressed(1000100L)).isTrue();
+  }
+
+  @Test
+  public void isCompressedShouldReturnFalseIfNotCompressed() {
+    assertThat(offHeapRegionEntryHelperInstance.isCompressed(1000000L)).isFalse();
+  }
+
+  @Test
+  public void isOffHeapShouldReturnTrueIfAddressIsOnOffHeap() {
+    OffHeapStoredObject value = createChunk(Long.MAX_VALUE);
+
+    assertThat(offHeapRegionEntryHelperInstance.isOffHeap(value.getAddress())).isTrue();
+  }
+
+  @Test
+  public void isOffHeapShouldReturnFalseIfAddressIsAnEncodedAddress() {
+    byte[] data = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
+
+    long address = offHeapRegionEntryHelperInstance.encodeDataAsAddress(data, false, false);
+
+    assertThat(offHeapRegionEntryHelperInstance.isOffHeap(address)).isFalse();
+  }
+
+  @Test
+  public void isOffHeapShouldReturnFalseForAnyTokenAddress() {
+    assertThat(offHeapRegionEntryHelperInstance.isOffHeap(NULL_ADDRESS)).isFalse();
+    assertThat(offHeapRegionEntryHelperInstance.isOffHeap(INVALID_ADDRESS)).isFalse();
+    assertThat(offHeapRegionEntryHelperInstance.isOffHeap(LOCAL_INVALID_ADDRESS)).isFalse();
+    assertThat(offHeapRegionEntryHelperInstance.isOffHeap(DESTROYED_ADDRESS)).isFalse();
+    assertThat(offHeapRegionEntryHelperInstance.isOffHeap(REMOVED_PHASE1_ADDRESS)).isFalse();
+    assertThat(offHeapRegionEntryHelperInstance.isOffHeap(REMOVED_PHASE2_ADDRESS)).isFalse();
+    assertThat(offHeapRegionEntryHelperInstance.isOffHeap(END_OF_STREAM_ADDRESS)).isFalse();
+    assertThat(offHeapRegionEntryHelperInstance.isOffHeap(NOT_AVAILABLE_ADDRESS)).isFalse();
+    assertThat(offHeapRegionEntryHelperInstance.isOffHeap(TOMBSTONE_ADDRESS)).isFalse();
+  }
+
+  @Test
+  public void setValueShouldChangeTheRegionEntryAddressToNewAddress() {
+    // mock region entry
+    OffHeapRegionEntry regionEntry = mock(OffHeapRegionEntry.class);
+
+    // some old address
+    long oldAddress = 1L;
+
+    // testing when the newValue is a chunk
+    OffHeapStoredObject newValue = createChunk(Long.MAX_VALUE);
+    // mock region entry methods required for test
+    when(regionEntry.getAddress()).thenReturn(oldAddress);
+    when(regionEntry.setAddress(oldAddress, newValue.getAddress())).thenReturn(Boolean.TRUE);
+
+    // invoke the method under test
+    offHeapRegionEntryHelperInstance.setValue(regionEntry, newValue);
+
+    // verify oldAddress is replaced with newAddress
+    verify(regionEntry).setAddress(oldAddress, newValue.getAddress());
+    // resetting the spy in-order to re-use
+    reset(regionEntry);
+
+    // testing when the newValue is DataAsAddress
+    TinyStoredObject newAddress1 = new TinyStoredObject(2L);
+    // mock region entry methods required for test
+    when(regionEntry.getAddress()).thenReturn(oldAddress);
+    when(regionEntry.setAddress(oldAddress, newAddress1.getAddress())).thenReturn(true);
+    offHeapRegionEntryHelperInstance.setValue(regionEntry, newAddress1);
+
+    // verify oldAddress is replaced with newAddress
+    verify(regionEntry).setAddress(oldAddress, newAddress1.getAddress());
+    reset(regionEntry);
+
+    // Testing when newValue is Token Objects
+
+    // mock region entry methods required for test
+    when(regionEntry.getAddress()).thenReturn(oldAddress);
+    when(regionEntry.setAddress(oldAddress, NULL_ADDRESS)).thenReturn(true);
+    offHeapRegionEntryHelperInstance.setValue(regionEntry, null);
+
+    // verify oldAddress is replaced with newAddress
+    verify(regionEntry).setAddress(oldAddress, NULL_ADDRESS);
+    reset(regionEntry);
+
+    // mock region entry methods required for test
+    when(regionEntry.getAddress()).thenReturn(oldAddress);
+    when(regionEntry.setAddress(oldAddress, INVALID_ADDRESS)).thenReturn(true);
+    offHeapRegionEntryHelperInstance.setValue(regionEntry, Token.INVALID);
+
+    // verify oldAddress is replaced with newAddress
+    verify(regionEntry).setAddress(oldAddress, INVALID_ADDRESS);
+    reset(regionEntry);
+
+    // mock region entry methods required for test
+    when(regionEntry.getAddress()).thenReturn(oldAddress);
+    when(regionEntry.setAddress(oldAddress, LOCAL_INVALID_ADDRESS)).thenReturn(true);
+    offHeapRegionEntryHelperInstance.setValue(regionEntry, Token.LOCAL_INVALID);
+
+    // verify oldAddress is replaced with newAddress
+    verify(regionEntry).setAddress(oldAddress, LOCAL_INVALID_ADDRESS);
+    reset(regionEntry);
+
+    // mock region entry methods required for test
+    when(regionEntry.getAddress()).thenReturn(oldAddress);
+    when(regionEntry.setAddress(oldAddress, DESTROYED_ADDRESS)).thenReturn(true);
+    offHeapRegionEntryHelperInstance.setValue(regionEntry, Token.DESTROYED);
+
+    // verify oldAddress is replaced with newAddress
+    verify(regionEntry).setAddress(oldAddress, DESTROYED_ADDRESS);
+    reset(regionEntry);
+
+    // mock region entry methods required for test
+    when(regionEntry.getAddress()).thenReturn(oldAddress);
+    when(regionEntry.setAddress(oldAddress, REMOVED_PHASE1_ADDRESS)).thenReturn(true);
+    offHeapRegionEntryHelperInstance.setValue(regionEntry, Token.REMOVED_PHASE1);
+
+    // verify oldAddress is replaced with newAddress
+    verify(regionEntry).setAddress(oldAddress, REMOVED_PHASE1_ADDRESS);
+    reset(regionEntry);
+
+    // mock region entry methods required for test
+    when(regionEntry.getAddress()).thenReturn(oldAddress);
+    when(regionEntry.setAddress(oldAddress, REMOVED_PHASE2_ADDRESS)).thenReturn(true);
+    offHeapRegionEntryHelperInstance.setValue(regionEntry, Token.REMOVED_PHASE2);
+
+    // verify oldAddress is replaced with newAddress
+    verify(regionEntry).setAddress(oldAddress, REMOVED_PHASE2_ADDRESS);
+    reset(regionEntry);
+
+    // mock region entry methods required for test
+    when(regionEntry.getAddress()).thenReturn(oldAddress);
+    when(regionEntry.setAddress(oldAddress, END_OF_STREAM_ADDRESS)).thenReturn(true);
+    offHeapRegionEntryHelperInstance.setValue(regionEntry, Token.END_OF_STREAM);
+
+    // verify oldAddress is replaced with newAddress
+    verify(regionEntry).setAddress(oldAddress, END_OF_STREAM_ADDRESS);
+    reset(regionEntry);
+
+    // mock region entry methods required for test
+    when(regionEntry.getAddress()).thenReturn(oldAddress);
+    when(regionEntry.setAddress(oldAddress, NOT_AVAILABLE_ADDRESS)).thenReturn(true);
+    offHeapRegionEntryHelperInstance.setValue(regionEntry, Token.NOT_AVAILABLE);
+
+    // verify oldAddress is replaced with newAddress
+    verify(regionEntry).setAddress(oldAddress, NOT_AVAILABLE_ADDRESS);
+    reset(regionEntry);
+
+    // mock region entry methods required for test
+    when(regionEntry.getAddress()).thenReturn(oldAddress);
+    when(regionEntry.setAddress(oldAddress, TOMBSTONE_ADDRESS)).thenReturn(true);
+    offHeapRegionEntryHelperInstance.setValue(regionEntry, Token.TOMBSTONE);
+
+    // verify oldAddress is replaced with newAddress
+    verify(regionEntry).setAddress(oldAddress, TOMBSTONE_ADDRESS);
+  }
+
+  @Test
+  public void setValueShouldChangeTheRegionEntryAddressToNewAddressAndReleaseOldValueIfItsOnOffHeap() {
+    OffHeapStoredObject oldValue = createChunk(Long.MAX_VALUE);
+    OffHeapStoredObject newValue = createChunk(Long.MAX_VALUE - 1);
+    OffHeapRegionEntry regionEntry = mock(OffHeapRegionEntry.class);
+
+    // mock Chunk static methods - in-order to verify that release is called
+    doNothing().when(offHeapStoredObject).release();
+
+    // mock region entry methods required for test
+    when(regionEntry.getAddress())
+        .thenReturn(oldValue.getAddress());
+    when(regionEntry.setAddress(oldValue.getAddress(), newValue.getAddress()))
+        .thenReturn(Boolean.TRUE);
+
+    // invoke the method under test
+    offHeapRegionEntryHelperInstance.setValue(regionEntry, newValue);
+
+    // verify oldAddress is changed to newAddress
+    verify(regionEntry)
+        .setAddress(oldValue.getAddress(), newValue.getAddress());
+
+    // verify oldAddress is released
+    verify(referenceCounter)
+        .release(oldValue.getAddress());
+  }
+
+  @Test
+  public void setValueShouldChangeTheRegionEntryAddressToNewAddressAndDoesNothingIfOldAddressIsAnEncodedAddress() {
+    byte[] oldData =
+        ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
+    byte[] newData =
+        ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE - 1).array();
+    long oldAddress = offHeapRegionEntryHelperInstance.encodeDataAsAddress(oldData, false, false);
+    StoredObject newAddress = new TinyStoredObject(
+        offHeapRegionEntryHelperInstance.encodeDataAsAddress(newData, false, false));
+    OffHeapRegionEntry regionEntry = mock(OffHeapRegionEntry.class);
+
+    // mock region entry methods required for test
+    when(regionEntry.getAddress())
+        .thenReturn(oldAddress);
+    when(regionEntry.setAddress(oldAddress, newAddress.getAddress()))
+        .thenReturn(true);
+
+    // invoke the method under test
+    offHeapRegionEntryHelperInstance.setValue(regionEntry, newAddress);
+
+    // verify oldAddress is changed to newAddress
+    verify(regionEntry)
+        .setAddress(oldAddress, newAddress.getAddress());
+
+    // verify that release is never called as the old address is not on offheap
+    verifyNoInteractions(offHeapStoredObject);
+  }
+
+  @Test
+  public void setValueShouldChangeTheRegionEntryAddressToNewAddressAndDoesNothingIfOldAddressIsATokenAddress() {
+    long oldAddress = REMOVED_PHASE1_ADDRESS;
+    long newAddress = REMOVED_PHASE2_ADDRESS;
+    OffHeapRegionEntry regionEntry = mock(OffHeapRegionEntry.class);
+
+    when(regionEntry.getAddress())
+        .thenReturn(oldAddress);
+    when(regionEntry.setAddress(oldAddress, newAddress))
+        .thenReturn(true);
+
+    // invoke the method under test
+    offHeapRegionEntryHelperInstance.setValue(regionEntry, Token.REMOVED_PHASE2);
+
+    // verify oldAddress is changed to newAddress
+    verify(regionEntry)
+        .setAddress(oldAddress, newAddress);
+  }
+
+  @Test
+  public void setValueShouldThrowIllegalExceptionIfNewValueCannotBeConvertedToAddress() {
+    OffHeapRegionEntry regionEntry = mock(OffHeapRegionEntry.class);
+
+    when(regionEntry.getAddress())
+        .thenReturn(1L);
+
+    // invoke the method under test with some object other than Chunk/DataAsAddress/Token
+    Throwable thrown = catchThrowable(() -> {
+      offHeapRegionEntryHelperInstance.setValue(regionEntry, new Object());
+    });
+
+    assertThat(thrown)
+        .isInstanceOf(IllegalStateException.class);
+  }
+
+  @Test
+  public void getValueAsTokenShouldReturnNotATokenIfValueIsOnOffHeap() {
+    OffHeapStoredObject chunk = createChunk(Long.MAX_VALUE);
+    OffHeapRegionEntry regionEntry = mock(OffHeapRegionEntry.class);
+
+    when(regionEntry.getAddress())
+        .thenReturn(chunk.getAddress());
+
+    Token token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
+
+    assertThat(token)
+        .isEqualTo(Token.NOT_A_TOKEN);
+  }
+
+  @Test
+  public void getValueAsTokenShouldReturnNotATokenIfValueIsEncoded() {
+    byte[] data = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
+    long address = offHeapRegionEntryHelperInstance.encodeDataAsAddress(data, false, false);
+    OffHeapRegionEntry regionEntry = mock(OffHeapRegionEntry.class);
+
+    when(regionEntry.getAddress())
+        .thenReturn(address);
+
+    Token token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
+
+    assertThat(token)
+        .isEqualTo(Token.NOT_A_TOKEN);
+  }
+
+  @Test
+  public void getValueAsTokenShouldReturnAValidToken() {
+    OffHeapRegionEntry regionEntry = mock(OffHeapRegionEntry.class);
+
+    when(regionEntry.getAddress()).thenReturn(NULL_ADDRESS);
+    Token token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
+    assertThat(token).isNull();
+
+    // mock region entry methods required for test
+    when(regionEntry.getAddress()).thenReturn(INVALID_ADDRESS);
+    token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
+    assertThat(token).isEqualTo(Token.INVALID);
+
+    // mock region entry methods required for test
+    when(regionEntry.getAddress()).thenReturn(LOCAL_INVALID_ADDRESS);
+    token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
+    assertThat(token).isEqualTo(Token.LOCAL_INVALID);
+
+    // mock region entry methods required for test
+    when(regionEntry.getAddress()).thenReturn(DESTROYED_ADDRESS);
+    token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
+    assertThat(token).isEqualTo(Token.DESTROYED);
+
+    // mock region entry methods required for test
+    when(regionEntry.getAddress()).thenReturn(REMOVED_PHASE1_ADDRESS);
+    token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
+    assertThat(token).isEqualTo(Token.REMOVED_PHASE1);
+
+    // mock region entry methods required for test
+    when(regionEntry.getAddress()).thenReturn(REMOVED_PHASE2_ADDRESS);
+    token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
+    assertThat(token).isEqualTo(Token.REMOVED_PHASE2);
+
+    // mock region entry methods required for test
+    when(regionEntry.getAddress()).thenReturn(END_OF_STREAM_ADDRESS);
+    token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
+    assertThat(token).isEqualTo(Token.END_OF_STREAM);
+
+    // mock region entry methods required for test
+    when(regionEntry.getAddress()).thenReturn(NOT_AVAILABLE_ADDRESS);
+    token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
+    assertThat(token).isEqualTo(Token.NOT_AVAILABLE);
+
+    // mock region entry methods required for test
+    when(regionEntry.getAddress()).thenReturn(TOMBSTONE_ADDRESS);
+    token = offHeapRegionEntryHelperInstance.getValueAsToken(regionEntry);
+    assertThat(token).isEqualTo(Token.TOMBSTONE);
+  }
+
+  @Test
+  public void addressToObjectShouldReturnValueFromChunk() {
+    OffHeapRegionEntryHelperInstance offHeapRegionEntryHelperInstance =
+        new OffHeapRegionEntryHelperInstance();
+    OffHeapStoredObject expected = createChunk(Long.MAX_VALUE);
+
+    Object actual =
+        offHeapRegionEntryHelperInstance.addressToObject(expected.getAddress(), false, null);
+
+    assertThat(actual)
+        .isInstanceOf(OffHeapStoredObject.class)
+        .isEqualTo(expected);
+  }
+
+  @Test
+  public void addressToObjectShouldReturnCachedDeserializableFromChunkIfAskedToDecompress() {
+    byte[] data = EntryEventImpl.serialize(Long.MAX_VALUE);
+    RegionEntryContext regionContext = mock(RegionEntryContext.class);
+    CachePerfStats cacheStats = mock(CachePerfStats.class);
+    Compressor compressor = mock(Compressor.class);
+
+    when(regionContext.getCompressor())
+        .thenReturn(compressor);
+    when(compressor.decompress(data))
+        .thenReturn(data);
+    when(regionContext.getCachePerfStats())
+        .thenReturn(cacheStats);
+    when(cacheStats.startDecompression())
+        .thenReturn(10000L);
+
+    MemoryBlock chunk =
+        (MemoryBlock) memoryAllocator.allocateAndInitialize(data, true, true);
+    offHeapRegionEntryHelperInstance =
+        spy(new OffHeapRegionEntryHelperInstance(OffHeapStoredObject::new, referenceCounter));
+
+    Object actual =
+        offHeapRegionEntryHelperInstance.addressToObject(chunk.getAddress(), true, regionContext);
+
+    assertThat(actual)
+        .isInstanceOf(VMCachedDeserializable.class);
+
+    long actualValue = (long) ((CachedDeserializable) actual).getDeserializedForReading();
+
+    assertThat(actualValue)
+        .isEqualTo(Long.MAX_VALUE);
+  }
+
+  @Test
+  public void addressToObjectShouldReturnDecompressedValueFromChunkIfAskedToDecompress() {
+    byte[] data = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(Long.MAX_VALUE).array();
+    RegionEntryContext regionContext = mock(RegionEntryContext.class);
+    CachePerfStats cacheStats = mock(CachePerfStats.class);
+    Compressor compressor = mock(Compressor.class);
+
+    when(regionContext.getCompressor())
+        .thenReturn(compressor);
+    when(compressor.decompress(data))
+        .thenReturn(data);
+    when(regionContext.getCachePerfStats())
+        .thenReturn(cacheStats);
+    when(cacheStats.startDecompression())
+        .thenReturn(10000L);
+
+    MemoryBlock chunk = (MemoryBlock) memoryAllocator.allocateAndInitialize(data, false, true);
+    offHeapRegionEntryHelperInstance =
+        spy(new OffHeapRegionEntryHelperInstance(OffHeapStoredObject::new, referenceCounter));
+
+    Object actual =
+        offHeapRegionEntryHelperInstance.addressToObject(chunk.getAddress(), true, regionContext);
+
+    assertThat(actual)
+        .isInstanceOf(byte[].class)
+        .isEqualTo(data);
+  }
+
+  @Test
+  public void addressToObjectShouldReturnValueFromDataAsAddress() {
+    byte[] data = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
+    long address = offHeapRegionEntryHelperInstance.encodeDataAsAddress(data, false, false);
+
+    Object actual = offHeapRegionEntryHelperInstance.addressToObject(address, false, null);
+
+    TinyStoredObject expected = new TinyStoredObject(address);
+
+    assertThat(actual)
+        .isInstanceOf(TinyStoredObject.class)
+        .isEqualTo(expected);
+  }
+
+  @Test
+  public void addressToObjectShouldReturnCachedDeserializableFromSerializedDataAsAddressIfAskedToDecompress() {
+    byte[] data = EntryEventImpl.serialize(Integer.MAX_VALUE);
+    RegionEntryContext regionContext = mock(RegionEntryContext.class);
+    CachePerfStats cacheStats = mock(CachePerfStats.class);
+    Compressor compressor = mock(Compressor.class);
+
+    when(regionContext.getCompressor())
+        .thenReturn(compressor);
+    when(compressor.decompress(data))
+        .thenReturn(data);
+    when(regionContext.getCachePerfStats())
+        .thenReturn(cacheStats);
+    when(cacheStats.startDecompression())
+        .thenReturn(10000L);
+
+    long address =
+        offHeapRegionEntryHelperInstance.encodeDataAsAddress(data, true, true);
+
+    Object actual = offHeapRegionEntryHelperInstance.addressToObject(address, true, regionContext);
+
+    assertThat(actual)
+        .isInstanceOf(VMCachedDeserializable.class);
+
+    int actualValue = (int) ((CachedDeserializable) actual).getDeserializedForReading();
+
+    assertThat(actualValue)
+        .isEqualTo(Integer.MAX_VALUE);
+  }
+
+  @Test
+  public void addressToObjectShouldReturnDecompressedValueFromDataAsAddressIfAskedToDecompress() {
+    byte[] data = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
+    RegionEntryContext regionContext = mock(RegionEntryContext.class);
+    CachePerfStats cacheStats = mock(CachePerfStats.class);
+    Compressor compressor = mock(Compressor.class);
+
+    when(regionContext.getCompressor())
+        .thenReturn(compressor);
+    when(compressor.decompress(data))
+        .thenReturn(data);
+    when(regionContext.getCachePerfStats())
+        .thenReturn(cacheStats);
+    when(cacheStats.startDecompression())
+        .thenReturn(10000L);
+
+    long address =
+        offHeapRegionEntryHelperInstance.encodeDataAsAddress(data, false, true);
+
+    Object actual = offHeapRegionEntryHelperInstance.addressToObject(address, true, regionContext);
+
+    assertThat(actual)
+        .isInstanceOf(byte[].class)
+        .isEqualTo(data);
+  }
+
+  @Test
+  public void addressToObjectShouldReturnToken() {
+    Token token = (Token) offHeapRegionEntryHelperInstance
+        .addressToObject(NULL_ADDRESS, false, null);
+    assertThat(token).isNull();
+
+    token = (Token) offHeapRegionEntryHelperInstance
+        .addressToObject(INVALID_ADDRESS, false, null);
+    assertThat(token).isEqualTo(Token.INVALID);
+
+    token = (Token) offHeapRegionEntryHelperInstance
+        .addressToObject(LOCAL_INVALID_ADDRESS, false, null);
+    assertThat(token).isEqualTo(Token.LOCAL_INVALID);
+
+    token = (Token) offHeapRegionEntryHelperInstance
+        .addressToObject(DESTROYED_ADDRESS, false, null);
+    assertThat(token).isEqualTo(Token.DESTROYED);
+
+    token = (Token) offHeapRegionEntryHelperInstance
+        .addressToObject(REMOVED_PHASE1_ADDRESS, false, null);
+    assertThat(token).isEqualTo(Token.REMOVED_PHASE1);
+
+    token = (Token) offHeapRegionEntryHelperInstance
+        .addressToObject(REMOVED_PHASE2_ADDRESS, false, null);
+    assertThat(token).isEqualTo(Token.REMOVED_PHASE2);
+
+    token = (Token) offHeapRegionEntryHelperInstance
+        .addressToObject(END_OF_STREAM_ADDRESS, false, null);
+    assertThat(token).isEqualTo(Token.END_OF_STREAM);
+
+    token = (Token) offHeapRegionEntryHelperInstance
+        .addressToObject(NOT_AVAILABLE_ADDRESS, false, null);
+    assertThat(token).isEqualTo(Token.NOT_AVAILABLE);
+
+    token = (Token) offHeapRegionEntryHelperInstance
+        .addressToObject(TOMBSTONE_ADDRESS, false, null);
+    assertThat(token).isEqualTo(Token.TOMBSTONE);
+  }
+
+  @Test
+  public void getSerializedLengthFromDataAsAddressShouldReturnValidLength() {
+    byte[] data = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
+    long address =
+        offHeapRegionEntryHelperInstance.encodeDataAsAddress(data, false, true);
+    TinyStoredObject tinyStoredObject = new TinyStoredObject(address);
+
+    int actualLength = offHeapRegionEntryHelperInstance.getSerializedLength(tinyStoredObject);
+
+    assertThat(actualLength)
+        .isEqualTo(data.length);
+  }
+
+  @Test
+  public void getSerializedLengthFromDataAsAddressShouldReturnZeroForNonEncodedAddress() {
+    TinyStoredObject nonEncodedAddress = new TinyStoredObject(100000L);
+
+    int actualLength = offHeapRegionEntryHelperInstance.getSerializedLength(nonEncodedAddress);
+
+    assertThat(actualLength)
+        .isZero();
+  }
+
+  @Test
+  public void releaseEntryShouldSetValueToRemovePhase2() {
+    OffHeapRegionEntry regionEntry = mock(OffHeapRegionEntry.class);
+
+    when(regionEntry.getAddress())
+        .thenReturn(1L);
+    when(regionEntry.setAddress(1L, REMOVED_PHASE2_ADDRESS))
+        .thenReturn(Boolean.TRUE);
+
+    offHeapRegionEntryHelperInstance.releaseEntry(regionEntry);
+
+    verify(offHeapRegionEntryHelperInstance)
+        .setValue(regionEntry, Token.REMOVED_PHASE2);
+  }
+
+  @Test
+  public void releaseEntryShouldSetValueToRemovePhase2AndSetsAsyncToFalseForDiskEntry() {
+    OffHeapRegionEntry regionEntry = mock(VersionedStatsDiskRegionEntryOffHeap.class);
+    DiskId diskId = spy(DiskId.class);
+
+    when(regionEntry.getAddress())
+        .thenReturn(1L);
+    when(regionEntry.setAddress(1L, REMOVED_PHASE2_ADDRESS))
+        .thenReturn(Boolean.TRUE);
+    when(((DiskEntry) regionEntry).getDiskId())
+        .thenReturn(diskId);
+    when(diskId.isPendingAsync())
+        .thenReturn(Boolean.TRUE);
+
+    offHeapRegionEntryHelperInstance.releaseEntry(regionEntry);
+
+    verify(offHeapRegionEntryHelperInstance)
+        .setValue(regionEntry, Token.REMOVED_PHASE2);
+    verify(diskId)
+        .setPendingAsync(Boolean.FALSE);
+  }
+
+  @Test
+  public void doWithOffHeapClearShouldSetTheThreadLocalToTrue() {
+    // verify that threadlocal is not set
+    assertThat(OffHeapClearRequired.doesClearNeedToCheckForOffHeap()).isFalse();
+
+    OffHeapClearRequired.doWithOffHeapClear(() -> {
+      // verify that threadlocal is set when offheap is cleared
+      assertThat(OffHeapClearRequired.doesClearNeedToCheckForOffHeap()).isTrue();
+    });
+
+    // verify that threadlocal is reset after offheap is cleared
+    assertThat(OffHeapClearRequired.doesClearNeedToCheckForOffHeap()).isFalse();
+  }
+
+  private OffHeapStoredObject createChunk(Object value) {
+    byte[] bytes = EntryEventImpl.serialize(value);
+
+    StoredObject chunk = memoryAllocator.allocateAndInitialize(bytes, true, false);
+
+    return (OffHeapStoredObject) chunk;
+  }
+
+  private static void assertSerializedAndCompressedBits(long encodedAddress,
+      boolean shouldSerializedBitBeSet, boolean shouldCompressedBitBeSet) {
+    boolean isSerializedBitSet = (encodedAddress & SERIALIZED_BIT) == SERIALIZED_BIT;
+
+    assertThat(isSerializedBitSet)
+        .isEqualTo(shouldSerializedBitBeSet);
+
+    boolean isCompressedBitSet = (encodedAddress & COMPRESSED_BIT) == COMPRESSED_BIT;
+
+    assertThat(isCompressedBitSet)
+        .isEqualTo(shouldCompressedBitBeSet);
+  }
+}
diff --git a/geode-core/src/test/java/org/apache/geode/internal/offheap/OffHeapRegionEntryHelperJUnitTest.java b/geode-core/src/test/java/org/apache/geode/internal/offheap/OffHeapRegionEntryHelperJUnitTest.java
deleted file mode 100644
index 3c219ff..0000000
--- a/geode-core/src/test/java/org/apache/geode/internal/offheap/OffHeapRegionEntryHelperJUnitTest.java
+++ /dev/null
@@ -1,926 +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.internal.offheap;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.nio.ByteBuffer;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
-
-import org.apache.geode.compression.Compressor;
-import org.apache.geode.internal.cache.CachePerfStats;
-import org.apache.geode.internal.cache.DiskId;
-import org.apache.geode.internal.cache.EntryEventImpl;
-import org.apache.geode.internal.cache.RegionEntryContext;
-import org.apache.geode.internal.cache.Token;
-import org.apache.geode.internal.cache.VMCachedDeserializable;
-import org.apache.geode.internal.cache.entries.DiskEntry;
-import org.apache.geode.internal.cache.entries.OffHeapRegionEntry;
-import org.apache.geode.internal.cache.entries.VersionedStatsDiskRegionEntryOffHeap;
-import org.apache.geode.internal.serialization.DSCODE;
-
-@RunWith(PowerMockRunner.class)
-@PowerMockIgnore("*.UnitTest")
-@PrepareForTest({OffHeapStoredObject.class, OffHeapRegionEntryHelper.class})
-public class OffHeapRegionEntryHelperJUnitTest {
-
-  private static final Long VALUE_IS_NOT_ENCODABLE = 0L;
-
-  private MemoryAllocator ma;
-
-  @Before
-  public void setUp() {
-    OutOfOffHeapMemoryListener ooohml = mock(OutOfOffHeapMemoryListener.class);
-    OffHeapMemoryStats stats = mock(OffHeapMemoryStats.class);
-
-    ma = MemoryAllocatorImpl.create(ooohml, stats, 1, OffHeapStorage.MIN_SLAB_SIZE * 1,
-        OffHeapStorage.MIN_SLAB_SIZE);
-  }
-
-  @After
-  public void tearDown() {
-    MemoryAllocatorImpl.freeOffHeapMemory();
-  }
-
-  private OffHeapStoredObject createChunk(Object value) {
-    byte[] v = EntryEventImpl.serialize(value);
-
-    boolean isSerialized = true;
-    boolean isCompressed = false;
-
-    OffHeapStoredObject chunk =
-        (OffHeapStoredObject) ma.allocateAndInitialize(v, isSerialized, isCompressed);
-
-    return chunk;
-  }
-
-  @Test
-  public void encodeDataAsAddressShouldReturnZeroIfValueIsGreaterThanSevenBytes() {
-    Long value = Long.MAX_VALUE;
-
-    byte[] valueInBytes = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(value).array();
-    boolean isSerialized = false;
-    boolean isCompressed = false;
-
-    assertThat(valueInBytes.length)
-        .isGreaterThanOrEqualTo(OffHeapRegionEntryHelper.MAX_LENGTH_FOR_DATA_AS_ADDRESS);
-
-    long encodedAddress =
-        OffHeapRegionEntryHelper.encodeDataAsAddress(valueInBytes, isSerialized, isCompressed);
-
-    assertThat(encodedAddress).isEqualTo(VALUE_IS_NOT_ENCODABLE);
-  }
-
-  @Test
-  public void encodeDataAsAddressShouldEncodeLongIfItsSerializedAndIfItsNotTooBig() {
-    Long value = 0L;
-    long expectedEncodedAddress = 123L;
-
-    byte[] valueInBytes = EntryEventImpl.serialize(value);
-    boolean isSerialized = true;
-    boolean isCompressed = false;
-
-    long encodedAddress =
-        OffHeapRegionEntryHelper.encodeDataAsAddress(valueInBytes, isSerialized, isCompressed);
-
-    assertThat(encodedAddress).isEqualTo(expectedEncodedAddress);
-    assertSerializedAndCompressedBits(encodedAddress, isSerialized, isCompressed);
-  }
-
-  @Test
-  public void encodeDataAsAddressShouldReturnZeroIfValueIsLongAndItIsSerializedAndBig() {
-    Long value = Long.MAX_VALUE;
-
-    byte[] valueInBytes = EntryEventImpl.serialize(value);
-    boolean isSerialized = true;
-    boolean isCompressed = false;
-
-    long encodedAddress =
-        OffHeapRegionEntryHelper.encodeDataAsAddress(valueInBytes, isSerialized, isCompressed);
-
-    assertThat(encodedAddress).isEqualTo(VALUE_IS_NOT_ENCODABLE);
-  }
-
-  @Test
-  public void encodeDataAsAddressShouldReturnZeroIfValueIsLargerThanEightBytesAndNotLong() {
-    byte[] someValue = new byte[8];
-    someValue[0] = DSCODE.CLASS.toByte();
-
-    boolean isSerialized = true;
-    boolean isCompressed = false;
-
-    long encodedAddress =
-        OffHeapRegionEntryHelper.encodeDataAsAddress(someValue, isSerialized, isCompressed);
-
-    assertThat(encodedAddress).isEqualTo(VALUE_IS_NOT_ENCODABLE);
-  }
-
-  @Test
-  public void encodeDataAsAddressShouldReturnValidAddressIfValueIsLesserThanSevenBytes() {
-    long expectedAddress = 549755813697L;
-
-    Integer value = Integer.MAX_VALUE;
-
-    byte[] valueInBytes = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(value).array();
-    boolean isSerialized = false;
-    boolean isCompressed = false;
-
-    long encodedAddress =
-        OffHeapRegionEntryHelper.encodeDataAsAddress(valueInBytes, isSerialized, isCompressed);
-
-    assertThat(encodedAddress).isEqualTo(expectedAddress);
-    assertSerializedAndCompressedBits(encodedAddress, isSerialized, isCompressed);
-  }
-
-  @Test
-  public void encodeDataAsAssressShouldSetSerialziedBitIfSerizliaed() {
-    long expectedAddress = 63221918596947L;
-
-    Integer value = Integer.MAX_VALUE;
-
-    byte[] valueInBytes = EntryEventImpl.serialize(value);
-    boolean isSerialized = true;
-    boolean isCompressed = false;
-
-    long encodedAddress =
-        OffHeapRegionEntryHelper.encodeDataAsAddress(valueInBytes, isSerialized, isCompressed);
-
-    assertThat(expectedAddress).isEqualTo(encodedAddress);
-    assertSerializedAndCompressedBits(encodedAddress, isSerialized, isCompressed);
-  }
-
-  @Test
-  public void encodeDataAsAssressShouldSetSerialziedBitIfCompressed() {
-    long expectedAddress = 549755813701L;
-
-    Integer value = Integer.MAX_VALUE;
-
-    byte[] valueInBytes = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(value).array();
-    boolean isSerialized = false;
-    boolean isCompressed = true;
-
-    long encodedAddress =
-        OffHeapRegionEntryHelper.encodeDataAsAddress(valueInBytes, isSerialized, isCompressed);
-
-    assertThat(encodedAddress).isEqualTo(expectedAddress);
-    assertSerializedAndCompressedBits(encodedAddress, isSerialized, isCompressed);
-  }
-
-  @Test
-  public void encodeDataAsAssressShouldSetBothSerialziedAndCompressedBitsIfSerializedAndCompressed() {
-    long expectedAddress = 63221918596951L;
-
-    Integer value = Integer.MAX_VALUE;
-
-    byte[] valueInBytes = EntryEventImpl.serialize(value);
-    boolean isSerialized = true;
-    boolean isCompressed = true;
-
-    long encodedAddress =
-        OffHeapRegionEntryHelper.encodeDataAsAddress(valueInBytes, isSerialized, isCompressed);
-
-    assertThat(expectedAddress).isEqualTo(encodedAddress);
-    assertSerializedAndCompressedBits(encodedAddress, isSerialized, isCompressed);
-  }
-
-  private void assertSerializedAndCompressedBits(long encodedAddress,
-      boolean shouldSerializedBitBeSet, boolean shouldCompressedBitBeSet) {
-    boolean isSerializedBitSet = (encodedAddress
-        & OffHeapRegionEntryHelper.SERIALIZED_BIT) == OffHeapRegionEntryHelper.SERIALIZED_BIT ? true
-            : false;
-    boolean isCompressedBitSet = (encodedAddress
-        & OffHeapRegionEntryHelper.COMPRESSED_BIT) == OffHeapRegionEntryHelper.COMPRESSED_BIT ? true
-            : false;
-
-    assertThat(isSerializedBitSet).isEqualTo(shouldSerializedBitBeSet);
-    assertThat(isCompressedBitSet).isEqualTo(shouldCompressedBitBeSet);
-  }
-
-  @Test
-  public void decodeUncompressedAddressToBytesShouldReturnActualBytes() {
-    long encodedAddress = 549755813697L;
-    Integer value = Integer.MAX_VALUE;
-
-    byte[] actual = OffHeapRegionEntryHelper.decodeUncompressedAddressToBytes(encodedAddress);
-    byte[] expectedValue = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(value).array();
-
-    assertThat(actual).isEqualTo(expectedValue);
-  }
-
-  @Test
-  public void decodeUncompressedAddressToBytesShouldDecodeLongIfItsSerializedAndIfItsNotTooBig() {
-    Long value = 0L;
-    long encodedAddress = 123L;
-
-    byte[] actual = OffHeapRegionEntryHelper.decodeUncompressedAddressToBytes(encodedAddress);
-    byte[] expectedValue = EntryEventImpl.serialize(value);
-
-    assertThat(actual).isEqualTo(expectedValue);
-  }
-
-  @Test(expected = AssertionError.class)
-  public void decodeUncompressedAddressToBytesWithCompressedAddressShouldThrowException() {
-    long encodedAddress = 549755813703L;
-    OffHeapRegionEntryHelper.decodeUncompressedAddressToBytes(encodedAddress);
-  }
-
-  @Test
-  public void decodeCompressedDataAsAddressToRawBytes() {
-    long encodedAddress = 549755813703L;
-    byte[] expected = new byte[] {127, -1, -1, -1};
-
-    byte[] bytes = OffHeapRegionEntryHelper.decodeAddressToRawBytes(encodedAddress);
-
-    assertThat(bytes).isEqualTo(expected);
-  }
-
-  @Test
-  public void encodedAddressShouldBeDecodableEvenIfValueIsSerialized() {
-    Integer value = Integer.MAX_VALUE;
-
-    byte[] serializedValue = EntryEventImpl.serialize(value);
-    boolean isSerialized = true;
-    boolean isCompressed = false;
-
-    long encodedAddress =
-        OffHeapRegionEntryHelper.encodeDataAsAddress(serializedValue, isSerialized, isCompressed);
-
-    Integer actualValue = (Integer) OffHeapRegionEntryHelper.decodeAddressToObject(encodedAddress);
-
-    assertThat(actualValue).isEqualTo(value);
-  }
-
-  @Test
-  public void encodedAddressShouldBeDecodableEvenIfValueIsUnserialized() {
-    Integer value = Integer.MAX_VALUE;
-
-    byte[] unSerializedValue = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(value).array();
-    boolean isSerialized = false;
-    boolean isCompressed = false;
-
-    long encodedAddress =
-        OffHeapRegionEntryHelper.encodeDataAsAddress(unSerializedValue, isSerialized, isCompressed);
-
-    byte[] actualValue = (byte[]) OffHeapRegionEntryHelper.decodeAddressToObject(encodedAddress);
-
-    assertThat(actualValue).isEqualTo(unSerializedValue);
-  }
-
-  @Test
-  public void isSerializedShouldReturnTrueIfSerialized() {
-    assertThat(OffHeapRegionEntryHelper.isSerialized(1000010L)).isTrue();
-  }
-
-  @Test
-  public void isSerializedShouldReturnFalseIfNotSerialized() {
-    assertThat(OffHeapRegionEntryHelper.isSerialized(1000000L)).isFalse();
-  }
-
-  @Test
-  public void isCompressedShouldReturnTrueIfCompressed() {
-    assertThat(OffHeapRegionEntryHelper.isCompressed(1000100L)).isTrue();
-  }
-
-  @Test
-  public void isCompressedShouldReturnFalseIfNotCompressed() {
-    assertThat(OffHeapRegionEntryHelper.isCompressed(1000000L)).isFalse();
-  }
-
-  @Test
-  public void isOffHeapShouldReturnTrueIfAddressIsOnOffHeap() {
-    OffHeapStoredObject value = createChunk(Long.MAX_VALUE);
-    assertThat(OffHeapRegionEntryHelper.isOffHeap(value.getAddress())).isTrue();
-  }
-
-  @Test
-  public void isOffHeapShouldReturnFalseIfAddressIsAnEncodedAddress() {
-    byte[] data =
-        ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt((Integer) Integer.MAX_VALUE).array();
-    long address = OffHeapRegionEntryHelper.encodeDataAsAddress(data, false, false);
-    assertThat(OffHeapRegionEntryHelper.isOffHeap(address)).isFalse();
-  }
-
-  @Test
-  public void isOffHeapShouldReturnFalseForAnyTokenAddress() {
-    assertThat(OffHeapRegionEntryHelper.isOffHeap(OffHeapRegionEntryHelper.NULL_ADDRESS)).isFalse();
-    assertThat(OffHeapRegionEntryHelper.isOffHeap(OffHeapRegionEntryHelper.INVALID_ADDRESS))
-        .isFalse();
-    assertThat(OffHeapRegionEntryHelper.isOffHeap(OffHeapRegionEntryHelper.LOCAL_INVALID_ADDRESS))
-        .isFalse();
-    assertThat(OffHeapRegionEntryHelper.isOffHeap(OffHeapRegionEntryHelper.DESTROYED_ADDRESS))
-        .isFalse();
-    assertThat(OffHeapRegionEntryHelper.isOffHeap(OffHeapRegionEntryHelper.REMOVED_PHASE1_ADDRESS))
-        .isFalse();
-    assertThat(OffHeapRegionEntryHelper.isOffHeap(OffHeapRegionEntryHelper.REMOVED_PHASE2_ADDRESS))
-        .isFalse();
-    assertThat(OffHeapRegionEntryHelper.isOffHeap(OffHeapRegionEntryHelper.END_OF_STREAM_ADDRESS))
-        .isFalse();
-    assertThat(OffHeapRegionEntryHelper.isOffHeap(OffHeapRegionEntryHelper.NOT_AVAILABLE_ADDRESS))
-        .isFalse();
-    assertThat(OffHeapRegionEntryHelper.isOffHeap(OffHeapRegionEntryHelper.TOMBSTONE_ADDRESS))
-        .isFalse();
-  }
-
-  @Test
-  public void setValueShouldChangeTheRegionEntryAddressToNewAddress() {
-    // mock region entry
-    OffHeapRegionEntry re = mock(OffHeapRegionEntry.class);
-
-    // some old address
-    long oldAddress = 1L;
-
-    // testing when the newValue is a chunk
-    OffHeapStoredObject newValue = createChunk(Long.MAX_VALUE);
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(oldAddress);
-    when(re.setAddress(oldAddress, newValue.getAddress())).thenReturn(Boolean.TRUE);
-
-    // invoke the method under test
-    OffHeapRegionEntryHelper.setValue(re, newValue);
-
-    // verify oldAddress is replaced with newAddress
-    verify(re, times(1)).setAddress(oldAddress, newValue.getAddress());
-    // resetting the spy in-order to re-use
-    reset(re);
-
-    // testing when the newValue is DataAsAddress
-    TinyStoredObject newAddress1 = new TinyStoredObject(2L);
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(oldAddress);
-    when(re.setAddress(oldAddress, newAddress1.getAddress())).thenReturn(Boolean.TRUE);
-    OffHeapRegionEntryHelper.setValue(re, newAddress1);
-
-    // verify oldAddress is replaced with newAddress
-    verify(re, times(1)).setAddress(oldAddress, newAddress1.getAddress());
-    reset(re);
-
-    // Testing when newValue is Token Objects
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(oldAddress);
-    when(re.setAddress(oldAddress, OffHeapRegionEntryHelper.NULL_ADDRESS)).thenReturn(Boolean.TRUE);
-    OffHeapRegionEntryHelper.setValue(re, null);
-
-    // verify oldAddress is replaced with newAddress
-    verify(re, times(1)).setAddress(oldAddress, OffHeapRegionEntryHelper.NULL_ADDRESS);
-    reset(re);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(oldAddress);
-    when(re.setAddress(oldAddress, OffHeapRegionEntryHelper.INVALID_ADDRESS))
-        .thenReturn(Boolean.TRUE);
-    OffHeapRegionEntryHelper.setValue(re, Token.INVALID);
-
-    // verify oldAddress is replaced with newAddress
-    verify(re, times(1)).setAddress(oldAddress, OffHeapRegionEntryHelper.INVALID_ADDRESS);
-    reset(re);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(oldAddress);
-    when(re.setAddress(oldAddress, OffHeapRegionEntryHelper.LOCAL_INVALID_ADDRESS))
-        .thenReturn(Boolean.TRUE);
-    OffHeapRegionEntryHelper.setValue(re, Token.LOCAL_INVALID);
-
-    // verify oldAddress is replaced with newAddress
-    verify(re, times(1)).setAddress(oldAddress, OffHeapRegionEntryHelper.LOCAL_INVALID_ADDRESS);
-    reset(re);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(oldAddress);
-    when(re.setAddress(oldAddress, OffHeapRegionEntryHelper.DESTROYED_ADDRESS))
-        .thenReturn(Boolean.TRUE);
-    OffHeapRegionEntryHelper.setValue(re, Token.DESTROYED);
-
-    // verify oldAddress is replaced with newAddress
-    verify(re, times(1)).setAddress(oldAddress, OffHeapRegionEntryHelper.DESTROYED_ADDRESS);
-    reset(re);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(oldAddress);
-    when(re.setAddress(oldAddress, OffHeapRegionEntryHelper.REMOVED_PHASE1_ADDRESS))
-        .thenReturn(Boolean.TRUE);
-    OffHeapRegionEntryHelper.setValue(re, Token.REMOVED_PHASE1);
-
-    // verify oldAddress is replaced with newAddress
-    verify(re, times(1)).setAddress(oldAddress, OffHeapRegionEntryHelper.REMOVED_PHASE1_ADDRESS);
-    reset(re);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(oldAddress);
-    when(re.setAddress(oldAddress, OffHeapRegionEntryHelper.REMOVED_PHASE2_ADDRESS))
-        .thenReturn(Boolean.TRUE);
-    OffHeapRegionEntryHelper.setValue(re, Token.REMOVED_PHASE2);
-
-    // verify oldAddress is replaced with newAddress
-    verify(re, times(1)).setAddress(oldAddress, OffHeapRegionEntryHelper.REMOVED_PHASE2_ADDRESS);
-    reset(re);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(oldAddress);
-    when(re.setAddress(oldAddress, OffHeapRegionEntryHelper.END_OF_STREAM_ADDRESS))
-        .thenReturn(Boolean.TRUE);
-    OffHeapRegionEntryHelper.setValue(re, Token.END_OF_STREAM);
-
-    // verify oldAddress is replaced with newAddress
-    verify(re, times(1)).setAddress(oldAddress, OffHeapRegionEntryHelper.END_OF_STREAM_ADDRESS);
-    reset(re);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(oldAddress);
-    when(re.setAddress(oldAddress, OffHeapRegionEntryHelper.NOT_AVAILABLE_ADDRESS))
-        .thenReturn(Boolean.TRUE);
-    OffHeapRegionEntryHelper.setValue(re, Token.NOT_AVAILABLE);
-
-    // verify oldAddress is replaced with newAddress
-    verify(re, times(1)).setAddress(oldAddress, OffHeapRegionEntryHelper.NOT_AVAILABLE_ADDRESS);
-    reset(re);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(oldAddress);
-    when(re.setAddress(oldAddress, OffHeapRegionEntryHelper.TOMBSTONE_ADDRESS))
-        .thenReturn(Boolean.TRUE);
-    OffHeapRegionEntryHelper.setValue(re, Token.TOMBSTONE);
-
-    // verify oldAddress is replaced with newAddress
-    verify(re, times(1)).setAddress(oldAddress, OffHeapRegionEntryHelper.TOMBSTONE_ADDRESS);
-    reset(re);
-  }
-
-  @Test
-  public void setValueShouldChangeTheRegionEntryAddressToNewAddressAndReleaseOldValueIfItsOnOffHeap() {
-    // mock region entry
-    OffHeapRegionEntry re = mock(OffHeapRegionEntry.class);
-
-    OffHeapStoredObject oldValue = createChunk(Long.MAX_VALUE);
-    OffHeapStoredObject newValue = createChunk(Long.MAX_VALUE - 1);
-
-    // mock Chunk static methods - in-order to verify that release is called
-    PowerMockito.spy(OffHeapStoredObject.class);
-    PowerMockito.doNothing().when(OffHeapStoredObject.class);
-    OffHeapStoredObject.release(oldValue.getAddress());
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(oldValue.getAddress());
-    when(re.setAddress(oldValue.getAddress(), newValue.getAddress())).thenReturn(Boolean.TRUE);
-
-    // invoke the method under test
-    OffHeapRegionEntryHelper.setValue(re, newValue);
-
-    // verify oldAddress is changed to newAddress
-    verify(re, times(1)).setAddress(oldValue.getAddress(), newValue.getAddress());
-
-    // verify oldAddress is released
-    PowerMockito.verifyStatic(OffHeapStoredObject.class);
-    OffHeapStoredObject.release(oldValue.getAddress());
-  }
-
-  @Test
-  public void setValueShouldChangeTheRegionEntryAddressToNewAddressAndDoesNothingIfOldAddressIsAnEncodedAddress() {
-    // mock region entry
-    OffHeapRegionEntry re = mock(OffHeapRegionEntry.class);
-
-    byte[] oldData =
-        ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt((Integer) Integer.MAX_VALUE).array();
-    long oldAddress = OffHeapRegionEntryHelper.encodeDataAsAddress(oldData, false, false);
-
-    byte[] newData = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE)
-        .putInt((Integer) Integer.MAX_VALUE - 1).array();
-    TinyStoredObject newAddress =
-        new TinyStoredObject(OffHeapRegionEntryHelper.encodeDataAsAddress(newData, false, false));
-
-    // mock Chunk static methods - in-order to verify that release is never called
-    PowerMockito.spy(OffHeapStoredObject.class);
-    PowerMockito.doNothing().when(OffHeapStoredObject.class);
-    OffHeapStoredObject.release(oldAddress);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(oldAddress);
-    when(re.setAddress(oldAddress, newAddress.getAddress())).thenReturn(Boolean.TRUE);
-
-    // invoke the method under test
-    OffHeapRegionEntryHelper.setValue(re, newAddress);
-
-    // verify oldAddress is changed to newAddress
-    verify(re, times(1)).setAddress(oldAddress, newAddress.getAddress());
-
-    // verify that release is never called as the old address is not on offheap
-    PowerMockito.verifyStatic(OffHeapStoredObject.class, never());
-    OffHeapStoredObject.release(oldAddress);
-  }
-
-  @Test
-  public void setValueShouldChangeTheRegionEntryAddressToNewAddressAndDoesNothingIfOldAddressIsATokenAddress() {
-    // mock region entry
-    OffHeapRegionEntry re = mock(OffHeapRegionEntry.class);
-
-    long oldAddress = OffHeapRegionEntryHelper.REMOVED_PHASE1_ADDRESS;
-
-    Token newValue = Token.REMOVED_PHASE2;
-    long newAddress = OffHeapRegionEntryHelper.REMOVED_PHASE2_ADDRESS;
-
-    // mock Chunk static methods - in-order to verify that release is never called
-    PowerMockito.spy(OffHeapStoredObject.class);
-    PowerMockito.doNothing().when(OffHeapStoredObject.class);
-    OffHeapStoredObject.release(oldAddress);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(oldAddress);
-    when(re.setAddress(oldAddress, newAddress)).thenReturn(Boolean.TRUE);
-
-    // invoke the method under test
-    OffHeapRegionEntryHelper.setValue(re, newValue);
-
-    // verify oldAddress is changed to newAddress
-    verify(re, times(1)).setAddress(oldAddress, newAddress);
-
-    // verify that release is never called as the old address is not on offheap
-    PowerMockito.verifyStatic(OffHeapStoredObject.class, never());
-    OffHeapStoredObject.release(oldAddress);
-  }
-
-  @Test(expected = IllegalStateException.class)
-  public void setValueShouldThrowIllegalExceptionIfNewValueCannotBeConvertedToAddress() {
-    // mock region entry
-    OffHeapRegionEntry re = mock(OffHeapRegionEntry.class);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(1L);
-
-    // invoke the method under test with some object other than Chunk/DataAsAddress/Token
-    OffHeapRegionEntryHelper.setValue(re, new Object());
-  }
-
-  @Test
-  public void getValueAsTokenShouldReturnNotATokenIfValueIsOnOffHeap() {
-    // mock region entry
-    OffHeapRegionEntry re = mock(OffHeapRegionEntry.class);
-
-    OffHeapStoredObject chunk = createChunk(Long.MAX_VALUE);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(chunk.getAddress());
-    Token token = OffHeapRegionEntryHelper.getValueAsToken(re);
-
-    assertThat(token).isEqualTo(Token.NOT_A_TOKEN);
-  }
-
-  @Test
-  public void getValueAsTokenShouldReturnNotATokenIfValueIsEncoded() {
-    // mock region entry
-    OffHeapRegionEntry re = mock(OffHeapRegionEntry.class);
-
-    byte[] data = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
-    long address = OffHeapRegionEntryHelper.encodeDataAsAddress(data, false, false);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(address);
-    Token token = OffHeapRegionEntryHelper.getValueAsToken(re);
-
-    assertThat(token).isEqualTo(Token.NOT_A_TOKEN);
-  }
-
-  @Test
-  public void getValueAsTokenShouldReturnAValidToken() {
-    // mock region entry
-    OffHeapRegionEntry re = mock(OffHeapRegionEntry.class);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(OffHeapRegionEntryHelper.NULL_ADDRESS);
-    Token token = OffHeapRegionEntryHelper.getValueAsToken(re);
-
-    assertThat(token).isNull();
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(OffHeapRegionEntryHelper.INVALID_ADDRESS);
-    token = OffHeapRegionEntryHelper.getValueAsToken(re);
-
-    assertThat(token).isEqualTo(Token.INVALID);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(OffHeapRegionEntryHelper.LOCAL_INVALID_ADDRESS);
-    token = OffHeapRegionEntryHelper.getValueAsToken(re);
-
-    assertThat(token).isEqualTo(Token.LOCAL_INVALID);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(OffHeapRegionEntryHelper.DESTROYED_ADDRESS);
-    token = OffHeapRegionEntryHelper.getValueAsToken(re);
-
-    assertThat(token).isEqualTo(Token.DESTROYED);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(OffHeapRegionEntryHelper.REMOVED_PHASE1_ADDRESS);
-    token = OffHeapRegionEntryHelper.getValueAsToken(re);
-
-    assertThat(token).isEqualTo(Token.REMOVED_PHASE1);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(OffHeapRegionEntryHelper.REMOVED_PHASE2_ADDRESS);
-    token = OffHeapRegionEntryHelper.getValueAsToken(re);
-
-    assertThat(token).isEqualTo(Token.REMOVED_PHASE2);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(OffHeapRegionEntryHelper.END_OF_STREAM_ADDRESS);
-    token = OffHeapRegionEntryHelper.getValueAsToken(re);
-
-    assertThat(token).isEqualTo(Token.END_OF_STREAM);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(OffHeapRegionEntryHelper.NOT_AVAILABLE_ADDRESS);
-    token = OffHeapRegionEntryHelper.getValueAsToken(re);
-
-    assertThat(token).isEqualTo(Token.NOT_AVAILABLE);
-
-    // mock region entry methods required for test
-    when(re.getAddress()).thenReturn(OffHeapRegionEntryHelper.TOMBSTONE_ADDRESS);
-    token = OffHeapRegionEntryHelper.getValueAsToken(re);
-
-    assertThat(token).isEqualTo(Token.TOMBSTONE);
-  }
-
-  @Test
-  public void addressToObjectShouldReturnValueFromChunk() {
-    OffHeapStoredObject expected = createChunk(Long.MAX_VALUE);
-    Object actual = OffHeapRegionEntryHelper.addressToObject(expected.getAddress(), false, null);
-
-    assertThat(actual).isInstanceOf(OffHeapStoredObject.class);
-    assertThat(actual).isEqualTo(expected);
-  }
-
-  @Test
-  public void addressToObjectShouldReturnCachedDeserializableFromChunkIfAskedToDecompress() {
-    byte[] data = EntryEventImpl.serialize(Long.MAX_VALUE);
-    boolean isSerialized = true;
-    boolean isCompressed = true;
-
-    OffHeapStoredObject chunk =
-        (OffHeapStoredObject) ma.allocateAndInitialize(data, isSerialized, isCompressed);
-
-    // create the mock context
-    RegionEntryContext regionContext = mock(RegionEntryContext.class);
-    CachePerfStats cacheStats = mock(CachePerfStats.class);
-    Compressor compressor = mock(Compressor.class);
-
-    long startTime = 10000L;
-
-    // mock required things
-    when(regionContext.getCompressor()).thenReturn(compressor);
-    when(compressor.decompress(data)).thenReturn(data);
-    when(regionContext.getCachePerfStats()).thenReturn(cacheStats);
-    when(cacheStats.startDecompression()).thenReturn(startTime);
-
-    Object actual =
-        OffHeapRegionEntryHelper.addressToObject(chunk.getAddress(), true, regionContext);
-
-    assertThat(actual).isInstanceOf(VMCachedDeserializable.class);
-
-    Long actualValue = (Long) ((VMCachedDeserializable) actual).getDeserializedForReading();
-    assertThat(actualValue).isEqualTo(Long.MAX_VALUE);
-  }
-
-  @Test
-  public void addressToObjectShouldReturnDecompressedValueFromChunkIfAskedToDecompress() {
-    byte[] data = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).putLong(Long.MAX_VALUE).array();
-    boolean isSerialized = false;
-    boolean isCompressed = true;
-
-    OffHeapStoredObject chunk =
-        (OffHeapStoredObject) ma.allocateAndInitialize(data, isSerialized, isCompressed);
-
-    // create the mock context
-    RegionEntryContext regionContext = mock(RegionEntryContext.class);
-    CachePerfStats cacheStats = mock(CachePerfStats.class);
-    Compressor compressor = mock(Compressor.class);
-
-    long startTime = 10000L;
-
-    // mock required things
-    when(regionContext.getCompressor()).thenReturn(compressor);
-    when(compressor.decompress(data)).thenReturn(data);
-    when(regionContext.getCachePerfStats()).thenReturn(cacheStats);
-    when(cacheStats.startDecompression()).thenReturn(startTime);
-
-    Object actual =
-        OffHeapRegionEntryHelper.addressToObject(chunk.getAddress(), true, regionContext);
-
-    assertThat(actual).isInstanceOf(byte[].class);
-    assertThat(actual).isEqualTo(data);
-  }
-
-  @Test
-  public void addressToObjectShouldReturnValueFromDataAsAddress() {
-    byte[] data = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
-    long address = OffHeapRegionEntryHelper.encodeDataAsAddress(data, false, false);
-
-    TinyStoredObject expected = new TinyStoredObject(address);
-    Object actual = OffHeapRegionEntryHelper.addressToObject(address, false, null);
-
-    assertThat(actual).isInstanceOf(TinyStoredObject.class);
-    assertThat(actual).isEqualTo(expected);
-  }
-
-  @Test
-  public void addressToObjectShouldReturnCachedDeserializableFromSerializedDataAsAddressIfAskedToDecompress() {
-    byte[] data = EntryEventImpl.serialize(Integer.MAX_VALUE);
-    boolean isSerialized = true;
-    boolean isCompressed = true;
-
-    long address = OffHeapRegionEntryHelper.encodeDataAsAddress(data, isSerialized, isCompressed);
-
-    // create the mock context
-    RegionEntryContext regionContext = mock(RegionEntryContext.class);
-    CachePerfStats cacheStats = mock(CachePerfStats.class);
-    Compressor compressor = mock(Compressor.class);
-
-    long startTime = 10000L;
-
-    // mock required things
-    when(regionContext.getCompressor()).thenReturn(compressor);
-    when(compressor.decompress(data)).thenReturn(data);
-    when(regionContext.getCachePerfStats()).thenReturn(cacheStats);
-    when(cacheStats.startDecompression()).thenReturn(startTime);
-
-    Object actual = OffHeapRegionEntryHelper.addressToObject(address, true, regionContext);
-
-    assertThat(actual).isInstanceOf(VMCachedDeserializable.class);
-
-    Integer actualValue = (Integer) ((VMCachedDeserializable) actual).getDeserializedForReading();
-    assertThat(actualValue).isEqualTo(Integer.MAX_VALUE);
-  }
-
-  @Test
-  public void addressToObjectShouldReturnDecompressedValueFromDataAsAddressIfAskedToDecompress() {
-    byte[] data = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
-    boolean isSerialized = false;
-    boolean isCompressed = true;
-
-    long address = OffHeapRegionEntryHelper.encodeDataAsAddress(data, isSerialized, isCompressed);
-
-    // create the mock context
-    RegionEntryContext regionContext = mock(RegionEntryContext.class);
-    CachePerfStats cacheStats = mock(CachePerfStats.class);
-    Compressor compressor = mock(Compressor.class);
-
-    long startTime = 10000L;
-
-    // mock required things
-    when(regionContext.getCompressor()).thenReturn(compressor);
-    when(compressor.decompress(data)).thenReturn(data);
-    when(regionContext.getCachePerfStats()).thenReturn(cacheStats);
-    when(cacheStats.startDecompression()).thenReturn(startTime);
-
-    Object actual = OffHeapRegionEntryHelper.addressToObject(address, true, regionContext);
-
-    assertThat(actual).isInstanceOf(byte[].class);
-    assertThat(actual).isEqualTo(data);
-  }
-
-  @Test
-  public void addressToObjectShouldReturnToken() {
-    Token token = (Token) OffHeapRegionEntryHelper
-        .addressToObject(OffHeapRegionEntryHelper.NULL_ADDRESS, false, null);
-    assertThat(token).isNull();
-
-    token = (Token) OffHeapRegionEntryHelper
-        .addressToObject(OffHeapRegionEntryHelper.INVALID_ADDRESS, false, null);
-    assertThat(token).isEqualTo(Token.INVALID);
-
-    token = (Token) OffHeapRegionEntryHelper
-        .addressToObject(OffHeapRegionEntryHelper.LOCAL_INVALID_ADDRESS, false, null);
-    assertThat(token).isEqualTo(Token.LOCAL_INVALID);
-
-    token = (Token) OffHeapRegionEntryHelper
-        .addressToObject(OffHeapRegionEntryHelper.DESTROYED_ADDRESS, false, null);
-    assertThat(token).isEqualTo(Token.DESTROYED);
-
-    token = (Token) OffHeapRegionEntryHelper
-        .addressToObject(OffHeapRegionEntryHelper.REMOVED_PHASE1_ADDRESS, false, null);
-    assertThat(token).isEqualTo(Token.REMOVED_PHASE1);
-
-    token = (Token) OffHeapRegionEntryHelper
-        .addressToObject(OffHeapRegionEntryHelper.REMOVED_PHASE2_ADDRESS, false, null);
-    assertThat(token).isEqualTo(Token.REMOVED_PHASE2);
-
-    token = (Token) OffHeapRegionEntryHelper
-        .addressToObject(OffHeapRegionEntryHelper.END_OF_STREAM_ADDRESS, false, null);
-    assertThat(token).isEqualTo(Token.END_OF_STREAM);
-
-    token = (Token) OffHeapRegionEntryHelper
-        .addressToObject(OffHeapRegionEntryHelper.NOT_AVAILABLE_ADDRESS, false, null);
-    assertThat(token).isEqualTo(Token.NOT_AVAILABLE);
-
-    token = (Token) OffHeapRegionEntryHelper
-        .addressToObject(OffHeapRegionEntryHelper.TOMBSTONE_ADDRESS, false, null);
-    assertThat(token).isEqualTo(Token.TOMBSTONE);
-  }
-
-  @Test
-  public void getSerializedLengthFromDataAsAddressShouldReturnValidLength() {
-    byte[] data = ByteBuffer.allocate(Integer.SIZE / Byte.SIZE).putInt(Integer.MAX_VALUE).array();
-    boolean isSerialized = false;
-    boolean isCompressed = true;
-
-    long address = OffHeapRegionEntryHelper.encodeDataAsAddress(data, isSerialized, isCompressed);
-    TinyStoredObject daa = new TinyStoredObject(address);
-
-    int actualLength = OffHeapRegionEntryHelper.getSerializedLength(daa);
-
-    assertThat(actualLength).isEqualTo(data.length);
-  }
-
-  @Test
-  public void getSerializedLengthFromDataAsAddressShouldReturnZeroForNonEncodedAddress() {
-    TinyStoredObject nonEncodedAddress = new TinyStoredObject(100000L);
-    int actualLength = OffHeapRegionEntryHelper.getSerializedLength(nonEncodedAddress);
-    assertThat(actualLength).isZero();
-  }
-
-  @Test
-  public void releaseEntryShouldSetValueToRemovePhase2() {
-    // mock region entry
-    OffHeapRegionEntry re = mock(OffHeapRegionEntry.class);
-    when(re.getAddress()).thenReturn(1L);
-    when(re.setAddress(1L, OffHeapRegionEntryHelper.REMOVED_PHASE2_ADDRESS))
-        .thenReturn(Boolean.TRUE);
-
-    // mock required methods
-    PowerMockito.spy(OffHeapRegionEntryHelper.class);
-    PowerMockito.doNothing().when(OffHeapRegionEntryHelper.class);
-    OffHeapRegionEntryHelper.setValue(re, Token.REMOVED_PHASE2);
-
-    OffHeapRegionEntryHelper.releaseEntry(re);
-
-    PowerMockito.verifyStatic(OffHeapRegionEntryHelper.class);
-    OffHeapRegionEntryHelper.setValue(re, Token.REMOVED_PHASE2);
-  }
-
-  @Test
-  public void releaseEntryShouldSetValueToRemovePhase2AndSetsAsyncToFalseForDiskEntry() {
-    // mock region entry
-    OffHeapRegionEntry re = mock(VersionedStatsDiskRegionEntryOffHeap.class);
-    when(re.getAddress()).thenReturn(1L);
-    when(re.setAddress(1L, OffHeapRegionEntryHelper.REMOVED_PHASE2_ADDRESS))
-        .thenReturn(Boolean.TRUE);
-
-    DiskId spy = Mockito.spy(DiskId.class);
-    when(((DiskEntry) re).getDiskId()).thenReturn(spy);
-    when(spy.isPendingAsync()).thenReturn(Boolean.TRUE);
-
-    // mock required methods
-    PowerMockito.spy(OffHeapRegionEntryHelper.class);
-    PowerMockito.doNothing().when(OffHeapRegionEntryHelper.class);
-    OffHeapRegionEntryHelper.setValue(re, Token.REMOVED_PHASE2);
-
-    OffHeapRegionEntryHelper.releaseEntry(re);
-
-    verify(spy, times(1)).setPendingAsync(Boolean.FALSE);
-
-    PowerMockito.verifyStatic(OffHeapRegionEntryHelper.class);
-    OffHeapRegionEntryHelper.setValue(re, Token.REMOVED_PHASE2);
-  }
-
-  @Test
-  public void doWithOffHeapClearShouldSetTheThreadLocalToTrue() {
-    // verify that threadlocal is not set
-    assertThat(OffHeapRegionEntryHelper.doesClearNeedToCheckForOffHeap()).isFalse();
-
-    OffHeapRegionEntryHelper.doWithOffHeapClear(new Runnable() {
-      @Override
-      public void run() {
-        // verify that threadlocal is set when offheap is cleared
-        assertThat(OffHeapRegionEntryHelper.doesClearNeedToCheckForOffHeap()).isTrue();
-      }
-    });
-
-    // verify that threadlocal is reset after offheap is cleared
-    assertThat(OffHeapRegionEntryHelper.doesClearNeedToCheckForOffHeap()).isFalse();
-  }
-}
diff --git a/geode-core/src/test/java/org/apache/geode/internal/offheap/ReferenceCountHelperImplTest.java b/geode-core/src/test/java/org/apache/geode/internal/offheap/ReferenceCountHelperImplTest.java
index 4ee1356..28d0732 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/offheap/ReferenceCountHelperImplTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/offheap/ReferenceCountHelperImplTest.java
@@ -12,976 +12,966 @@
  * or implied. See the License for the specific language governing permissions and limitations under
  * the License.
  */
-
 package org.apache.geode.internal.offheap;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.apache.geode.util.internal.UncheckedUtils.uncheckedCast;
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.BiConsumer;
 
-import org.junit.Rule;
+import org.junit.Before;
 import org.junit.Test;
-import org.junit.contrib.java.lang.system.SystemOutRule;
-import org.junit.runner.RunWith;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
 
 import org.apache.geode.internal.cache.RegionEntry;
 
-/*
- * PowerMock used in this test to verify static method MemoryAllocatorImpl.debugLog
- */
-@RunWith(PowerMockRunner.class)
-@PowerMockIgnore({"*.UnitTest"})
-@PrepareForTest({MemoryAllocatorImpl.class})
 public class ReferenceCountHelperImplTest {
 
-  ReferenceCountHelperImpl rchi;
+  private BiConsumer<String, Boolean> debugLogger;
 
-  @Rule
-  public SystemOutRule sor = new SystemOutRule();
+  @Before
+  public void setUp() {
+    debugLogger = uncheckedCast(mock(BiConsumer.class));
+  }
 
   @Test
   public void doTrackReferenceCountsWithTrackRefsTrueAndTrackFreesTrue() {
-    rchi = getTrueTrue();
-    assertTrue(rchi.trackReferenceCounts());
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_TrueTrue();
+    assertThat(referenceCountHelperImpl.trackReferenceCounts()).isTrue();
   }
 
   @Test
   public void doTrackReferenceCountsWithTrackRefsTrueAndTrackFreesFalse() {
-    rchi = getTrueFalse();
-    assertTrue(rchi.trackReferenceCounts());
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_TrueFalse();
+    assertThat(referenceCountHelperImpl.trackReferenceCounts()).isTrue();
   }
 
   @Test
   public void doTrackReferenceCountsWithTrackRefsFalseAndTrackFreesTrue() {
-    rchi = getFalseTrue();
-    assertFalse(rchi.trackReferenceCounts());
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_FalseTrue();
+    assertThat(referenceCountHelperImpl.trackReferenceCounts()).isFalse();
   }
 
   @Test
   public void doTrackReferenceCountsWithTrackRefsFalseAndTrackFreesFalse() {
-    rchi = getFalseFalse();
-    assertFalse(rchi.trackReferenceCounts());
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_FalseFalse();
+    assertThat(referenceCountHelperImpl.trackReferenceCounts()).isFalse();
   }
 
   @Test
   public void doTrackFreedReferenceCountsWithTrackRefsTrueAndTrackFreesTrue() {
-    rchi = getTrueTrue();
-    assertTrue(rchi.trackFreedReferenceCounts());
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_TrueTrue();
+    assertThat(referenceCountHelperImpl.trackFreedReferenceCounts()).isTrue();
   }
 
   @Test
   public void doTrackFreedReferenceCountsWithTrackRefsTrueAndTrackFreesFalse() {
-    rchi = getTrueFalse();
-    assertFalse(rchi.trackFreedReferenceCounts());
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_TrueFalse();
+    assertThat(referenceCountHelperImpl.trackFreedReferenceCounts()).isFalse();
   }
 
   @Test
   public void doTrackFreedReferenceCountsWithTrackRefsFalseAndTrackFreesTrue() {
-    rchi = getFalseTrue();
-    assertTrue(rchi.trackFreedReferenceCounts());
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_FalseTrue();
+    assertThat(referenceCountHelperImpl.trackFreedReferenceCounts()).isTrue();
   }
 
   @Test
   public void doTrackFreedReferenceCountsWithTrackRefsFalseAndTrackFreesFalse() {
-    rchi = getFalseFalse();
-    assertFalse(rchi.trackFreedReferenceCounts());
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_FalseFalse();
+    assertThat(referenceCountHelperImpl.trackFreedReferenceCounts()).isFalse();
   }
 
   @Test
   public void doSkipRefCountTrackingWithTrackRefsTrueAndTrackFreesTrue() {
-    rchi = getTrueTrue();
-    Object preOwner = rchi.getReferenceCountOwner();
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_TrueTrue();
 
-    rchi.skipRefCountTracking();
-    Object postOwner = rchi.getReferenceCountOwner();
+    Object preOwner = referenceCountHelperImpl.getReferenceCountOwner();
+    referenceCountHelperImpl.skipRefCountTracking();
+    Object postOwner = referenceCountHelperImpl.getReferenceCountOwner();
 
-    assertTrue(postOwner != preOwner); // skip sets owner to SKIP_REF_COUNT_TRACKING
-
-    assertFalse(rchi.isRefCountTracking());
+    // skip sets owner to SKIP_REF_COUNT_TRACKING
+    assertThat(postOwner).isNotEqualTo(preOwner);
+    assertThat(referenceCountHelperImpl.isRefCountTracking()).isFalse();
 
     Long address = (long) 0x1000;
-    boolean decRefCount = false;
-    int rc = 1;
-    rchi.refCountChanged(address, decRefCount, rc);
-    List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address);
-    assertEquals(null, list);
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    List<RefCountChangeInfo> list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).isNull();
 
-    rchi.unskipRefCountTracking();
-    postOwner = rchi.getReferenceCountOwner();
-    assertEquals(postOwner, preOwner);
+    referenceCountHelperImpl.unskipRefCountTracking();
+    postOwner = referenceCountHelperImpl.getReferenceCountOwner();
 
-    assertTrue(rchi.isRefCountTracking());
+    assertThat(preOwner).isEqualTo(postOwner);
+    assertThat(referenceCountHelperImpl.isRefCountTracking()).isTrue();
   }
 
   @Test
   public void doSkipRefCountTrackingWithTrackRefsFalseAndTrackFreesTrue() {
-    rchi = getFalseTrue();
-    Object preOwner = rchi.getReferenceCountOwner();
-    assertEquals(null, preOwner); // getReferenceCountOwner returns null if not tracking
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_FalseTrue();
+    Object preOwner = referenceCountHelperImpl.getReferenceCountOwner();
+    // getReferenceCountOwner returns null if not tracking
+    assertThat(preOwner).isNull();
 
-    rchi.skipRefCountTracking();
-    assertFalse(rchi.isRefCountTracking());
+    referenceCountHelperImpl.skipRefCountTracking();
+    assertThat(referenceCountHelperImpl.isRefCountTracking()).isFalse();
 
-    rchi.unskipRefCountTracking();
-    assertFalse(rchi.isRefCountTracking()); // system prop not set
+    referenceCountHelperImpl.unskipRefCountTracking();
+    assertThat(referenceCountHelperImpl.isRefCountTracking()).isFalse(); // system prop not set
   }
 
   @Test
   public void doSkipRefCountTrackingWithTrackRefsFalseAndTrackFreesFalse() {
-    rchi = getFalseFalse();
-    Object preOwner = rchi.getReferenceCountOwner();
-    assertEquals(null, preOwner); // getReferenceCountOwner returns null if not tracking
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_FalseFalse();
+    Object preOwner = referenceCountHelperImpl.getReferenceCountOwner();
+    // getReferenceCountOwner returns null if not tracking
+    assertThat(preOwner).isNull();
 
-    rchi.skipRefCountTracking();
-    assertFalse(rchi.isRefCountTracking());
+    referenceCountHelperImpl.skipRefCountTracking();
+    assertThat(referenceCountHelperImpl.isRefCountTracking()).isFalse();
 
-    rchi.unskipRefCountTracking();
-    assertFalse(rchi.isRefCountTracking()); // system prop not set
+    referenceCountHelperImpl.unskipRefCountTracking();
+    // system prop not set
+    assertThat(referenceCountHelperImpl.isRefCountTracking()).isFalse();
   }
 
   @Test
   public void doSkipRefCountTrackingWithTrackRefsTrueAndTrackFreesFalse() {
-    rchi = getTrueFalse();
-    Object preOwner = rchi.getReferenceCountOwner();
-
-    rchi.skipRefCountTracking();
-    Object postOwner = rchi.getReferenceCountOwner();
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_TrueFalse();
 
-    assertTrue(postOwner != preOwner); // skip sets owner to SKIP_REF_COUNT_TRACKING
+    Object preOwner = referenceCountHelperImpl.getReferenceCountOwner();
+    referenceCountHelperImpl.skipRefCountTracking();
+    Object postOwner = referenceCountHelperImpl.getReferenceCountOwner();
+    // skip sets owner to SKIP_REF_COUNT_TRACKING
+    assertThat(postOwner).isNotEqualTo(preOwner);
+    assertThat(referenceCountHelperImpl.isRefCountTracking()).isFalse();
 
-    assertFalse(rchi.isRefCountTracking());
-
-    rchi.unskipRefCountTracking();
-    postOwner = rchi.getReferenceCountOwner();
-    assertEquals(postOwner, preOwner);
-
-    assertTrue(rchi.isRefCountTracking());
+    referenceCountHelperImpl.unskipRefCountTracking();
+    postOwner = referenceCountHelperImpl.getReferenceCountOwner();
+    assertThat(preOwner).isEqualTo(postOwner);
+    assertThat(referenceCountHelperImpl.isRefCountTracking()).isTrue();
   }
 
   @Test
   public void doSetReferenceCountOwnerWithTrackRefsTrueAndTrackFreesTrue() {
-    rchi = getTrueTrue();
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_TrueTrue();
     String owner = null;
-    rchi.setReferenceCountOwner(owner);
-    AtomicInteger ai = rchi.getReenterCount();
-    assertEquals(0, ai.get());
-
-    owner = new String("SomeOwner");
-    rchi.setReferenceCountOwner(owner);
-    ai = rchi.getReenterCount();
-    assertEquals(1, ai.get());
-    assertEquals(rchi.getReferenceCountOwner(), owner);
-
-    String owner2 = new String("SomeOwner2");
-    rchi.setReferenceCountOwner(owner2);
-    ai = rchi.getReenterCount();
-    assertEquals(2, ai.get());
-    assertTrue(rchi.getReferenceCountOwner() != owner2); // stays original owner until cnt = 0
+    referenceCountHelperImpl.setReferenceCountOwner(owner);
+    AtomicInteger reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isZero();
+
+    owner = "SomeOwner";
+    referenceCountHelperImpl.setReferenceCountOwner(owner);
+    reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isOne();
+    assertThat(referenceCountHelperImpl.getReferenceCountOwner()).isEqualTo(owner);
+
+    String owner2 = "SomeOwner2";
+    referenceCountHelperImpl.setReferenceCountOwner(owner2);
+    reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isEqualTo(2);
+    // stays original owner until cnt = 0
+    assertThat(referenceCountHelperImpl.getReferenceCountOwner()).isNotEqualTo(owner2);
 
     String owner3 = null;
-    rchi.setReferenceCountOwner(owner3);
-    ai = rchi.getReenterCount();
-    assertEquals(1, ai.get());
-    assertEquals(rchi.getReferenceCountOwner(), owner);
+    referenceCountHelperImpl.setReferenceCountOwner(owner3);
+    reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isOne();
+    assertThat(referenceCountHelperImpl.getReferenceCountOwner()).isEqualTo(owner);
 
     owner = null;
-    rchi.setReferenceCountOwner(owner);
-    ai = rchi.getReenterCount();
-    assertEquals(0, ai.get());
-    assertEquals(rchi.getReferenceCountOwner(), null);
+    referenceCountHelperImpl.setReferenceCountOwner(owner);
+    reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isZero();
+    assertThat(referenceCountHelperImpl.getReferenceCountOwner()).isNull();
 
-    RegionEntry re = mock(RegionEntry.class);
-    rchi.setReferenceCountOwner(re);
-    ai = rchi.getReenterCount();
-    assertEquals(1, ai.get());
-    assertEquals(rchi.getReferenceCountOwner(), re);
+    RegionEntry regionEntry = mock(RegionEntry.class);
+    referenceCountHelperImpl.setReferenceCountOwner(regionEntry);
+    reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isOne();
+    assertThat(regionEntry).isEqualTo(referenceCountHelperImpl.getReferenceCountOwner());
 
     Long address = (long) 0x1000;
     boolean decRefCount = false;
     int rc = 1;
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    RefCountChangeInfo rcci = list.get(0);
-    assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
+    referenceCountHelperImpl.refCountChanged(address, decRefCount, rc);
+    List<RefCountChangeInfo> list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
+    RefCountChangeInfo refCountChangeInfo = list.get(0);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isZero();
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    rcci = list.get(0);
-    assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
+    referenceCountHelperImpl.refCountChanged(address, decRefCount, rc);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
+    refCountChangeInfo = list.get(0);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isOne();
 
     decRefCount = true;
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    rcci = list.get(0);
-    assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
-
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(0, list.size());
+    referenceCountHelperImpl.refCountChanged(address, decRefCount, rc);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
+    refCountChangeInfo = list.get(0);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isZero();
 
+    referenceCountHelperImpl.refCountChanged(address, decRefCount, rc);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).isEmpty();
   }
 
   @Test
   public void doSetReferenceCountOwnerWithTrackRefsFalseAndTrackFreesTrue() {
-    rchi = getFalseTrue();
-    String owner = null;
-    rchi.setReferenceCountOwner(owner);
-    assertEquals(rchi.getReferenceCountOwner(), owner);
-    AtomicInteger ai = rchi.getReenterCount();
-    assertEquals(null, ai);
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_FalseTrue();
+
+    referenceCountHelperImpl.setReferenceCountOwner(null);
+    assertThat(referenceCountHelperImpl.getReferenceCountOwner()).isNull();
+
+    AtomicInteger reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount).isNull();
   }
 
   @Test
   public void doSetReferenceCountOwnerWithTrackRefsFalseAndTrackFreesFalse() {
-    rchi = getFalseFalse();
-    String owner = null;
-    rchi.setReferenceCountOwner(owner);
-    assertEquals(rchi.getReferenceCountOwner(), owner);
-    AtomicInteger ai = rchi.getReenterCount();
-    assertEquals(null, ai);
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_FalseFalse();
+
+    referenceCountHelperImpl.setReferenceCountOwner(null);
+    assertThat(referenceCountHelperImpl.getReferenceCountOwner()).isNull();
+
+    AtomicInteger reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount).isNull();
   }
 
   @Test
   public void doSetReferenceCountOwnerWithTrackRefsTrueAndTrackFreesFalse() {
-    rchi = getTrueFalse();
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_TrueFalse();
+
     String owner = null;
-    rchi.setReferenceCountOwner(owner);
-    AtomicInteger ai = rchi.getReenterCount();
-    assertEquals(0, ai.get());
-
-    owner = new String("SomeOwner");
-    rchi.setReferenceCountOwner(owner);
-    ai = rchi.getReenterCount();
-    assertEquals(1, ai.get());
-    assertEquals(rchi.getReferenceCountOwner(), owner);
-
-    String owner2 = new String("SomeOwner2");
-    rchi.setReferenceCountOwner(owner2);
-    ai = rchi.getReenterCount();
-    assertEquals(2, ai.get());
-    assertTrue(rchi.getReferenceCountOwner() != owner2); // stays original owner until cnt = 0
+    referenceCountHelperImpl.setReferenceCountOwner(owner);
+    AtomicInteger reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isZero();
+
+    owner = "SomeOwner";
+    referenceCountHelperImpl.setReferenceCountOwner(owner);
+    reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isOne();
+    assertThat(referenceCountHelperImpl.getReferenceCountOwner()).isEqualTo(owner);
+
+    String owner2 = "SomeOwner2";
+    referenceCountHelperImpl.setReferenceCountOwner(owner2);
+    reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isEqualTo(2);
+    // stays original owner until cnt = 0
+    assertThat(referenceCountHelperImpl.getReferenceCountOwner()).isEqualTo(owner);
 
     String owner3 = null;
-    rchi.setReferenceCountOwner(owner3);
-    ai = rchi.getReenterCount();
-    assertEquals(1, ai.get());
-    assertEquals(rchi.getReferenceCountOwner(), owner);
+    referenceCountHelperImpl.setReferenceCountOwner(owner3);
+    reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isOne();
+    assertThat(referenceCountHelperImpl.getReferenceCountOwner()).isEqualTo(owner);
 
     owner = null;
-    rchi.setReferenceCountOwner(owner);
-    ai = rchi.getReenterCount();
-    assertEquals(0, ai.get());
-    assertEquals(rchi.getReferenceCountOwner(), null);
+    referenceCountHelperImpl.setReferenceCountOwner(owner);
+    reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isZero();
+    assertThat(referenceCountHelperImpl.getReferenceCountOwner()).isNull();
   }
 
   @Test
   public void doCreateReferenceCountOwnerWithTrackRefsTrueAndTrackFreesTrue() {
-    rchi = getTrueTrue();
-    Object owner = rchi.createReferenceCountOwner();
-    assertFalse(owner == null);
-    AtomicInteger ai = rchi.getReenterCount();
-    assertEquals(1, ai.get());
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_TrueTrue();
+
+    Object owner = referenceCountHelperImpl.createReferenceCountOwner();
+    assertThat(owner).isNotNull();
+    AtomicInteger reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isOne();
 
     owner = null;
-    rchi.setReferenceCountOwner(owner);
-    ai = rchi.getReenterCount();
-    assertEquals(0, ai.get());
-    assertEquals(rchi.getReferenceCountOwner(), null);
+    referenceCountHelperImpl.setReferenceCountOwner(owner);
+    reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isZero();
+    assertThat(referenceCountHelperImpl.getReferenceCountOwner()).isNull();
   }
 
   @Test
   public void doCreateReferenceCountOwnerWithTrackRefsFalseAndTrackFreesTrue() {
-    rchi = getFalseTrue();
-    Object owner = rchi.createReferenceCountOwner();
-    assertTrue(owner == null);
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_FalseTrue();
+    Object owner = referenceCountHelperImpl.createReferenceCountOwner();
+    assertThat(owner).isNull();
   }
 
   @Test
   public void doCreateReferenceCountOwnerWithTrackRefsFalseAndTrackFreesFalse() {
-    rchi = getFalseFalse();
-    Object owner = rchi.createReferenceCountOwner();
-    assertTrue(owner == null);
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_FalseFalse();
+    Object owner = referenceCountHelperImpl.createReferenceCountOwner();
+    assertThat(owner).isNull();
   }
 
   @Test
   public void doCreateReferenceCountOwnerWithTrackRefsTrueAndTrackFreesFalse() {
-    rchi = getTrueFalse();
-    Object owner = rchi.createReferenceCountOwner();
-    assertFalse(owner == null);
-    AtomicInteger ai = rchi.getReenterCount();
-    assertEquals(1, ai.get());
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_TrueFalse();
+    Object owner = referenceCountHelperImpl.createReferenceCountOwner();
+    assertThat(owner).isNotNull();
+    AtomicInteger reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isOne();
 
     owner = null;
-    rchi.setReferenceCountOwner(owner);
-    ai = rchi.getReenterCount();
-    assertEquals(0, ai.get());
-    assertEquals(rchi.getReferenceCountOwner(), null);
+    referenceCountHelperImpl.setReferenceCountOwner(owner);
+    reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isZero();
+    assertThat(referenceCountHelperImpl.getReferenceCountOwner()).isNull();
   }
 
   @Test
   public void doRefCountChangedNoOwnerWithTrackRefsTrueAndTrackFreesTrue() {
-    rchi = getTrueTrue();
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_TrueTrue();
     Long address = (long) 0x1000;
-    boolean decRefCount = false;
-    int rc = 1;
 
-    rchi.freeRefCountInfo(address); // quick check of free of nonexistent info
+    // quick check of free of nonexistent info
+    referenceCountHelperImpl.freeRefCountInfo(address);
+    Object owner = referenceCountHelperImpl.getReferenceCountOwner();
+    assertThat(owner).isNull();
 
-    Object owner = rchi.getReferenceCountOwner();
-    assertTrue(owner == null);
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    List<RefCountChangeInfo> list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    RefCountChangeInfo rcci = list.get(0);
-    assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
+    RefCountChangeInfo refCountChangeInfo = list.get(0);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isZero();
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    rcci = list.get(0);
-    assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
 
-    decRefCount = true;
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(2, list.size()); // inc and dec are tracked in different changeinfo objects (?)
-    rcci = list.get(1);
-    assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
+    refCountChangeInfo = list.get(0);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isOne();
+
+    referenceCountHelperImpl.refCountChanged(address, true, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    // inc and dec are tracked in different changeinfo objects (?)
+    assertThat(list).hasSize(2);
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(2, list.size());
-    rcci = list.get(1);
-    assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
+    refCountChangeInfo = list.get(1);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isZero();
 
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(2, list.size()); // list contains 2 entries from inc/dec done above
+    referenceCountHelperImpl.refCountChanged(address, true, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(2);
 
-    List<RefCountChangeInfo> freeInfo = rchi.getFreeRefCountInfo(address);
-    assertEquals(null, freeInfo); // no freeRefCountInfo calls yet
+    refCountChangeInfo = list.get(1);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isOne();
 
-    rchi.freeRefCountInfo(address); // when freed, moved to FreeRefCountInfo list
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    // list contains 2 entries from inc/dec done above
+    assertThat(list).hasSize(2);
 
-    List<RefCountChangeInfo> freeInfo2 = rchi.getFreeRefCountInfo(address);
-    assertEquals(2, freeInfo2.size()); // the inc/dec info moved to freeRefCountInfo list
+    List<RefCountChangeInfo> freeInfo = referenceCountHelperImpl.getFreeRefCountInfo(address);
+    // no freeRefCountInfo calls yet
+    assertThat(freeInfo).isNull();
 
-    list = rchi.getRefCountInfo(address);
-    assertEquals(null, list); // the inc/dec ref count list should now be null
+    // when freed, moved to FreeRefCountInfo list
+    referenceCountHelperImpl.freeRefCountInfo(address);
+    List<RefCountChangeInfo> freeInfo2 = referenceCountHelperImpl.getFreeRefCountInfo(address);
+    // the inc/dec info moved to freeRefCountInfo list
+    assertThat(freeInfo2).hasSize(2);
+
+    list = referenceCountHelperImpl.getRefCountInfo(address);
+    // the inc/dec ref count list should now be null
+    assertThat(list).isNull();
   }
 
   @Test
   public void doRefCountChangedNoOwnerWithTrackRefsFalseAndTrackFreesTrue() {
-
-    rchi = getFalseTrue();
-
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_FalseTrue();
     Long address = (long) 0x1000;
-    boolean decRefCount = false;
-    int rc = 1;
-
-    Object owner = rchi.getReferenceCountOwner();
-    assertTrue(owner == null);
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address);
-    assertEquals(null, list);
+    Object owner = referenceCountHelperImpl.getReferenceCountOwner();
+    assertThat(owner).isNull();
 
-    decRefCount = true;
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(null, list);
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    List<RefCountChangeInfo> list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).isNull();
 
-    List<RefCountChangeInfo> freeInfo = rchi.getFreeRefCountInfo(address);
-    assertEquals(null, freeInfo); // no freeRefCountInfo calls yet
+    referenceCountHelperImpl.refCountChanged(address, true, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).isNull();
 
-    rchi.freeRefCountInfo(address); // noop when not tracking
+    List<RefCountChangeInfo> freeInfo = referenceCountHelperImpl.getFreeRefCountInfo(address);
+    // no freeRefCountInfo calls yet
+    assertThat(freeInfo).isNull();
 
-    freeInfo = rchi.getFreeRefCountInfo(address);
-    assertEquals(null, freeInfo); // should still be null
+    // noop when not tracking
+    referenceCountHelperImpl.freeRefCountInfo(address);
+    freeInfo = referenceCountHelperImpl.getFreeRefCountInfo(address);
+    // should still be null
+    assertThat(freeInfo).isNull();
   }
 
   @Test
   public void doRefCountChangedNoOwnerWithTrackRefsFalseAndTrackFreesFalse() {
-
-    rchi = getFalseFalse();
-
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_FalseFalse();
     Long address = (long) 0x1000;
-    boolean decRefCount = false;
-    int rc = 1;
 
-    Object owner = rchi.getReferenceCountOwner();
-    assertTrue(owner == null);
+    Object owner = referenceCountHelperImpl.getReferenceCountOwner();
+    assertThat(owner).isNull();
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address);
-    assertEquals(null, list);
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    List<RefCountChangeInfo> list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).isNull();
 
-    decRefCount = true;
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(null, list);
-
-    List<RefCountChangeInfo> freeInfo = rchi.getFreeRefCountInfo(address);
-    assertEquals(null, freeInfo); // no freeRefCountInfo calls yet
+    referenceCountHelperImpl.refCountChanged(address, true, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).isNull();
 
-    rchi.freeRefCountInfo(address); // noop when not tracking
+    List<RefCountChangeInfo> freeInfo = referenceCountHelperImpl.getFreeRefCountInfo(address);
+    // no freeRefCountInfo calls yet
+    assertThat(freeInfo).isNull();
 
-    freeInfo = rchi.getFreeRefCountInfo(address);
-    assertEquals(null, freeInfo); // should still be null
+    // noop when not tracking
+    referenceCountHelperImpl.freeRefCountInfo(address);
+    // should still be null
+    freeInfo = referenceCountHelperImpl.getFreeRefCountInfo(address);
+    assertThat(freeInfo).isNull();
   }
 
   @Test
   public void doRefCountChangedNoOwnerWithTrackRefsTrueAndTrackFreesFalse() {
-    rchi = getTrueFalse();
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_TrueFalse();
     Long address = (long) 0x1000;
-    boolean decRefCount = false;
-    int rc = 1;
 
-    Object owner = rchi.getReferenceCountOwner();
-    assertTrue(owner == null);
+    Object owner = referenceCountHelperImpl.getReferenceCountOwner();
+    assertThat(owner).isNull();
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    RefCountChangeInfo rcci = list.get(0);
-    assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    List<RefCountChangeInfo> list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
+    RefCountChangeInfo refCountChangeInfo = list.get(0);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isZero();
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    rcci = list.get(0);
-    assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
+    refCountChangeInfo = list.get(0);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isOne();
 
-    decRefCount = true;
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(2, list.size()); // inc and dec are tracked in different changeinfo objects (?)
-    rcci = list.get(1);
-    assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
+    referenceCountHelperImpl.refCountChanged(address, true, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    // inc and dec are tracked in different changeinfo objects (?)
+    assertThat(list).hasSize(2);
+    refCountChangeInfo = list.get(1);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isZero();
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(2, list.size());
-    rcci = list.get(1);
-    assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
+    referenceCountHelperImpl.refCountChanged(address, true, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(2);
+    refCountChangeInfo = list.get(1);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isOne();
 
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(2, list.size()); // list contains 2 entries from inc/dec done above
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    // list contains 2 entries from inc/dec done above
+    assertThat(list).hasSize(2);
 
-    List<RefCountChangeInfo> freeInfo = rchi.getFreeRefCountInfo(address);
-    assertEquals(null, freeInfo); // no freeRefCountInfo calls yet
+    List<RefCountChangeInfo> freeInfo = referenceCountHelperImpl.getFreeRefCountInfo(address);
+    // no freeRefCountInfo calls yet
+    assertThat(freeInfo).isNull();
 
-    rchi.freeRefCountInfo(address); // when freed, moved to FreeRefCountInfo list
+    // when freed, moved to FreeRefCountInfo list
+    referenceCountHelperImpl.freeRefCountInfo(address);
 
-    List<RefCountChangeInfo> freeInfo2 = rchi.getFreeRefCountInfo(address);
-    assertEquals(null, freeInfo2); // not tracking freed info
+    List<RefCountChangeInfo> freeInfo2 = referenceCountHelperImpl.getFreeRefCountInfo(address);
+    // not tracking freed info
+    assertThat(freeInfo2).isNull();
 
-    list = rchi.getRefCountInfo(address);
-    assertEquals(null, list); // the inc/dec ref count list should now be null
+    list = referenceCountHelperImpl.getRefCountInfo(address);
+    // the inc/dec ref count list should now be null
+    assertThat(list).isNull();
   }
 
   @Test
   public void doRefCountChangedWithOwnerWithTrackRefsTrueAndTrackFreesTrue() {
-    rchi = getTrueTrue();
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_TrueTrue();
     Long address = (long) 0x1000;
-    boolean decRefCount = false;
-    int rc = 1;
 
-    Object owner = rchi.createReferenceCountOwner();
-    assertFalse(owner == null);
+    Object owner = referenceCountHelperImpl.createReferenceCountOwner();
+    assertThat(owner).isNotNull();
 
-    AtomicInteger ai = rchi.getReenterCount();
-    assertEquals(1, ai.get());
+    AtomicInteger reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isOne();
 
     owner = null;
-    rchi.setReferenceCountOwner(owner);
-    ai = rchi.getReenterCount();
-    assertEquals(0, ai.get());
-    assertEquals(rchi.getReferenceCountOwner(), null);
-
-    owner = rchi.createReferenceCountOwner();
-    assertFalse(owner == null);
-
-    ai = rchi.getReenterCount();
-    assertEquals(1, ai.get());
-
-    rchi.refCountChanged(address, decRefCount, rc);
-    List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    RefCountChangeInfo rcci = list.get(0);
-    assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
-
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    rcci = list.get(0);
-    assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
+    referenceCountHelperImpl.setReferenceCountOwner(owner);
+    reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isZero();
+    assertThat(referenceCountHelperImpl.getReferenceCountOwner()).isNull();
 
-    decRefCount = true;
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size()); // inc and dec are tracked in different changeinfo objects (?)
-    rcci = list.get(0);
-    assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
+    owner = referenceCountHelperImpl.createReferenceCountOwner();
+    assertThat(owner).isNotNull();
+
+    reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isOne();
+
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    List<RefCountChangeInfo> list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
+    RefCountChangeInfo refCountChangeInfo = list.get(0);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isZero();
+
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
+    refCountChangeInfo = list.get(0);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isOne();
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.getRefCountInfo(address);
-    assertEquals(0, list.size());
+    referenceCountHelperImpl.refCountChanged(address, true, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    // inc and dec are tracked in different changeinfo objects (?)
+    assertThat(list).hasSize(1);
+    refCountChangeInfo = list.get(0);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isZero();
+
+    referenceCountHelperImpl.refCountChanged(address, true, 1);
+    list = referenceCountHelperImpl.getRefCountInfo(address);
+    assertThat(list).isEmpty();
   }
 
   @Test
   public void doRefCountChangedWithOwnerWithTrackRefsFalseAndTrackFreesTrue() {
-
-    rchi = getFalseTrue();
-
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_FalseTrue();
     Long address = (long) 0x1000;
-    boolean decRefCount = false;
-    int rc = 1;
 
-    Object owner = rchi.createReferenceCountOwner();
-    assertTrue(owner == null);
+    Object owner = referenceCountHelperImpl.createReferenceCountOwner();
+    assertThat(owner).isNull();
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address);
-    assertEquals(null, list);
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    List<RefCountChangeInfo> list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).isNull();
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(null, list);
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).isNull();
 
-    decRefCount = true;
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(null, list); // inc and dec are tracked in different changeinfo objects (?)
+    referenceCountHelperImpl.refCountChanged(address, true, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    // inc and dec are tracked in different changeinfo objects (?)
+    assertThat(list).isNull();
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(null, list);
+    referenceCountHelperImpl.refCountChanged(address, true, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).isNull();
 
-    List<RefCountChangeInfo> freeInfo = rchi.getFreeRefCountInfo(address);
-    assertEquals(null, freeInfo); // no freeRefCountInfo calls yet
+    List<RefCountChangeInfo> freeInfo = referenceCountHelperImpl.getFreeRefCountInfo(address);
+    // no freeRefCountInfo calls yet
+    assertThat(freeInfo).isNull();
 
-    rchi.freeRefCountInfo(address); // when freed, moved to FreeRefCountInfo list
+    // when freed, moved to FreeRefCountInfo list
+    referenceCountHelperImpl.freeRefCountInfo(address);
 
-    freeInfo = rchi.getFreeRefCountInfo(address);
-    assertEquals(null, freeInfo); // the inc/dec info moved to freeRefCountInfo list
+    freeInfo = referenceCountHelperImpl.getFreeRefCountInfo(address);
+    // the inc/dec info moved to freeRefCountInfo list
+    assertThat(freeInfo).isNull();
   }
 
   @Test
   public void doRefCountChangedWithOwnerWithTrackRefsFalseAndTrackFreesFalse() {
-
-    rchi = getFalseFalse();
-
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_FalseFalse();
     Long address = (long) 0x1000;
-    boolean decRefCount = false;
-    int rc = 1;
 
-    Object owner = rchi.createReferenceCountOwner();
-    assertTrue(owner == null);
+    Object owner = referenceCountHelperImpl.createReferenceCountOwner();
+    assertThat(owner).isNull();
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address);
-    assertEquals(null, list);
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    List<RefCountChangeInfo> list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).isNull();
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(null, list);
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).isNull();
 
-    decRefCount = true;
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(null, list); // inc and dec are tracked in different changeinfo objects (?)
+    referenceCountHelperImpl.refCountChanged(address, true, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    // inc and dec are tracked in different changeinfo objects (?)
+    assertThat(list).isNull();
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(null, list);
+    referenceCountHelperImpl.refCountChanged(address, true, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).isNull();
 
-    List<RefCountChangeInfo> freeInfo = rchi.getFreeRefCountInfo(address);
-    assertEquals(null, freeInfo); // no freeRefCountInfo calls yet
+    List<RefCountChangeInfo> freeInfo = referenceCountHelperImpl.getFreeRefCountInfo(address);
+    // no freeRefCountInfo calls yet
+    assertThat(freeInfo).isNull();
 
-    rchi.freeRefCountInfo(address); // when freed, moved to FreeRefCountInfo list
+    // when freed, moved to FreeRefCountInfo list
+    referenceCountHelperImpl.freeRefCountInfo(address);
 
-    freeInfo = rchi.getFreeRefCountInfo(address);
-    assertEquals(null, freeInfo); // the inc/dec info moved to freeRefCountInfo list
+    freeInfo = referenceCountHelperImpl.getFreeRefCountInfo(address);
+    // the inc/dec info moved to freeRefCountInfo list
+    assertThat(freeInfo).isNull();
   }
 
   @Test
   public void doRefCountChangedWithOwnerWithTrackRefsTrueAndTrackFreesFalse() {
-    rchi = getTrueFalse();
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_TrueFalse();
     Long address = (long) 0x1000;
-    boolean decRefCount = false;
-    int rc = 1;
 
-    Object owner = rchi.createReferenceCountOwner();
-    assertFalse(owner == null);
+    Object owner = referenceCountHelperImpl.createReferenceCountOwner();
+    assertThat(owner).isNotNull();
 
-    AtomicInteger ai = rchi.getReenterCount();
-    assertEquals(1, ai.get());
+    AtomicInteger reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isOne();
 
     owner = null;
-    rchi.setReferenceCountOwner(owner);
-    ai = rchi.getReenterCount();
-    assertEquals(0, ai.get());
-    assertEquals(rchi.getReferenceCountOwner(), null);
-
-    owner = rchi.createReferenceCountOwner();
-    assertFalse(owner == null);
-
-    ai = rchi.getReenterCount();
-    assertEquals(1, ai.get());
-
-    rchi.refCountChanged(address, decRefCount, rc);
-    List<RefCountChangeInfo> list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    RefCountChangeInfo rcci = list.get(0);
-    assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
-
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    rcci = list.get(0);
-    assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
-
-    decRefCount = true;
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size()); // inc and dec are tracked in different changeinfo objects (?)
-    rcci = list.get(0);
-    assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
-
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(0, list.size());
+    referenceCountHelperImpl.setReferenceCountOwner(owner);
+    reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isZero();
+    assertThat(referenceCountHelperImpl.getReferenceCountOwner()).isNull();
+
+    owner = referenceCountHelperImpl.createReferenceCountOwner();
+    assertThat(owner).isNotNull();
+    reenterCount = referenceCountHelperImpl.getReenterCount();
+    assertThat(reenterCount.get()).isOne();
+
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    List<RefCountChangeInfo> list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
+    RefCountChangeInfo refCountChangeInfo = list.get(0);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isZero();
+
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
+    refCountChangeInfo = list.get(0);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isOne();
+
+    referenceCountHelperImpl.refCountChanged(address, true, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    // inc and dec are tracked in different changeinfo objects (?)
+    assertThat(list).hasSize(1);
+    refCountChangeInfo = list.get(0);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isZero();
+
+    referenceCountHelperImpl.refCountChanged(address, true, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).isEmpty();
   }
 
   @Test
   public void doGetRefCountInfoWithTrackRefsTrueAndTrackFreesTrue() {
-    rchi = getTrueTrue();
-    long address = (long) 0x1000;
-    boolean decRefCount = false;
-    int rc = 1;
-
-    List<RefCountChangeInfo> list = null;
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_TrueTrue();
+    Long address = (long) 0x1000;
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    RefCountChangeInfo rcci = list.get(0);
-    assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    List<RefCountChangeInfo> list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
+    RefCountChangeInfo refCountChangeInfo = list.get(0);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isZero();
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    rcci = list.get(0);
-    assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
+    refCountChangeInfo = list.get(0);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isOne();
 
-    List<RefCountChangeInfo> info = rchi.getRefCountInfo(address); // now getRefCountInfo
-    assertEquals(1, info.size());
-    rcci = info.get(0);
-    assertEquals(1, rcci.getUseCount());
+    // now getRefCountInfo
+    List<RefCountChangeInfo> info = referenceCountHelperImpl.getRefCountInfo(address);
+    assertThat(info).hasSize(1);
+    refCountChangeInfo = info.get(0);
+    assertThat(refCountChangeInfo.getUseCount()).isOne();
 
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(0, list.size()); // getRefCountInfo leaves list LOCKED (i.e. empty)
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    // getRefCountInfo leaves list LOCKED (i.e. empty)
+    assertThat(list).isEmpty();
   }
 
   @Test
-  public void doRefCountChangedAfterGetRefCountInfoWithTrackRefsTrueAndTrackFreesTrue()
-      throws Exception {
-    rchi = getTrueTrue();
-    long address = (long) 0x1000;
-    boolean decRefCount = false;
-    int rc = 1;
-
-    List<RefCountChangeInfo> list = null;
-
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    RefCountChangeInfo rcci = list.get(0);
-    assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
-
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    rcci = list.get(0);
-    assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
-
-    List<RefCountChangeInfo> info = rchi.getRefCountInfo(address); // now getRefCountInfo
-    assertEquals(1, info.size());
-    rcci = info.get(0);
-    assertEquals(1, rcci.getUseCount());
-
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(0, list.size()); // getRefCountInfo leaves list LOCKED (i.e. empty)
+  public void doRefCountChangedAfterGetRefCountInfoWithTrackRefsTrueAndTrackFreesTrue() {
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_TrueTrue();
+    Long address = (long) 0x1000;
 
-    sor.mute(); // Mute system out
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    List<RefCountChangeInfo> list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
+    RefCountChangeInfo refCountChangeInfo = list.get(0);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isZero();
 
-    PowerMockito.spy(MemoryAllocatorImpl.class); // Watch the impl for invocation of debugLog
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
+    refCountChangeInfo = list.get(0);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isOne();
 
-    rchi.refCountChanged(address, decRefCount, rc); // this line should fail. no inc after getInfo
-                                                    // allowed
-    PowerMockito.verifyStatic(MemoryAllocatorImpl.class);
-    MemoryAllocatorImpl.debugLog("refCount inced after orphan detected for @1000", true);
+    // now getRefCountInfo
+    List<RefCountChangeInfo> info = referenceCountHelperImpl.getRefCountInfo(address);
+    assertThat(info).hasSize(1);
+    refCountChangeInfo = info.get(0);
+    assertThat(refCountChangeInfo.getUseCount()).isOne();
 
-    decRefCount = true;
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    // getRefCountInfo leaves list LOCKED (i.e. empty)
+    assertThat(list).isEmpty();
 
-    rchi.refCountChanged(address, decRefCount, rc); // this line should fail. no inc after getInfo
-                                                    // allowed
-    PowerMockito.verifyStatic(MemoryAllocatorImpl.class);
-    MemoryAllocatorImpl.debugLog("refCount deced after orphan detected for @1000", true);
+    // this line should fail. no inc after getInfo allowed
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    verify(debugLogger).accept("refCount inced after orphan detected for @1000", true);
 
-    rchi.freeRefCountInfo(address); // this line should fail. no free after getInfo allowed
-    PowerMockito.verifyStatic(MemoryAllocatorImpl.class);
-    MemoryAllocatorImpl.debugLog("freed after orphan detected for @1000", true);
+    // this line should fail. no inc after getInfo allowed
+    referenceCountHelperImpl.refCountChanged(address, true, 1);
+    verify(debugLogger).accept("refCount deced after orphan detected for @1000", true);
 
+    // this line should fail. no free after getInfo allowed
+    referenceCountHelperImpl.freeRefCountInfo(address);
+    verify(debugLogger).accept("freed after orphan detected for @1000", true);
   }
 
   @Test
   public void doGetRefCountInfoWithTrackRefsFalseAndTrackFreesTrue() {
-    rchi = getFalseTrue();
-    long address = (long) 0x1000;
-    boolean decRefCount = false;
-    int rc = 1;
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_FalseTrue();
+    Long address = (long) 0x1000;
 
-    List<RefCountChangeInfo> list = rchi.getRefCountInfo(address);
-    assertEquals(null, list);
+    List<RefCountChangeInfo> list = referenceCountHelperImpl.getRefCountInfo(address);
+    assertThat(list).isNull();
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(null, list);
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).isNull();
 
-    List<RefCountChangeInfo> info = rchi.getRefCountInfo(address);
-    assertEquals(null, info);
+    List<RefCountChangeInfo> info = referenceCountHelperImpl.getRefCountInfo(address);
+    assertThat(info).isNull();
 
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(null, list);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).isNull();
 
-    rchi.refCountChanged(address, decRefCount, rc); // this will be ignored.
-    decRefCount = true;
-    rchi.refCountChanged(address, decRefCount, rc); // this will be ignored.
-    rchi.freeRefCountInfo(address); // this will be ignored.
+    // TODO: add assertions for the following
 
+    // this will be ignored.
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    // this will be ignored.
+    referenceCountHelperImpl.refCountChanged(address, true, 1);
+    // this will be ignored.
+    referenceCountHelperImpl.freeRefCountInfo(address);
   }
 
   @Test
   public void doGetRefCountInfoWithTrackRefsFalseAndTrackFreesFalse() {
-    rchi = getFalseFalse();
-    long address = (long) 0x1000;
-    boolean decRefCount = false;
-    int rc = 1;
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_FalseFalse();
+    Long address = (long) 0x1000;
 
-    List<RefCountChangeInfo> list = rchi.getRefCountInfo(address);
-    assertEquals(null, list);
+    List<RefCountChangeInfo> list = referenceCountHelperImpl.getRefCountInfo(address);
+    assertThat(list).isNull();
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(null, list);
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).isNull();
 
-    List<RefCountChangeInfo> info = rchi.getRefCountInfo(address);
-    assertEquals(null, info);
+    List<RefCountChangeInfo> info = referenceCountHelperImpl.getRefCountInfo(address);
+    assertThat(info).isNull();
 
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(null, list);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).isNull();
   }
 
   @Test
   public void doGetRefCountInfoWithTrackRefsTrueAndTrackFreesFalse() {
-    rchi = getTrueFalse();
+    ReferenceCountHelperImpl referenceCountHelperImpl = newReferenceCountHelperImpl_TrueFalse();
     long address = (long) 0x1000;
-    boolean decRefCount = false;
-    int rc = 1;
-
-    List<RefCountChangeInfo> list = null;
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    RefCountChangeInfo rcci = list.get(0);
-    assertEquals(0, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    List<RefCountChangeInfo> list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
+    RefCountChangeInfo refCountChangeInfo = list.get(0);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isZero();
 
-    rchi.refCountChanged(address, decRefCount, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    rcci = list.get(0);
-    assertEquals(1, rcci.getUseCount()); // line 258 of ref cnt helper does not set useCount = 1
-                                         // when adding new entry?
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
+    refCountChangeInfo = list.get(0);
+    // line 258 of ref cnt helper does not set useCount = 1 when adding new entry?
+    assertThat(refCountChangeInfo.getUseCount()).isOne();
 
-    List<RefCountChangeInfo> info = rchi.getRefCountInfo(address); // now getRefCountInfo
-    assertEquals(1, info.size());
-    rcci = info.get(0);
-    assertEquals(1, rcci.getUseCount());
+    // now getRefCountInfo
+    List<RefCountChangeInfo> info = referenceCountHelperImpl.getRefCountInfo(address);
+    assertThat(info).hasSize(1);
+    refCountChangeInfo = info.get(0);
+    assertThat(refCountChangeInfo.getUseCount()).isOne();
 
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(0, list.size()); // getRefCountInfo leaves list LOCKED (i.e. empty)
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    // getRefCountInfo leaves list LOCKED (i.e. empty)
+    assertThat(list).isEmpty();
   }
 
-  private ReferenceCountHelperImpl getTrueTrue() {
-    return new ReferenceCountHelperImpl(true, true);
-  }
-
-  private ReferenceCountHelperImpl getTrueFalse() {
-    return new ReferenceCountHelperImpl(true, false);
-  }
-
-  private ReferenceCountHelperImpl getFalseTrue() {
-    return new ReferenceCountHelperImpl(false, true);
-  }
-
-  private ReferenceCountHelperImpl getFalseFalse() {
-    return new ReferenceCountHelperImpl(false, false);
-  }
+  @Test
+  public void doGetRefCountInfoNonRegionEntryConcurrencyTest() {
+    ReferenceCountHelperImpl referenceCountHelperImpl = newHookedReferenceCountHelperImpl();
+    Long address = (long) 0x1000;
 
-  private ReferenceCountHelperImpl getHookedImpl() {
-    return new HookedReferenceCountHelperImpl(true, true);
+    // assume test identity
+    referenceCountHelperImpl.setReferenceCountOwner("TestOwner");
+
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    List<RefCountChangeInfo> list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
+    RefCountChangeInfo refCountChangeInfo = list.get(0);
+    // hooked impl simulates a concurrent update, so cnt is > expected
+    assertThat(refCountChangeInfo.getUseCount()).isOne();
+
+    // sets owner to null and resets count
+    referenceCountHelperImpl.setReferenceCountOwner(null);
+    // sets owner to null and resets count // sets owner to null and resets count
+
+    // assume new identity
+    referenceCountHelperImpl.setReferenceCountOwner("TestOwner2");
+
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
+    refCountChangeInfo = list.get(0);
+    // list is not null, so hook not used
+    assertThat(refCountChangeInfo.getUseCount()).isEqualTo(2);
+
+    // dec ref count
+    referenceCountHelperImpl.refCountChanged(address, true, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    // dec adds new list of stack traces
+    assertThat(list).hasSize(2);
+    refCountChangeInfo = list.get(1);
+    // cnt starts at 0 for new entries
+    assertThat(refCountChangeInfo.getUseCount()).isZero();
+
+    // now getRefCountInfo
+    List<RefCountChangeInfo> info = referenceCountHelperImpl.getRefCountInfo(address);
+    // hooked impl added one to list
+    assertThat(info).hasSize(3);
+    refCountChangeInfo = info.get(2);
+    // count starts at 0 for new entries
+    assertThat(refCountChangeInfo.getUseCount()).isZero();
+
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    // getRefCountInfo leaves list LOCKED (i.e. empty)
+    assertThat(list).isEmpty();
   }
 
   @Test
-  public void doGetRefCountInfoNonRegionEntryConcurrencyTest() {
-    rchi = getHookedImpl();
-    long address = (long) 0x1000;
-    int rc = 1;
-    RefCountChangeInfo rcci;
-
-    List<RefCountChangeInfo> list = null;
+  public void doGetRefCountInfoRegionEntryConcurrencyTest() {
+    ReferenceCountHelperImpl referenceCountHelperImpl = newHookedReferenceCountHelperImpl();
+    Long address = (long) 0x1000;
 
-    rchi.setReferenceCountOwner("TestOwner"); // assume test identity
+    RegionEntry regionEntry1 = mock(RegionEntry.class);
+    // set owner to region entry type
+    referenceCountHelperImpl.setReferenceCountOwner(regionEntry1);
 
-    rchi.refCountChanged(address, false, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    rcci = list.get(0);
-    assertEquals(1, rcci.getUseCount()); // hooked impl simulates a concurrent update, so cnt is >
-                                         // expected
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    List<RefCountChangeInfo> list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
+    RefCountChangeInfo refCountChangeInfo = list.get(0);
+    // hooked impl simulates a concurrent update, so cnt is > expected
+    assertThat(refCountChangeInfo.getUseCount()).isOne();
 
-    rchi.setReferenceCountOwner(null); // sets owner to null and resets count
-    rchi.setReferenceCountOwner(null); // sets owner to null and resets count
+    // sets owner to null and resets count
+    referenceCountHelperImpl.setReferenceCountOwner(null);
+    // sets owner to null and resets count
+    referenceCountHelperImpl.setReferenceCountOwner(null);
 
-    rchi.setReferenceCountOwner("TestOwner2"); // assume new identity
+    RegionEntry regionEntry2 = mock(RegionEntry.class);
+    // set owner to region entry type
+    referenceCountHelperImpl.setReferenceCountOwner(regionEntry2);
 
-    rchi.refCountChanged(address, false, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    rcci = list.get(0);
-    assertEquals(2, rcci.getUseCount()); // list is not null, so hook not used
+    referenceCountHelperImpl.refCountChanged(address, false, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    assertThat(list).hasSize(1);
+    refCountChangeInfo = list.get(0);
+    // list is not null, so hook not used
+    assertThat(refCountChangeInfo.getUseCount()).isEqualTo(2);
 
-    rchi.refCountChanged(address, true, rc); // dec ref count
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(2, list.size()); // dec adds new list of stack traces
-    rcci = list.get(1);
-    assertEquals(0, rcci.getUseCount()); // cnt starts at 0 for new entries
+    // dec ref count
+    referenceCountHelperImpl.refCountChanged(address, true, 1);
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    // dec adds new list of stack traces
+    assertThat(list).hasSize(2);
+    refCountChangeInfo = list.get(1);
+    // cnt starts at 0 for new entries
+    assertThat(refCountChangeInfo.getUseCount()).isZero();
 
-    List<RefCountChangeInfo> info = rchi.getRefCountInfo(address); // now getRefCountInfo
-    assertEquals(3, info.size()); // hooked impl added one to list
-    rcci = info.get(2);
-    assertEquals(0, rcci.getUseCount()); // count starts at 0 for new entries
+    // now getRefCountInfo
+    List<RefCountChangeInfo> info = referenceCountHelperImpl.getRefCountInfo(address);
+    // hooked impl added one to list
+    assertThat(info).hasSize(3);
+    refCountChangeInfo = info.get(2);
+    // count starts at 0 for new entries
+    assertThat(refCountChangeInfo.getUseCount()).isZero();
 
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(0, list.size()); // getRefCountInfo leaves list LOCKED (i.e. empty)
+    list = referenceCountHelperImpl.peekRefCountInfo(address);
+    // getRefCountInfo leaves list LOCKED (i.e. empty)
+    assertThat(list).isEmpty();
   }
 
-  @Test
-  public void doGetRefCountInfoRegionEntryConcurrencyTest() {
-    rchi = getHookedImpl();
-    long address = (long) 0x1000;
-    int rc = 1;
-    RefCountChangeInfo rcci;
-
-    List<RefCountChangeInfo> list = null;
-
-    RegionEntry re = mock(RegionEntry.class);
-    rchi.setReferenceCountOwner(re); // set owner to region entry type
-
-    rchi.refCountChanged(address, false, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    rcci = list.get(0);
-    assertEquals(1, rcci.getUseCount()); // hooked impl simulates a concurrent update, so cnt is >
-                                         // expected
+  private ReferenceCountHelperImpl newReferenceCountHelperImpl_TrueTrue() {
+    return new ReferenceCountHelperImpl(true, true, debugLogger);
+  }
 
-    rchi.setReferenceCountOwner(null); // sets owner to null and resets count
-    rchi.setReferenceCountOwner(null); // sets owner to null and resets count
+  private ReferenceCountHelperImpl newReferenceCountHelperImpl_TrueFalse() {
+    return new ReferenceCountHelperImpl(true, false, debugLogger);
+  }
 
-    RegionEntry re2 = mock(RegionEntry.class);
-    rchi.setReferenceCountOwner(re2); // set owner to region entry type
+  private ReferenceCountHelperImpl newReferenceCountHelperImpl_FalseTrue() {
+    return new ReferenceCountHelperImpl(false, true, debugLogger);
+  }
 
-    rchi.refCountChanged(address, false, rc);
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(1, list.size());
-    rcci = list.get(0);
-    assertEquals(2, rcci.getUseCount()); // list is not null, so hook not used
+  private ReferenceCountHelperImpl newReferenceCountHelperImpl_FalseFalse() {
+    return new ReferenceCountHelperImpl(false, false, debugLogger);
+  }
 
-    rchi.refCountChanged(address, true, rc); // dec ref count
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(2, list.size()); // dec adds new list of stack traces
-    rcci = list.get(1);
-    assertEquals(0, rcci.getUseCount()); // cnt starts at 0 for new entries
+  private ReferenceCountHelperImpl newHookedReferenceCountHelperImpl() {
+    return new HookedReferenceCountHelperImpl(true, true, debugLogger);
+  }
 
-    List<RefCountChangeInfo> info = rchi.getRefCountInfo(address); // now getRefCountInfo
-    assertEquals(3, info.size()); // hooked impl added one to list
-    rcci = info.get(2);
-    assertEquals(0, rcci.getUseCount()); // count starts at 0 for new entries
+  private static class HookedReferenceCountHelperImpl extends ReferenceCountHelperImpl {
 
-    list = rchi.peekRefCountInfo(address);
-    assertEquals(0, list.size()); // getRefCountInfo leaves list LOCKED (i.e. empty)
-  }
+    private int refCountChangedTestHookCount;
 
-  private class HookedReferenceCountHelperImpl extends ReferenceCountHelperImpl {
-    HookedReferenceCountHelperImpl(boolean trackRefCounts, boolean trackFreedRefCounts) {
-      super(trackRefCounts, trackFreedRefCounts);
+    private HookedReferenceCountHelperImpl(boolean trackRefCounts, boolean trackFreedRefCounts,
+        BiConsumer<String, Boolean> debugLogger) {
+      super(trackRefCounts, trackFreedRefCounts, debugLogger);
     }
 
-    protected int refCountChangedTestHookCount = 0;
-
-    /*
+    /**
      * Update list of stack traces for address. Hooked SUT should see that the list changed.
      */
     @Override
     protected void getReferenceCountInfoTestHook(
-        ConcurrentMap<Long, List<RefCountChangeInfo>> stacktraces, long address) {
+        ConcurrentMap<Long, List<RefCountChangeInfo>> stackTraces, long address) {
       List<RefCountChangeInfo> updatedList =
-          new ArrayList<RefCountChangeInfo>(stacktraces.get(address));
-      RefCountChangeInfo rcci = new RefCountChangeInfo(false, 0, "TestOwner");
-      updatedList.add(rcci);
-      stacktraces.put(address, updatedList);
+          new ArrayList<>(stackTraces.get(address));
+      RefCountChangeInfo refCountChangeInfo = new RefCountChangeInfo(false, 0, "TestOwner");
+      updatedList.add(refCountChangeInfo);
+      stackTraces.put(address, updatedList);
     }
 
-    /*
+    /**
      * Reinvoke refCountChanged to update reference count. Hooked SUT should see that the count has
      * changed.
      */
diff --git a/geode-core/src/test/java/org/apache/geode/internal/offheap/ReferenceCountHelperJUnitTest.java b/geode-core/src/test/java/org/apache/geode/internal/offheap/ReferenceCountHelperJUnitTest.java
deleted file mode 100644
index a7ca6101..0000000
--- a/geode-core/src/test/java/org/apache/geode/internal/offheap/ReferenceCountHelperJUnitTest.java
+++ /dev/null
@@ -1,205 +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.internal.offheap;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mockito;
-import org.powermock.api.mockito.PowerMockito;
-import org.powermock.core.classloader.annotations.PowerMockIgnore;
-import org.powermock.core.classloader.annotations.PrepareForTest;
-import org.powermock.modules.junit4.PowerMockRunner;
-
-
-/*
- * This test simply verifies the static class delegates properly to the impl
- *
- * PowerMock used in this test to inject mocked impl into static class The PowerMockRule bootstraps
- * PowerMock without the need for the @RunWith(PowerMockRunner.class) annotation, which was
- * interfering with jacoco
- *
- */
-@RunWith(PowerMockRunner.class)
-@PowerMockIgnore({"*.UnitTest"})
-@PrepareForTest({ReferenceCountHelper.class})
-public class ReferenceCountHelperJUnitTest {
-
-  private ReferenceCountHelperImpl prepareInstance() {
-    ReferenceCountHelperImpl rchi = mock(ReferenceCountHelperImpl.class);
-    PowerMockito.mockStatic(ReferenceCountHelper.class, Mockito.CALLS_REAL_METHODS);
-    PowerMockito.when(ReferenceCountHelper.getInstance()).thenReturn(rchi);
-    return rchi;
-  }
-
-  @Test
-  public void trackReferenceCountsTrueTest() throws Exception {
-    ReferenceCountHelperImpl rchi = prepareInstance();
-    when(rchi.trackReferenceCounts()).thenReturn(true);
-    boolean b = ReferenceCountHelper.trackReferenceCounts();
-    assertTrue(b);
-    verify(rchi).trackReferenceCounts();
-  }
-
-  @Test
-  public void trackReferenceCountsFalseTest() throws Exception {
-    ReferenceCountHelperImpl rchi = prepareInstance();
-    when(rchi.trackReferenceCounts()).thenReturn(false);
-    boolean b = ReferenceCountHelper.trackReferenceCounts();
-    assertFalse(b);
-    verify(rchi).trackReferenceCounts();
-  }
-
-  @Test
-  public void trackFreedReferenceCountsTrueTest() throws Exception {
-    ReferenceCountHelperImpl rchi = prepareInstance();
-    when(rchi.trackFreedReferenceCounts()).thenReturn(true);
-    assertTrue(ReferenceCountHelper.trackFreedReferenceCounts());
-    verify(rchi).trackFreedReferenceCounts();
-  }
-
-  @Test
-  public void trackFreedReferenceCountsFalseTest() throws Exception {
-    ReferenceCountHelperImpl rchi = prepareInstance();
-    when(rchi.trackFreedReferenceCounts()).thenReturn(false);
-    assertFalse(ReferenceCountHelper.trackFreedReferenceCounts());
-    verify(rchi).trackFreedReferenceCounts();
-  }
-
-  @Test
-  public void setReferenceCountOwnerTest() throws Exception {
-    ReferenceCountHelperImpl rchi = prepareInstance();
-    Object theOwner = new Object();
-    ReferenceCountHelper.setReferenceCountOwner(theOwner);
-    verify(rchi).setReferenceCountOwner(theOwner);
-  }
-
-  @Test
-  public void createReferenceCountOwnerTest() throws Exception {
-    ReferenceCountHelperImpl rchi = prepareInstance();
-    Object expectedResult = new String("createReferenceCountOwner result");
-    when(rchi.createReferenceCountOwner()).thenReturn(expectedResult);
-    Object s = ReferenceCountHelper.createReferenceCountOwner();
-    assertEquals(s, expectedResult);
-    verify(rchi).createReferenceCountOwner();
-  }
-
-  @Test
-  public void skipRefCountTrackingTest() throws Exception {
-    ReferenceCountHelperImpl rchi = prepareInstance();
-    ReferenceCountHelper.skipRefCountTracking();
-    verify(rchi).skipRefCountTracking();
-  }
-
-  @Test
-  public void isRefCountTrackingTrueTest() throws Exception {
-    ReferenceCountHelperImpl rchi = prepareInstance();
-    when(rchi.isRefCountTracking()).thenReturn(true);
-    boolean b = ReferenceCountHelper.isRefCountTracking();
-    assertTrue(b);
-    verify(rchi).isRefCountTracking();
-  }
-
-  @Test
-  public void isRefCountTrackingFalseTest() throws Exception {
-    ReferenceCountHelperImpl rchi = prepareInstance();
-    when(rchi.isRefCountTracking()).thenReturn(false);
-    boolean b = ReferenceCountHelper.isRefCountTracking();
-    assertFalse(b);
-    verify(rchi).isRefCountTracking();
-  }
-
-  @Test
-  public void unskipRefCountTrackingTest() throws Exception {
-    ReferenceCountHelperImpl rchi = prepareInstance();
-    ReferenceCountHelper.unskipRefCountTracking();
-    verify(rchi).unskipRefCountTracking();
-  }
-
-  @Test
-  public void getRefCountInfoTest() throws Exception {
-    ReferenceCountHelperImpl rchi = prepareInstance();
-    List<RefCountChangeInfo> expectedResult = Collections.emptyList();
-    when(rchi.getRefCountInfo((long) 1000)).thenReturn(expectedResult);
-    List<RefCountChangeInfo> l = ReferenceCountHelper.getRefCountInfo(1000);
-    assertEquals(l, expectedResult);
-    verify(rchi).getRefCountInfo(1000);
-  }
-
-  @Test
-  public void refCountChangedTest() throws Exception {
-    ReferenceCountHelperImpl rchi = prepareInstance();
-    ReferenceCountHelper.refCountChanged((long) 1000, true, 4);
-    verify(rchi).refCountChanged((long) 1000, true, 4);
-  }
-
-  @Test
-  public void freeRefCountInfoTest() throws Exception {
-    ReferenceCountHelperImpl rchi = prepareInstance();
-    ReferenceCountHelper.freeRefCountInfo((long) 1000);
-    verify(rchi).freeRefCountInfo((long) 1000);
-  }
-
-  @Test
-  public void getReferenceCountOwnerTest() throws Exception {
-    ReferenceCountHelperImpl rchi = prepareInstance();
-    Object expectedResult = new String("getReferenceCountOwner result");
-    when(rchi.getReferenceCountOwner()).thenReturn(expectedResult);
-    Object o = ReferenceCountHelper.getReferenceCountOwner();
-    assertEquals(o, expectedResult);
-    verify(rchi).getReferenceCountOwner();
-  }
-
-  @Test
-  public void getReenterCountTest() throws Exception {
-    ReferenceCountHelperImpl rchi = prepareInstance();
-    AtomicInteger expectedResult = new AtomicInteger(8);
-    when(rchi.getReenterCount()).thenReturn(expectedResult);
-    AtomicInteger ai = ReferenceCountHelper.getReenterCount();
-    assertEquals(ai, expectedResult);
-    verify(rchi).getReenterCount();
-  }
-
-  @Test
-  public void getFreeRefCountInfoTest() throws Exception {
-    ReferenceCountHelperImpl rchi = prepareInstance();
-    List<RefCountChangeInfo> expectedResult = Collections.emptyList();
-    when(rchi.getFreeRefCountInfo((long) 1000)).thenReturn(expectedResult);
-    List<RefCountChangeInfo> l = ReferenceCountHelper.getFreeRefCountInfo(1000);
-    assertEquals(l, expectedResult);
-    verify(rchi).getFreeRefCountInfo(1000);
-  }
-
-  @Test
-  public void peekRefCountInfoTest() throws Exception {
-    ReferenceCountHelperImpl rchi = prepareInstance();
-    List<RefCountChangeInfo> expectedResult = Collections.emptyList();
-    when(rchi.peekRefCountInfo((long) 1000)).thenReturn(expectedResult);
-    List<RefCountChangeInfo> l = ReferenceCountHelper.peekRefCountInfo(1000);
-    assertEquals(l, expectedResult);
-    verify(rchi).peekRefCountInfo(1000);
-  }
-}
diff --git a/geode-wan/src/distributedTest/java/org/apache/geode/internal/cache/wan/parallel/ParallelGatewaySenderOperationsDUnitTest.java b/geode-wan/src/distributedTest/java/org/apache/geode/internal/cache/wan/parallel/ParallelGatewaySenderOperationsDUnitTest.java
index 3a8a6a1..6b76e6a 100644
--- a/geode-wan/src/distributedTest/java/org/apache/geode/internal/cache/wan/parallel/ParallelGatewaySenderOperationsDUnitTest.java
+++ b/geode-wan/src/distributedTest/java/org/apache/geode/internal/cache/wan/parallel/ParallelGatewaySenderOperationsDUnitTest.java
@@ -63,7 +63,7 @@ import org.apache.geode.internal.cache.wan.AbstractGatewaySender;
 import org.apache.geode.internal.cache.wan.GatewaySenderException;
 import org.apache.geode.internal.cache.wan.WANTestBase;
 import org.apache.geode.internal.offheap.MemoryAllocatorImpl;
-import org.apache.geode.internal.offheap.OffHeapRegionEntryHelper;
+import org.apache.geode.internal.offheap.OffHeapClearRequired;
 import org.apache.geode.test.awaitility.GeodeAwaitility;
 import org.apache.geode.test.dunit.AsyncInvocation;
 import org.apache.geode.test.dunit.IgnoredException;
@@ -929,7 +929,7 @@ public class ParallelGatewaySenderOperationsDUnitTest extends WANTestBase {
       // above.
       callables.add(Executors.callable(() -> {
         try {
-          OffHeapRegionEntryHelper.doWithOffHeapClear(new Runnable() {
+          OffHeapClearRequired.doWithOffHeapClear(new Runnable() {
             @Override
             public void run() {
               // Wait for the cache writer to be invoked to release this countdown latch.