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 2016/03/01 01:09:29 UTC

[20/34] incubator-geode git commit: GEODE-831: restore FreeListManagerTest

GEODE-831: restore FreeListManagerTest


Project: http://git-wip-us.apache.org/repos/asf/incubator-geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-geode/commit/4fed7d84
Tree: http://git-wip-us.apache.org/repos/asf/incubator-geode/tree/4fed7d84
Diff: http://git-wip-us.apache.org/repos/asf/incubator-geode/diff/4fed7d84

Branch: refs/heads/feature/GEODE-949-2
Commit: 4fed7d841bc3956fb913630597378bb97f20e7bf
Parents: bd3ac70
Author: Darrel Schneider <ds...@pivotal.io>
Authored: Fri Feb 26 16:07:48 2016 -0800
Committer: Darrel Schneider <ds...@pivotal.io>
Committed: Fri Feb 26 16:08:21 2016 -0800

----------------------------------------------------------------------
 .../internal/offheap/FreeListManagerTest.java   | 797 +++++++++++++++++++
 1 file changed, 797 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/4fed7d84/geode-core/src/test/java/com/gemstone/gemfire/internal/offheap/FreeListManagerTest.java
----------------------------------------------------------------------
diff --git a/geode-core/src/test/java/com/gemstone/gemfire/internal/offheap/FreeListManagerTest.java b/geode-core/src/test/java/com/gemstone/gemfire/internal/offheap/FreeListManagerTest.java
new file mode 100644
index 0000000..64032cc
--- /dev/null
+++ b/geode-core/src/test/java/com/gemstone/gemfire/internal/offheap/FreeListManagerTest.java
@@ -0,0 +1,797 @@
+/*
+ * 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 com.gemstone.gemfire.internal.offheap;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+import static com.googlecode.catchexception.CatchException.*;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReferenceArray;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import com.gemstone.gemfire.LogWriter;
+import com.gemstone.gemfire.OutOfOffHeapMemoryException;
+import com.gemstone.gemfire.test.junit.categories.UnitTest;
+
+@Category(UnitTest.class)
+public class FreeListManagerTest {
+  static {
+    ClassLoader.getSystemClassLoader().setDefaultAssertionStatus(true);
+  }
+
+  private final int DEFAULT_SLAB_SIZE = 1024*1024*5;
+  private final SimpleMemoryAllocatorImpl ma = mock(SimpleMemoryAllocatorImpl.class);
+  private final OffHeapMemoryStats stats = mock(OffHeapMemoryStats.class);
+  private TestableFreeListManager freeListManager;
+  
+
+  @BeforeClass
+  public static void setUpBeforeClass() throws Exception {
+  }
+
+  @AfterClass
+  public static void tearDownAfterClass() throws Exception {
+  }
+
+  @Before
+  public void setUp() throws Exception {
+    when(ma.getStats()).thenReturn(stats);
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    if (this.freeListManager != null) {
+      this.freeListManager.freeSlabs();
+    }
+  }
+  
+  private static TestableFreeListManager createFreeListManager(SimpleMemoryAllocatorImpl ma, AddressableMemoryChunk[] slabs) {
+    return new TestableFreeListManager(ma, slabs);
+  }
+  
+  private void setUpSingleSlabManager() {
+    setUpSingleSlabManager(DEFAULT_SLAB_SIZE);
+  }
+  private void setUpSingleSlabManager(int slabSize) {
+    UnsafeMemoryChunk slab = new UnsafeMemoryChunk(slabSize);
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {slab});
+  }
+
+  @Test
+  public void usedMemoryIsZeroOnDefault() {
+    setUpSingleSlabManager();
+    assertThat(this.freeListManager.getUsedMemory()).isZero();
+  }
+
+  @Test
+  public void freeMemoryIsSlabSizeOnDefault() {
+    setUpSingleSlabManager();
+    assertThat(this.freeListManager.getFreeMemory()).isEqualTo(DEFAULT_SLAB_SIZE);
+  }
+  
+  @Test
+  public void totalMemoryIsSlabSizeOnDefault() {
+    setUpSingleSlabManager();
+    assertThat(this.freeListManager.getTotalMemory()).isEqualTo(DEFAULT_SLAB_SIZE);
+  }
+  
+  @Test
+  public void allocateTinyChunkHasCorrectSize() {
+    setUpSingleSlabManager();
+    int tinySize = 10;
+
+    ObjectChunk c = this.freeListManager.allocate(tinySize);
+    
+    validateChunkSizes(c, tinySize);
+  }
+  
+  private void validateChunkSizes(ObjectChunk c, int dataSize) {
+    assertThat(c).isNotNull();
+    assertThat(c.getDataSize()).isEqualTo(dataSize);
+    assertThat(c.getSize()).isEqualTo(computeExpectedSize(dataSize));
+  }
+
+  @Test
+  public void allocateTinyChunkFromFreeListHasCorrectSize() {
+    setUpSingleSlabManager();
+    int tinySize = 10;
+    
+    ObjectChunk c = this.freeListManager.allocate(tinySize);
+    ObjectChunk.release(c.getMemoryAddress(), this.freeListManager);
+    c = this.freeListManager.allocate(tinySize);
+
+    validateChunkSizes(c, tinySize);
+  }
+  
+  @Test
+  public void allocateTinyChunkFromEmptyFreeListHasCorrectSize() {
+    setUpSingleSlabManager();
+    int dataSize = 10;
+    
+    ObjectChunk c = this.freeListManager.allocate(dataSize);
+    ObjectChunk.release(c.getMemoryAddress(), this.freeListManager);
+    this.freeListManager.allocate(dataSize);
+    // free list will now be empty
+    c = this.freeListManager.allocate(dataSize);
+
+    validateChunkSizes(c, dataSize);
+  }
+
+  @Test
+  public void allocateHugeChunkHasCorrectSize() {
+    setUpSingleSlabManager();
+    int hugeSize = FreeListManager.MAX_TINY+1;
+
+    ObjectChunk c = this.freeListManager.allocate(hugeSize);
+
+    validateChunkSizes(c, hugeSize);
+  }
+  
+  @Test
+  public void allocateHugeChunkFromEmptyFreeListHasCorrectSize() {
+    setUpSingleSlabManager();
+    int dataSize = FreeListManager.MAX_TINY+1;
+    
+    ObjectChunk c = this.freeListManager.allocate(dataSize);
+    ObjectChunk.release(c.getMemoryAddress(), this.freeListManager);
+    this.freeListManager.allocate(dataSize);
+    // free list will now be empty
+    c = this.freeListManager.allocate(dataSize);
+    
+    validateChunkSizes(c, dataSize);
+  }
+
+  @Test
+  public void allocateHugeChunkFromFragmentWithItemInFreeListHasCorrectSize() {
+    setUpSingleSlabManager();
+    int dataSize = FreeListManager.MAX_TINY+1+1024;
+    
+    ObjectChunk c = this.freeListManager.allocate(dataSize);
+    ObjectChunk.release(c.getMemoryAddress(), this.freeListManager);
+    dataSize = FreeListManager.MAX_TINY+1;
+    c = this.freeListManager.allocate(dataSize);
+    
+    validateChunkSizes(c, dataSize);
+  }
+  @Test
+  public void freeTinyMemoryDefault() {
+    setUpSingleSlabManager();
+    
+    assertThat(this.freeListManager.getFreeTinyMemory()).isZero();
+  }
+  @Test
+  public void freeTinyMemoryEqualToChunkSize() {
+    setUpSingleSlabManager();
+    int dataSize = 10;
+    
+    ObjectChunk c = this.freeListManager.allocate(dataSize);
+    ObjectChunk.release(c.getMemoryAddress(), this.freeListManager);
+    
+    assertThat(this.freeListManager.getFreeTinyMemory()).isEqualTo(computeExpectedSize(dataSize));
+  }
+   
+  @Test
+  public void freeTinyMemoryWithTwoTinyFreeListsEqualToChunkSize() {
+    setUpSingleSlabManager();
+    int dataSize = 10;
+    
+    ObjectChunk c = this.freeListManager.allocate(dataSize);
+    ObjectChunk.release(c.getMemoryAddress(), this.freeListManager);
+    
+    int dataSize2 = 100;
+    
+    ObjectChunk c2 = this.freeListManager.allocate(dataSize2);
+    ObjectChunk.release(c2.getMemoryAddress(), this.freeListManager);
+    
+    assertThat(this.freeListManager.getFreeTinyMemory()).isEqualTo(computeExpectedSize(dataSize)+computeExpectedSize(dataSize2));
+  }
+   
+  @Test
+  public void freeHugeMemoryDefault() {
+    setUpSingleSlabManager();
+    
+    assertThat(this.freeListManager.getFreeHugeMemory()).isZero();
+  }
+  @Test
+  public void freeHugeMemoryEqualToChunkSize() {
+    setUpSingleSlabManager();
+    int dataSize = FreeListManager.MAX_TINY+1+1024;
+    
+    ObjectChunk c = this.freeListManager.allocate(dataSize);
+    ObjectChunk.release(c.getMemoryAddress(), this.freeListManager);
+    
+    assertThat(this.freeListManager.getFreeHugeMemory()).isEqualTo(computeExpectedSize(dataSize));
+  }
+  
+  @Test
+  public void freeFragmentMemoryDefault() {
+    setUpSingleSlabManager();
+    
+    assertThat(this.freeListManager.getFreeFragmentMemory()).isEqualTo(DEFAULT_SLAB_SIZE);
+  }
+  
+  @Test
+  public void freeFragmentMemorySomeOfFragmentAllocated() {
+    setUpSingleSlabManager();
+    ObjectChunk c = this.freeListManager.allocate(DEFAULT_SLAB_SIZE/4-8);
+    
+    assertThat(this.freeListManager.getFreeFragmentMemory()).isEqualTo((DEFAULT_SLAB_SIZE/4)*3);
+  }
+  
+  @Test
+  public void freeFragmentMemoryMostOfFragmentAllocated() {
+    setUpSingleSlabManager();
+    ObjectChunk c = this.freeListManager.allocate(DEFAULT_SLAB_SIZE-8-8);
+    
+    assertThat(this.freeListManager.getFreeFragmentMemory()).isZero();
+  }
+  
+  private int computeExpectedSize(int dataSize) {
+    return ((dataSize + ObjectChunk.OFF_HEAP_HEADER_SIZE + 7) / 8) * 8;
+  }
+
+  @Test(expected = AssertionError.class)
+  public void allocateZeroThrowsAssertion() {
+    setUpSingleSlabManager();
+    this.freeListManager.allocate(0);
+  }
+  
+  @Test
+  public void allocateFromMultipleSlabs() {
+    int SMALL_SLAB = 16;
+    int MEDIUM_SLAB = 128;
+    UnsafeMemoryChunk slab = new UnsafeMemoryChunk(DEFAULT_SLAB_SIZE);
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {
+        new UnsafeMemoryChunk(SMALL_SLAB), 
+        new UnsafeMemoryChunk(SMALL_SLAB), 
+        new UnsafeMemoryChunk(MEDIUM_SLAB), 
+        slab});
+    this.freeListManager.allocate(SMALL_SLAB-8+1);
+    this.freeListManager.allocate(DEFAULT_SLAB_SIZE-8);
+    this.freeListManager.allocate(SMALL_SLAB-8+1);
+    assertThat(this.freeListManager.getFreeMemory()).isEqualTo(SMALL_SLAB*2+MEDIUM_SLAB-((SMALL_SLAB+8)*2));
+    assertThat(this.freeListManager.getUsedMemory()).isEqualTo(DEFAULT_SLAB_SIZE+(SMALL_SLAB+8)*2);
+    assertThat(this.freeListManager.getTotalMemory()).isEqualTo(DEFAULT_SLAB_SIZE+MEDIUM_SLAB+SMALL_SLAB*2);
+  }
+  
+  @Test
+  public void compactWithLargeChunkSizeReturnsFalse() {
+    int SMALL_SLAB = 16;
+    int MEDIUM_SLAB = 128;
+    UnsafeMemoryChunk slab = new UnsafeMemoryChunk(DEFAULT_SLAB_SIZE);
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {
+        new UnsafeMemoryChunk(SMALL_SLAB), 
+        new UnsafeMemoryChunk(SMALL_SLAB), 
+        new UnsafeMemoryChunk(MEDIUM_SLAB), 
+        slab});
+    ArrayList<ObjectChunk> chunks = new ArrayList<>();
+    chunks.add(this.freeListManager.allocate(SMALL_SLAB-8+1));
+    chunks.add(this.freeListManager.allocate(DEFAULT_SLAB_SIZE/2-8));
+    chunks.add(this.freeListManager.allocate(DEFAULT_SLAB_SIZE/2-8));
+    chunks.add(this.freeListManager.allocate(SMALL_SLAB-8+1));
+    for (ObjectChunk c: chunks) {
+      ObjectChunk.release(c.getMemoryAddress(), this.freeListManager);
+    }
+    this.freeListManager.firstCompact = false;
+    assertThat(this.freeListManager.compact(DEFAULT_SLAB_SIZE+1)).isFalse();
+  }
+  
+  @Test
+  public void compactWithChunkSizeOfMaxSlabReturnsTrue() {
+    int SMALL_SLAB = 16;
+    int MEDIUM_SLAB = 128;
+    UnsafeMemoryChunk slab = new UnsafeMemoryChunk(DEFAULT_SLAB_SIZE);
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {
+        new UnsafeMemoryChunk(SMALL_SLAB), 
+        new UnsafeMemoryChunk(SMALL_SLAB), 
+        new UnsafeMemoryChunk(MEDIUM_SLAB), 
+        slab});
+    ArrayList<ObjectChunk> chunks = new ArrayList<>();
+    chunks.add(this.freeListManager.allocate(SMALL_SLAB-8+1));
+    chunks.add(this.freeListManager.allocate(DEFAULT_SLAB_SIZE/2-8));
+    chunks.add(this.freeListManager.allocate(DEFAULT_SLAB_SIZE/2-8));
+    chunks.add(this.freeListManager.allocate(SMALL_SLAB-8+1));
+    for (ObjectChunk c: chunks) {
+      ObjectChunk.release(c.getMemoryAddress(), this.freeListManager);
+    }
+    
+    assertThat(this.freeListManager.compact(DEFAULT_SLAB_SIZE)).isTrue();
+    //assertThat(this.freeListManager.getFragmentList()).hasSize(4); // TODO intermittently fails because Fragments may be merged
+  }
+  
+  @Test
+  public void compactWithLiveChunks() {
+    int SMALL_SLAB = 16;
+    int MEDIUM_SLAB = 128;
+    UnsafeMemoryChunk slab = new UnsafeMemoryChunk(DEFAULT_SLAB_SIZE);
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {
+        new UnsafeMemoryChunk(SMALL_SLAB), 
+        new UnsafeMemoryChunk(SMALL_SLAB), 
+        new UnsafeMemoryChunk(MEDIUM_SLAB), 
+        slab});
+    ArrayList<ObjectChunk> chunks = new ArrayList<>();
+    chunks.add(this.freeListManager.allocate(SMALL_SLAB-8+1));
+    this.freeListManager.allocate(DEFAULT_SLAB_SIZE/2-8);
+    chunks.add(this.freeListManager.allocate(DEFAULT_SLAB_SIZE/2-8));
+    this.freeListManager.allocate(SMALL_SLAB-8+1);
+    for (ObjectChunk c: chunks) {
+      ObjectChunk.release(c.getMemoryAddress(), this.freeListManager);
+    }
+    
+    assertThat(this.freeListManager.compact(DEFAULT_SLAB_SIZE/2)).isTrue();
+  }
+  
+  @Test
+  public void compactAfterAllocatingAll() {
+    setUpSingleSlabManager();
+    ObjectChunk c = freeListManager.allocate(DEFAULT_SLAB_SIZE-8);
+    this.freeListManager.firstCompact = false;
+    assertThat(this.freeListManager.compact(1)).isFalse();
+    // call compact twice for extra code coverage
+    assertThat(this.freeListManager.compact(1)).isFalse();
+    assertThat(this.freeListManager.getFragmentList()).isEmpty();
+  }
+  
+  @Test
+  public void afterAllocatingAllOneSizeCompactToAllocateDifferentSize() {
+    setUpSingleSlabManager();
+    ArrayList<ObjectChunk> chunksToFree = new ArrayList<>();
+    ArrayList<ObjectChunk> chunksToFreeLater = new ArrayList<>();
+    int ALLOCATE_COUNT = 1000;
+    ObjectChunk bigChunk = freeListManager.allocate(DEFAULT_SLAB_SIZE-8-(ALLOCATE_COUNT*32)-256-256);
+    for (int i=0; i < ALLOCATE_COUNT; i++) {
+      ObjectChunk c = freeListManager.allocate(24);
+      if (i%3 != 2) {
+        chunksToFree.add(c);
+      } else {
+        chunksToFreeLater.add(c);
+      }
+    }
+    ObjectChunk c1 = freeListManager.allocate(64-8);
+    ObjectChunk c2 = freeListManager.allocate(64-8);
+    ObjectChunk c3 = freeListManager.allocate(64-8);
+    ObjectChunk c4 = freeListManager.allocate(64-8);
+
+    ObjectChunk mediumChunk1 = freeListManager.allocate(128-8);
+    ObjectChunk mediumChunk2 = freeListManager.allocate(128-8);
+
+    ObjectChunk.release(bigChunk.getMemoryAddress(), freeListManager);
+    int s = chunksToFree.size();
+    for (int i=s/2; i >=0; i--) {
+      ObjectChunk c = chunksToFree.get(i);
+      ObjectChunk.release(c.getMemoryAddress(), freeListManager);
+    }
+    for (int i=(s/2)+1; i < s; i++) {
+      ObjectChunk c = chunksToFree.get(i);
+      ObjectChunk.release(c.getMemoryAddress(), freeListManager);
+    }
+    ObjectChunk.release(c3.getMemoryAddress(), freeListManager);
+    ObjectChunk.release(c1.getMemoryAddress(), freeListManager);
+    ObjectChunk.release(c2.getMemoryAddress(), freeListManager);
+    ObjectChunk.release(c4.getMemoryAddress(), freeListManager);
+    ObjectChunk.release(mediumChunk1.getMemoryAddress(), freeListManager);
+    ObjectChunk.release(mediumChunk2.getMemoryAddress(), freeListManager);
+    this.freeListManager.firstCompact = false;
+    assertThat(freeListManager.compact(DEFAULT_SLAB_SIZE-(ALLOCATE_COUNT*32))).isFalse();
+    for (int i=0; i < ((256*2)/96); i++) {
+      ObjectChunk.release(chunksToFreeLater.get(i).getMemoryAddress(), freeListManager);
+    }
+    assertThat(freeListManager.compact(DEFAULT_SLAB_SIZE-(ALLOCATE_COUNT*32))).isTrue();
+  }
+  
+  @Test
+  public void afterAllocatingAndFreeingCompact() {
+    int slabSize = 1024*3;
+    setUpSingleSlabManager(slabSize);
+    ObjectChunk bigChunk1 = freeListManager.allocate(slabSize/3-8);
+    ObjectChunk bigChunk2 = freeListManager.allocate(slabSize/3-8);
+    ObjectChunk bigChunk3 = freeListManager.allocate(slabSize/3-8);
+    this.freeListManager.firstCompact = false;
+    assertThat(freeListManager.compact(1)).isFalse();
+    ObjectChunk.release(bigChunk3.getMemoryAddress(), freeListManager);
+    ObjectChunk.release(bigChunk2.getMemoryAddress(), freeListManager);
+    ObjectChunk.release(bigChunk1.getMemoryAddress(), freeListManager);
+    assertThat(freeListManager.compact(slabSize)).isTrue();
+  }
+  
+  @Test
+  public void compactWithEmptyTinyFreeList() {
+    setUpSingleSlabManager();
+    Fragment originalFragment = this.freeListManager.getFragmentList().get(0);
+    ObjectChunk c = freeListManager.allocate(16);
+    ObjectChunk.release(c.getMemoryAddress(), this.freeListManager);
+    c = freeListManager.allocate(16);
+    this.freeListManager.firstCompact = false;
+    assertThat(this.freeListManager.compact(1)).isTrue();
+    assertThat(this.freeListManager.getFragmentList()).hasSize(1);
+    Fragment compactedFragment = this.freeListManager.getFragmentList().get(0);
+    assertThat(compactedFragment.getSize()).isEqualTo(originalFragment.getSize()-(16+8));
+    assertThat(compactedFragment.getMemoryAddress()).isEqualTo(originalFragment.getMemoryAddress()+(16+8));
+  }
+  
+  @Test
+  public void allocationsThatLeaveLessThanMinChunkSizeFreeInAFragment() {
+    int SMALL_SLAB = 16;
+    int MEDIUM_SLAB = 128;
+    UnsafeMemoryChunk slab = new UnsafeMemoryChunk(DEFAULT_SLAB_SIZE);
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {
+        new UnsafeMemoryChunk(SMALL_SLAB), 
+        new UnsafeMemoryChunk(SMALL_SLAB), 
+        new UnsafeMemoryChunk(MEDIUM_SLAB), 
+        slab});
+    this.freeListManager.allocate(DEFAULT_SLAB_SIZE-8-(ObjectChunk.MIN_CHUNK_SIZE-1));
+    this.freeListManager.allocate(MEDIUM_SLAB-8-(ObjectChunk.MIN_CHUNK_SIZE-1));
+    
+    assertThat(this.freeListManager.compact(SMALL_SLAB)).isTrue();
+  }
+ @Test
+  public void maxAllocationUsesAllMemory() {
+    setUpSingleSlabManager();
+
+    this.freeListManager.allocate(DEFAULT_SLAB_SIZE-8);
+
+    assertThat(this.freeListManager.getFreeMemory()).isZero();
+    assertThat(this.freeListManager.getUsedMemory()).isEqualTo(DEFAULT_SLAB_SIZE);
+  }
+
+
+  @Test
+  public void overMaxAllocationFails() {
+    setUpSingleSlabManager();
+    OutOfOffHeapMemoryListener ooohml = mock(OutOfOffHeapMemoryListener.class);
+    when(this.ma.getOutOfOffHeapMemoryListener()).thenReturn(ooohml);
+
+    catchException(this.freeListManager).allocate(DEFAULT_SLAB_SIZE-7);
+
+    verify(ooohml).outOfOffHeapMemory(caughtException());
+  }
+  
+  @Test(expected = AssertionError.class)
+  public void allocateNegativeThrowsAssertion() {
+    setUpSingleSlabManager();
+    this.freeListManager.allocate(-123);
+  }
+  
+  @Test
+  public void hugeMultipleLessThanZeroIsIllegal() {
+    try {
+      FreeListManager.verifyHugeMultiple(-1);
+      fail("expected IllegalStateException");
+    } catch (IllegalStateException expected) {
+      assertThat(expected.getMessage()).contains("HUGE_MULTIPLE must be >= 0 and <= " + FreeListManager.HUGE_MULTIPLE + " but it was -1");
+    }
+  }
+  @Test
+  public void hugeMultipleGreaterThan256IsIllegal() {
+    try {
+      FreeListManager.verifyHugeMultiple(257);
+      fail("expected IllegalStateException");
+    } catch (IllegalStateException expected) {
+      assertThat(expected.getMessage()).contains("HUGE_MULTIPLE must be >= 0 and <= 256 but it was 257");
+    }
+  }
+  @Test
+  public void hugeMultipleof256IsLegal() {
+    FreeListManager.verifyHugeMultiple(256);
+  }
+  
+  @Test
+  public void offHeapFreeListCountLessThanZeroIsIllegal() {
+    try {
+      FreeListManager.verifyOffHeapFreeListCount(-1);
+      fail("expected IllegalStateException");
+    } catch (IllegalStateException expected) {
+      assertThat(expected.getMessage()).contains("gemfire.OFF_HEAP_FREE_LIST_COUNT must be >= 1.");
+    }
+  }
+  @Test
+  public void offHeapFreeListCountOfZeroIsIllegal() {
+    try {
+      FreeListManager.verifyOffHeapFreeListCount(0);
+      fail("expected IllegalStateException");
+    } catch (IllegalStateException expected) {
+      assertThat(expected.getMessage()).contains("gemfire.OFF_HEAP_FREE_LIST_COUNT must be >= 1.");
+    }
+  }
+  @Test
+  public void offHeapFreeListCountOfOneIsLegal() {
+    FreeListManager.verifyOffHeapFreeListCount(1);
+  }
+  @Test
+  public void offHeapAlignmentLessThanZeroIsIllegal() {
+    try {
+      FreeListManager.verifyOffHeapAlignment(-1);
+      fail("expected IllegalStateException");
+    } catch (IllegalStateException expected) {
+      assertThat(expected.getMessage()).contains("gemfire.OFF_HEAP_ALIGNMENT must be a multiple of 8");
+    }
+  }
+  @Test
+  public void offHeapAlignmentNotAMultipleOf8IsIllegal() {
+    try {
+      FreeListManager.verifyOffHeapAlignment(9);
+      fail("expected IllegalStateException");
+    } catch (IllegalStateException expected) {
+      assertThat(expected.getMessage()).contains("gemfire.OFF_HEAP_ALIGNMENT must be a multiple of 8");
+    }
+  }
+  @Test
+  public void offHeapAlignmentGreaterThan256IsIllegal() {
+    try {
+      FreeListManager.verifyOffHeapAlignment(256+8);
+      fail("expected IllegalStateException");
+    } catch (IllegalStateException expected) {
+      assertThat(expected.getMessage()).contains("gemfire.OFF_HEAP_ALIGNMENT must be <= 256");
+    }
+  }
+  @Test
+  public void offHeapAlignmentOf256IsLegal() {
+    FreeListManager.verifyOffHeapAlignment(256);
+  }
+  
+  @Test
+  public void okToReuseNull() {
+    setUpSingleSlabManager();
+    assertThat(this.freeListManager.okToReuse(null)).isTrue();
+  }
+  
+  @Test
+  public void okToReuseSameSlabs() {
+    UnsafeMemoryChunk slab = new UnsafeMemoryChunk(DEFAULT_SLAB_SIZE);
+    UnsafeMemoryChunk[] slabs = new UnsafeMemoryChunk[] {slab};
+    this.freeListManager = createFreeListManager(ma, slabs);
+    assertThat(this.freeListManager.okToReuse(slabs)).isTrue();
+  }
+  @Test
+  public void notOkToReuseDifferentSlabs() {
+    UnsafeMemoryChunk slab = new UnsafeMemoryChunk(DEFAULT_SLAB_SIZE);
+    UnsafeMemoryChunk[] slabs = new UnsafeMemoryChunk[] {slab};
+    this.freeListManager = createFreeListManager(ma, slabs);
+    UnsafeMemoryChunk[] slabs2 = new UnsafeMemoryChunk[] {slab};
+    assertThat(this.freeListManager.okToReuse(slabs2)).isFalse();
+  }
+  @Test
+  public void firstSlabAlwaysLargest() {
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {
+        new UnsafeMemoryChunk(10), 
+        new UnsafeMemoryChunk(100)});
+    assertThat(this.freeListManager.getLargestSlabSize()).isEqualTo(10);
+  }
+
+  @Test
+  public void findSlab() {
+    UnsafeMemoryChunk chunk = new UnsafeMemoryChunk(10);
+    long address = chunk.getMemoryAddress();
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {chunk});
+    assertThat(this.freeListManager.findSlab(address)).isEqualTo(0);
+    assertThat(this.freeListManager.findSlab(address+9)).isEqualTo(0);
+    catchException(this.freeListManager).findSlab(address-1);
+    assertThat((Exception)caughtException())
+    .isExactlyInstanceOf(IllegalStateException.class)
+    .hasMessage("could not find a slab for addr " + (address-1));
+    catchException(this.freeListManager).findSlab(address+10);
+    assertThat((Exception)caughtException())
+    .isExactlyInstanceOf(IllegalStateException.class)
+    .hasMessage("could not find a slab for addr " + (address+10));
+  }
+  
+  @Test
+  public void findSecondSlab() {
+    UnsafeMemoryChunk chunk = new UnsafeMemoryChunk(10);
+    long address = chunk.getMemoryAddress();
+    UnsafeMemoryChunk slab = new UnsafeMemoryChunk(DEFAULT_SLAB_SIZE);
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {slab, chunk});
+    assertThat(this.freeListManager.findSlab(address)).isEqualTo(1);
+    assertThat(this.freeListManager.findSlab(address+9)).isEqualTo(1);
+    catchException(this.freeListManager).findSlab(address-1);
+    assertThat((Exception)caughtException())
+    .isExactlyInstanceOf(IllegalStateException.class)
+    .hasMessage("could not find a slab for addr " + (address-1));
+    catchException(this.freeListManager).findSlab(address+10);
+    assertThat((Exception)caughtException())
+    .isExactlyInstanceOf(IllegalStateException.class)
+    .hasMessage("could not find a slab for addr " + (address+10));
+  }
+  
+  @Test
+  public void validateAddressWithinSlab() {
+    UnsafeMemoryChunk chunk = new UnsafeMemoryChunk(10);
+    long address = chunk.getMemoryAddress();
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {chunk});
+    assertThat(this.freeListManager.validateAddressAndSizeWithinSlab(address, -1)).isTrue();
+    assertThat(this.freeListManager.validateAddressAndSizeWithinSlab(address+9, -1)).isTrue();
+    assertThat(this.freeListManager.validateAddressAndSizeWithinSlab(address-1, -1)).isFalse();
+    assertThat(this.freeListManager.validateAddressAndSizeWithinSlab(address+10, -1)).isFalse();
+  }
+  
+  @Test
+  public void validateAddressAndSizeWithinSlab() {
+    UnsafeMemoryChunk chunk = new UnsafeMemoryChunk(10);
+    long address = chunk.getMemoryAddress();
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {chunk});
+    assertThat(this.freeListManager.validateAddressAndSizeWithinSlab(address, 1)).isTrue();
+    assertThat(this.freeListManager.validateAddressAndSizeWithinSlab(address, 10)).isTrue();
+    catchException(this.freeListManager).validateAddressAndSizeWithinSlab(address, 0);
+    assertThat((Exception)caughtException())
+    .isExactlyInstanceOf(IllegalStateException.class)
+    .hasMessage(" address 0x" + Long.toString(address+0-1, 16) + " does not address the original slab memory");
+    catchException(this.freeListManager).validateAddressAndSizeWithinSlab(address, 11);
+    assertThat((Exception)caughtException())
+    .isExactlyInstanceOf(IllegalStateException.class)
+    .hasMessage(" address 0x" + Long.toString(address+11-1, 16) + " does not address the original slab memory");
+  }
+  
+  @Test
+  public void descriptionOfOneSlab() {
+    UnsafeMemoryChunk chunk = new UnsafeMemoryChunk(10);
+    long address = chunk.getMemoryAddress();
+    long endAddress = address+10;
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {chunk});
+    StringBuilder sb = new StringBuilder();
+    this.freeListManager.getSlabDescriptions(sb);
+    assertThat(sb.toString()).isEqualTo("[" + Long.toString(address, 16) + ".." + Long.toString(endAddress, 16) + "] ");
+  }
+
+  @Test
+  public void orderBlocksContainsFragment() {
+    UnsafeMemoryChunk chunk = new UnsafeMemoryChunk(10);
+    long address = chunk.getMemoryAddress();
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {chunk});
+    List<MemoryBlock> ob = this.freeListManager.getOrderedBlocks();
+    assertThat(ob).hasSize(1);
+    assertThat(ob.get(0).getMemoryAddress()).isEqualTo(address);
+    assertThat(ob.get(0).getBlockSize()).isEqualTo(10);
+  }
+  
+  @Test
+  public void orderBlocksContainsTinyFree() {
+    UnsafeMemoryChunk chunk = new UnsafeMemoryChunk(96);
+    long address = chunk.getMemoryAddress();
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {chunk});
+    ObjectChunk c = this.freeListManager.allocate(24);
+    ObjectChunk c2 = this.freeListManager.allocate(24);
+    ObjectChunk.release(c.getMemoryAddress(), this.freeListManager);
+
+    List<MemoryBlock> ob = this.freeListManager.getOrderedBlocks();
+    assertThat(ob).hasSize(3);
+  }
+
+  @Test
+  public void allocatedBlocksEmptyIfNoAllocations() {
+    UnsafeMemoryChunk chunk = new UnsafeMemoryChunk(10);
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {chunk});
+    List<MemoryBlock> ob = this.freeListManager.getAllocatedBlocks();
+    assertThat(ob).hasSize(0);
+  }
+
+  @Test
+  public void allocatedBlocksEmptyAfterFree() {
+    UnsafeMemoryChunk chunk = new UnsafeMemoryChunk(96);
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {chunk});
+    ObjectChunk c = this.freeListManager.allocate(24);
+    ObjectChunk.release(c.getMemoryAddress(), this.freeListManager);
+    List<MemoryBlock> ob = this.freeListManager.getAllocatedBlocks();
+    assertThat(ob).hasSize(0);
+  }
+
+  @Test
+  public void allocatedBlocksHasAllocatedChunk() {
+    UnsafeMemoryChunk chunk = new UnsafeMemoryChunk(96);
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {chunk});
+    ObjectChunk c = this.freeListManager.allocate(24);
+    List<MemoryBlock> ob = this.freeListManager.getAllocatedBlocks();
+    assertThat(ob).hasSize(1);
+    assertThat(ob.get(0).getMemoryAddress()).isEqualTo(c.getMemoryAddress());
+  }
+  
+  @Test
+  public void allocatedBlocksHasBothAllocatedChunks() {
+    UnsafeMemoryChunk chunk = new UnsafeMemoryChunk(96);
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {chunk});
+    ObjectChunk c = this.freeListManager.allocate(24);
+    ObjectChunk c2 = this.freeListManager.allocate(33);
+    List<MemoryBlock> ob = this.freeListManager.getAllocatedBlocks();
+    assertThat(ob).hasSize(2);
+  }
+  
+  @Test
+  public void allocateFromFragmentWithBadIndexesReturnsNull() {
+    UnsafeMemoryChunk chunk = new UnsafeMemoryChunk(96);
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {chunk});
+    assertThat(this.freeListManager.allocateFromFragment(-1, 32)).isNull();
+    assertThat(this.freeListManager.allocateFromFragment(1, 32)).isNull();
+  }
+
+  @Test
+  public void testLogging() {
+    UnsafeMemoryChunk chunk = new UnsafeMemoryChunk(32);
+    UnsafeMemoryChunk chunk2 = new UnsafeMemoryChunk(1024*1024*5);
+    this.freeListManager = createFreeListManager(ma, new UnsafeMemoryChunk[] {chunk, chunk2});
+    ObjectChunk c = this.freeListManager.allocate(24);
+    ObjectChunk c2 = this.freeListManager.allocate(1024*1024);
+    ObjectChunk.release(c.getMemoryAddress(), this.freeListManager);
+    ObjectChunk.release(c2.getMemoryAddress(), this.freeListManager);
+    
+    LogWriter lw = mock(LogWriter.class);
+    this.freeListManager.logOffHeapState(lw, 1024);
+  }
+  /**
+   * Just like Fragment except that the first time allocate is called
+   * it returns false indicating that the allocate failed.
+   * In a real system this would only happen if a concurrent allocate
+   * happened. This allows better code coverage.
+   */
+  private static class TestableFragment extends Fragment {
+    private boolean allocateCalled = false;
+    public TestableFragment(long addr, int size) {
+      super(addr, size);
+    }
+    @Override
+    public boolean allocate(int oldOffset, int newOffset) {
+      if (!allocateCalled) {
+        allocateCalled = true;
+        return false;
+      }
+      return super.allocate(oldOffset, newOffset);
+    }
+  }
+  private static class TestableFreeListManager extends FreeListManager {
+    private boolean firstTime = true;
+    
+    @Override
+    protected Fragment createFragment(long addr, int size) {
+      return new TestableFragment(addr, size);
+    }
+
+    @Override
+    protected SyncChunkStack createFreeListForEmptySlot(AtomicReferenceArray<SyncChunkStack> freeLists, int idx) {
+      if (this.firstTime) {
+        this.firstTime = false;
+        SyncChunkStack clq = super.createFreeListForEmptySlot(freeLists, idx);
+        if (!freeLists.compareAndSet(idx, null, clq)) {
+          fail("this should never happen. Indicates a concurrent modification");
+        }
+      }
+      return super.createFreeListForEmptySlot(freeLists, idx);
+    }
+
+    public boolean firstCompact = true;
+    @Override
+    protected void afterCompactCountFetched() {
+      if (this.firstCompact) {
+        this.firstCompact = false;
+        // Force compact into thinking a concurrent compaction happened.
+        this.compactCount.incrementAndGet();
+      }
+    }
+    
+    public TestableFreeListManager(SimpleMemoryAllocatorImpl ma, AddressableMemoryChunk[] slabs) {
+      super(ma, slabs);
+    }
+    
+  }
+}