test/hotspot/gtest/gc/shared/test_oopStorage.cpp
changeset 49977 9f758f0bb058
parent 49711 4a7addb5762c
child 50209 2fdce199fcb9
--- a/test/hotspot/gtest/gc/shared/test_oopStorage.cpp	Thu May 03 14:13:20 2018 -0700
+++ b/test/hotspot/gtest/gc/shared/test_oopStorage.cpp	Thu May 03 17:36:50 2018 -0400
@@ -53,9 +53,10 @@
 public:
   typedef OopStorage::Block Block;
   typedef OopStorage::BlockList BlockList;
+  typedef OopStorage::BlockArray BlockArray;
 
-  static BlockList& active_list(OopStorage& storage) {
-    return storage._active_list;
+  static BlockArray& active_array(const OopStorage& storage) {
+    return *storage._active_array;
   }
 
   static BlockList& allocate_list(OopStorage& storage) {
@@ -96,20 +97,25 @@
   static size_t memory_per_block() {
     return Block::allocation_size();
   }
+
+  static void block_array_set_block_count(BlockArray* blocks, size_t count) {
+    blocks->_block_count = count;
+  }
 };
 
 typedef OopStorage::TestAccess TestAccess;
-// --- FIXME: Should be just Block, but that collides with opto Block
-//     when building with precompiled headers.  There really should be
-//     an opto namespace.
+
+// The "Oop" prefix is to avoid collision with similar opto names when
+// building with precompiled headers, or for consistency with that
+// workaround.  There really should be an opto namespace.
 typedef TestAccess::Block OopBlock;
-// --- FIXME: Similarly, this typedef collides with opto BlockList.
-// typedef TestAccess::BlockList BlockList;
+typedef TestAccess::BlockList OopBlockList;
+typedef TestAccess::BlockArray OopBlockArray;
 
 // Using EXPECT_EQ can't use NULL directly. Otherwise AIX build breaks.
 const OopBlock* const NULL_BLOCK = NULL;
 
-static size_t list_length(const TestAccess::BlockList& list) {
+static size_t list_length(const OopBlockList& list) {
   size_t result = 0;
   for (const OopBlock* block = list.chead();
        block != NULL;
@@ -119,7 +125,7 @@
   return result;
 }
 
-static void clear_list(TestAccess::BlockList& list) {
+static void clear_list(OopBlockList& list) {
   OopBlock* next;
   for (OopBlock* block = list.head(); block != NULL; block = next) {
     next = list.next(*block);
@@ -127,7 +133,7 @@
   }
 }
 
-static bool is_list_empty(const TestAccess::BlockList& list) {
+static bool is_list_empty(const OopBlockList& list) {
   return list.chead() == NULL;
 }
 
@@ -149,7 +155,7 @@
 }
 
 static size_t empty_block_count(const OopStorage& storage) {
-  const TestAccess::BlockList& list = TestAccess::allocate_list(storage);
+  const OopBlockList& list = TestAccess::allocate_list(storage);
   size_t count = 0;
   for (const OopBlock* block = list.ctail();
        (block != NULL) && block->is_empty();
@@ -158,6 +164,20 @@
   return count;
 }
 
+static size_t active_count(const OopStorage& storage) {
+  return TestAccess::active_array(storage).block_count();
+}
+
+static OopBlock* active_head(const OopStorage& storage) {
+  OopBlockArray& ba = TestAccess::active_array(storage);
+  size_t count = ba.block_count();
+  if (count == 0) {
+    return NULL;
+  } else {
+    return ba.at(count - 1);
+  }
+}
+
 class OopStorageTest : public ::testing::Test {
 public:
   OopStorageTest();
@@ -188,7 +208,6 @@
 
 OopStorageTest::~OopStorageTest() {
   clear_list(TestAccess::allocate_list(_storage));
-  clear_list(TestAccess::active_list(_storage));
 }
 
 class OopStorageTestWithAllocation : public OopStorageTest {
@@ -227,7 +246,7 @@
 static bool is_allocate_list_sorted(const OopStorage& storage) {
   // The allocate_list isn't strictly sorted.  Rather, all empty
   // blocks are segregated to the end of the list.
-  const TestAccess::BlockList& list = TestAccess::allocate_list(storage);
+  const OopBlockList& list = TestAccess::allocate_list(storage);
   const OopBlock* block = list.ctail();
   for ( ; (block != NULL) && block->is_empty(); block = list.prev(*block)) {}
   for ( ; block != NULL; block = list.prev(*block)) {
@@ -238,25 +257,25 @@
   return true;
 }
 
-static size_t total_allocation_count(const TestAccess::BlockList& list) {
+static size_t total_allocation_count(const OopStorage& storage) {
   size_t total_count = 0;
-  for (const OopBlock* block = list.chead();
-       block != NULL;
-       block = list.next(*block)) {
-    total_count += TestAccess::block_allocation_count(*block);
+  const OopBlockArray& ba = TestAccess::active_array(storage);
+  size_t limit = active_count(storage);
+  for (size_t i = 0; i < limit; ++i) {
+    total_count += TestAccess::block_allocation_count(*ba.at(i));
   }
   return total_count;
 }
 
 TEST_VM_F(OopStorageTest, allocate_one) {
-  EXPECT_TRUE(is_list_empty(TestAccess::active_list(_storage)));
+  EXPECT_EQ(0u, active_count(_storage));
   EXPECT_TRUE(is_list_empty(TestAccess::allocate_list(_storage)));
 
   oop* ptr = _storage.allocate();
   EXPECT_TRUE(ptr != NULL);
   EXPECT_EQ(1u, _storage.allocation_count());
 
-  EXPECT_EQ(1u, list_length(TestAccess::active_list(_storage)));
+  EXPECT_EQ(1u, active_count(_storage));
   EXPECT_EQ(1u, _storage.block_count());
   EXPECT_EQ(1u, list_length(TestAccess::allocate_list(_storage)));
 
@@ -264,7 +283,7 @@
 
   const OopBlock* block = TestAccess::allocate_list(_storage).chead();
   EXPECT_NE(block, (OopBlock*)NULL);
-  EXPECT_EQ(block, (TestAccess::active_list(_storage).chead()));
+  EXPECT_EQ(block, active_head(_storage));
   EXPECT_FALSE(TestAccess::block_is_empty(*block));
   EXPECT_FALSE(TestAccess::block_is_full(*block));
   EXPECT_EQ(1u, TestAccess::block_allocation_count(*block));
@@ -272,7 +291,7 @@
   release_entry(_storage, ptr);
   EXPECT_EQ(0u, _storage.allocation_count());
 
-  EXPECT_EQ(1u, list_length(TestAccess::active_list(_storage)));
+  EXPECT_EQ(1u, active_count(_storage));
   EXPECT_EQ(1u, _storage.block_count());
   EXPECT_EQ(1u, list_length(TestAccess::allocate_list(_storage)));
 
@@ -280,7 +299,7 @@
 
   const OopBlock* new_block = TestAccess::allocate_list(_storage).chead();
   EXPECT_EQ(block, new_block);
-  EXPECT_EQ(block, (TestAccess::active_list(_storage).chead()));
+  EXPECT_EQ(block, active_head(_storage));
   EXPECT_TRUE(TestAccess::block_is_empty(*block));
   EXPECT_FALSE(TestAccess::block_is_full(*block));
   EXPECT_EQ(0u, TestAccess::block_allocation_count(*block));
@@ -290,20 +309,19 @@
   static const size_t max_entries = 1000;
   oop* entries[max_entries];
 
-  TestAccess::BlockList& active_list = TestAccess::active_list(_storage);
-  TestAccess::BlockList& allocate_list = TestAccess::allocate_list(_storage);
+  OopBlockList& allocate_list = TestAccess::allocate_list(_storage);
 
-  EXPECT_TRUE(is_list_empty(active_list));
+  EXPECT_EQ(0u, active_count(_storage));
   EXPECT_EQ(0u, _storage.block_count());
   EXPECT_TRUE(is_list_empty(allocate_list));
 
   size_t allocated = 0;
   for ( ; allocated < max_entries; ++allocated) {
     EXPECT_EQ(allocated, _storage.allocation_count());
-    if (!is_list_empty(active_list)) {
-      EXPECT_EQ(1u, list_length(active_list));
+    if (active_count(_storage) != 0) {
+      EXPECT_EQ(1u, active_count(_storage));
       EXPECT_EQ(1u, _storage.block_count());
-      const OopBlock& block = *active_list.chead();
+      const OopBlock& block = *TestAccess::active_array(_storage).at(0);
       EXPECT_EQ(allocated, TestAccess::block_allocation_count(block));
       if (TestAccess::block_is_full(block)) {
         break;
@@ -316,10 +334,10 @@
   }
 
   EXPECT_EQ(allocated, _storage.allocation_count());
-  EXPECT_EQ(1u, list_length(active_list));
+  EXPECT_EQ(1u, active_count(_storage));
   EXPECT_EQ(1u, _storage.block_count());
   EXPECT_TRUE(is_list_empty(allocate_list));
-  const OopBlock& block = *active_list.chead();
+  const OopBlock& block = *TestAccess::active_array(_storage).at(0);
   EXPECT_TRUE(TestAccess::block_is_full(block));
   EXPECT_EQ(allocated, TestAccess::block_allocation_count(block));
 
@@ -336,19 +354,18 @@
   static const size_t max_entries = 1000;
   oop* entries[max_entries];
 
-  TestAccess::BlockList& active_list = TestAccess::active_list(_storage);
-  TestAccess::BlockList& allocate_list = TestAccess::allocate_list(_storage);
+  OopBlockList& allocate_list = TestAccess::allocate_list(_storage);
 
   EXPECT_EQ(0u, empty_block_count(_storage));
 
   entries[0] = _storage.allocate();
   ASSERT_TRUE(entries[0] != NULL);
-  EXPECT_EQ(1u, list_length(active_list));
+  EXPECT_EQ(1u, active_count(_storage));
   EXPECT_EQ(1u, _storage.block_count());
   EXPECT_EQ(1u, list_length(allocate_list));
   EXPECT_EQ(0u, empty_block_count(_storage));
 
-  const OopBlock* block = active_list.chead();
+  const OopBlock* block = TestAccess::active_array(_storage).at(0);
   EXPECT_EQ(1u, TestAccess::block_allocation_count(*block));
   EXPECT_EQ(block, allocate_list.chead());
 
@@ -363,14 +380,14 @@
       EXPECT_EQ(1u, list_length(allocate_list));
       block = allocate_list.chead();
       EXPECT_EQ(1u, TestAccess::block_allocation_count(*block));
-      EXPECT_EQ(block, active_list.chead());
+      EXPECT_EQ(block, active_head(_storage));
     } else if (TestAccess::block_is_full(*block)) {
       EXPECT_TRUE(is_list_empty(allocate_list));
       block = NULL;
     } else {
       EXPECT_FALSE(is_list_empty(allocate_list));
       EXPECT_EQ(block, allocate_list.chead());
-      EXPECT_EQ(block, active_list.chead());
+      EXPECT_EQ(block, active_head(_storage));
     }
   }
 
@@ -378,20 +395,18 @@
     EXPECT_NE(0u, TestAccess::block_allocation_count(*block));
     EXPECT_FALSE(is_list_empty(allocate_list));
     EXPECT_EQ(block, allocate_list.chead());
-    EXPECT_EQ(block, active_list.chead());
+    EXPECT_EQ(block, active_head(_storage));
   }
 
-  size_t active_count = list_length(active_list);
-
   for (size_t i = 0; i < max_entries; ++i) {
     release_entry(_storage, entries[i]);
     EXPECT_TRUE(is_allocate_list_sorted(_storage));
-    EXPECT_EQ(max_entries - (i + 1), total_allocation_count(active_list));
+    EXPECT_EQ(max_entries - (i + 1), total_allocation_count(_storage));
   }
 
-  EXPECT_EQ(list_length(active_list), list_length(allocate_list));
-  EXPECT_EQ(list_length(active_list), _storage.block_count());
-  EXPECT_EQ(list_length(active_list), empty_block_count(_storage));
+  EXPECT_EQ(active_count(_storage), list_length(allocate_list));
+  EXPECT_EQ(active_count(_storage), _storage.block_count());
+  EXPECT_EQ(active_count(_storage), empty_block_count(_storage));
   for (const OopBlock* block = allocate_list.chead();
        block != NULL;
        block = allocate_list.next(*block)) {
@@ -405,10 +420,9 @@
 
   EXPECT_EQ(0u, empty_block_count(_storage));
 
-  TestAccess::BlockList& active_list = TestAccess::active_list(_storage);
-  TestAccess::BlockList& allocate_list = TestAccess::allocate_list(_storage);
+  OopBlockList& allocate_list = TestAccess::allocate_list(_storage);
 
-  EXPECT_EQ(_max_entries, total_allocation_count(active_list));
+  EXPECT_EQ(_max_entries, total_allocation_count(_storage));
   EXPECT_GE(1u, list_length(allocate_list));
 
   // Release all entries in "random" order.
@@ -418,14 +432,14 @@
       release_entry(_storage, _entries[i]);
       _entries[i] = NULL;
       ++released;
-      EXPECT_EQ(_max_entries - released, total_allocation_count(active_list));
+      EXPECT_EQ(_max_entries - released, total_allocation_count(_storage));
       EXPECT_TRUE(is_allocate_list_sorted(_storage));
     }
   }
 
-  EXPECT_EQ(list_length(active_list), list_length(allocate_list));
-  EXPECT_EQ(list_length(active_list), _storage.block_count());
-  EXPECT_EQ(0u, total_allocation_count(active_list));
+  EXPECT_EQ(active_count(_storage), list_length(allocate_list));
+  EXPECT_EQ(active_count(_storage), _storage.block_count());
+  EXPECT_EQ(0u, total_allocation_count(_storage));
   EXPECT_EQ(list_length(allocate_list), empty_block_count(_storage));
 }
 
@@ -436,10 +450,9 @@
 
   EXPECT_EQ(0u, empty_block_count(_storage));
 
-  TestAccess::BlockList& active_list = TestAccess::active_list(_storage);
-  TestAccess::BlockList& allocate_list = TestAccess::allocate_list(_storage);
+  OopBlockList& allocate_list = TestAccess::allocate_list(_storage);
 
-  EXPECT_EQ(_max_entries, total_allocation_count(active_list));
+  EXPECT_EQ(_max_entries, total_allocation_count(_storage));
   EXPECT_GE(1u, list_length(allocate_list));
 
   // Release all entries in "random" order, "randomly" interspersed
@@ -452,20 +465,20 @@
       _entries[i] = NULL;
       ++released;
       ++total_released;
-      EXPECT_EQ(_max_entries - released, total_allocation_count(active_list));
+      EXPECT_EQ(_max_entries - released, total_allocation_count(_storage));
       EXPECT_TRUE(is_allocate_list_sorted(_storage));
       if (total_released % allocate_step == 0) {
         _entries[i] = _storage.allocate();
         --released;
-        EXPECT_EQ(_max_entries - released, total_allocation_count(active_list));
+        EXPECT_EQ(_max_entries - released, total_allocation_count(_storage));
         EXPECT_TRUE(is_allocate_list_sorted(_storage));
       }
     }
   }
 
-  EXPECT_EQ(list_length(active_list), list_length(allocate_list));
-  EXPECT_EQ(list_length(active_list), _storage.block_count());
-  EXPECT_EQ(0u, total_allocation_count(active_list));
+  EXPECT_EQ(active_count(_storage), list_length(allocate_list));
+  EXPECT_EQ(active_count(_storage), _storage.block_count());
+  EXPECT_EQ(0u, total_allocation_count(_storage));
   EXPECT_EQ(list_length(allocate_list), empty_block_count(_storage));
 }
 
@@ -1015,9 +1028,7 @@
 }
 
 TEST_VM_F(OopStorageTestWithAllocation, delete_empty_blocks_safepoint) {
-  TestAccess::BlockList& active_list = TestAccess::active_list(_storage);
-
-  size_t initial_active_size = list_length(active_list);
+  size_t initial_active_size = active_count(_storage);
   EXPECT_EQ(initial_active_size, _storage.block_count());
   ASSERT_LE(3u, initial_active_size); // Need at least 3 blocks for test
 
@@ -1026,7 +1037,7 @@
     release_entry(_storage, _entries[i]);
   }
 
-  EXPECT_EQ(initial_active_size, list_length(active_list));
+  EXPECT_EQ(initial_active_size, active_count(_storage));
   EXPECT_EQ(initial_active_size, _storage.block_count());
   EXPECT_EQ(3u, empty_block_count(_storage));
 
@@ -1036,14 +1047,12 @@
     VMThread::execute(&op);
   }
   EXPECT_EQ(0u, empty_block_count(_storage));
-  EXPECT_EQ(initial_active_size - 3, list_length(active_list));
+  EXPECT_EQ(initial_active_size - 3, active_count(_storage));
   EXPECT_EQ(initial_active_size - 3, _storage.block_count());
 }
 
 TEST_VM_F(OopStorageTestWithAllocation, delete_empty_blocks_concurrent) {
-  TestAccess::BlockList& active_list = TestAccess::active_list(_storage);
-
-  size_t initial_active_size = list_length(active_list);
+  size_t initial_active_size = active_count(_storage);
   EXPECT_EQ(initial_active_size, _storage.block_count());
   ASSERT_LE(3u, initial_active_size); // Need at least 3 blocks for test
 
@@ -1052,13 +1061,13 @@
     release_entry(_storage, _entries[i]);
   }
 
-  EXPECT_EQ(initial_active_size, list_length(active_list));
+  EXPECT_EQ(initial_active_size, active_count(_storage));
   EXPECT_EQ(initial_active_size, _storage.block_count());
   EXPECT_EQ(3u, empty_block_count(_storage));
 
   _storage.delete_empty_blocks_concurrent();
   EXPECT_EQ(0u, empty_block_count(_storage));
-  EXPECT_EQ(initial_active_size - 3, list_length(active_list));
+  EXPECT_EQ(initial_active_size - 3, active_count(_storage));
   EXPECT_EQ(initial_active_size - 3, _storage.block_count());
 }
 
@@ -1161,23 +1170,21 @@
 
 #endif // !PRODUCT
 
-//////////////////////////////////////////////////////////////////////////////
-// Unit tests for block lists
-
-class OopStorageBlockListTest : public ::testing::Test {
-public:
-  OopStorageBlockListTest() {
+class OopStorageBlockCollectionTest : public ::testing::Test {
+protected:
+  OopStorageBlockCollectionTest() {
     for (size_t i = 0; i < nvalues; ++i) {
       values[i] = OopBlock::new_block(pseudo_owner());
     }
   }
 
-  ~OopStorageBlockListTest() {
+  ~OopStorageBlockCollectionTest() {
     for (size_t i = 0; i < nvalues; ++i) {
       OopBlock::delete_block(*values[i]);
     }
   }
 
+public:
   static const size_t nvalues = 10;
   OopBlock* values[nvalues];
 
@@ -1190,11 +1197,13 @@
   }
 };
 
-const size_t OopStorageBlockListTest::nvalues;
-const void* const OopStorageBlockListTest::_pseudo_owner[] = {};
+const size_t OopStorageBlockCollectionTest::nvalues;
+const void* const OopStorageBlockCollectionTest::_pseudo_owner[] = {};
+
+class OopStorageBlockListTest : public OopStorageBlockCollectionTest {};
 
 TEST_F(OopStorageBlockListTest, empty_list) {
-  TestAccess::BlockList list(&OopBlock::get_active_entry);
+  OopBlockList list(&OopBlock::get_allocate_entry);
 
   EXPECT_TRUE(is_list_empty(list));
   EXPECT_EQ(NULL_BLOCK, list.head());
@@ -1203,7 +1212,7 @@
 }
 
 TEST_F(OopStorageBlockListTest, push_back) {
-  TestAccess::BlockList list(&OopBlock::get_active_entry);
+  OopBlockList list(&OopBlock::get_allocate_entry);
 
   for (size_t i = 0; i < nvalues; ++i) {
     list.push_back(*values[i]);
@@ -1233,7 +1242,7 @@
 }
 
 TEST_F(OopStorageBlockListTest, push_front) {
-  TestAccess::BlockList list(&OopBlock::get_active_entry);
+  OopBlockList list(&OopBlock::get_allocate_entry);
 
   for (size_t i = 0; i < nvalues; ++i) {
     list.push_front(*values[i]);
@@ -1264,7 +1273,7 @@
 
 class OopStorageBlockListTestWithList : public OopStorageBlockListTest {
 public:
-  OopStorageBlockListTestWithList() : list(&OopBlock::get_active_entry) {
+  OopStorageBlockListTestWithList() : list(&OopBlock::get_allocate_entry) {
     for (size_t i = 0; i < nvalues; ++i) {
       list.push_back(*values[i]);
     }
@@ -1274,7 +1283,7 @@
     clear_list(list);
   }
 
-  TestAccess::BlockList list;
+  OopBlockList list;
 };
 
 TEST_F(OopStorageBlockListTestWithList, unlink_front) {
@@ -1336,7 +1345,7 @@
 }
 
 TEST_F(OopStorageBlockListTest, single) {
-  TestAccess::BlockList list(&OopBlock::get_active_entry);
+  OopBlockList list(&OopBlock::get_allocate_entry);
 
   list.push_back(*values[0]);
   EXPECT_EQ(NULL_BLOCK, list.next(*values[0]));
@@ -1351,31 +1360,79 @@
   EXPECT_EQ(NULL_BLOCK, list.ctail());
 }
 
-TEST_F(OopStorageBlockListTestWithList, two_lists) {
-  TestAccess::BlockList list2(&OopBlock::get_allocate_entry);
-  for (size_t i = 0; i < nvalues; ++i) {
-    list2.push_front(*values[i]);
+class OopStorageBlockArrayTest : public OopStorageBlockCollectionTest {};
+
+TEST_F(OopStorageBlockArrayTest, empty_array) {
+  OopBlockArray* a = OopBlockArray::create(nvalues);
+
+  EXPECT_EQ(nvalues, a->size());
+  EXPECT_EQ(0u, a->block_count_acquire());
+  TestAccess::block_array_set_block_count(a, 2);
+  EXPECT_EQ(2u, a->block_count_acquire());
+  TestAccess::block_array_set_block_count(a, 0);
+  a->increment_refcount();
+  a->increment_refcount();
+  EXPECT_FALSE(a->decrement_refcount());
+  EXPECT_TRUE(a->decrement_refcount());
+
+  OopBlockArray::destroy(a);
+}
+
+TEST_F(OopStorageBlockArrayTest, push) {
+  OopBlockArray* a = OopBlockArray::create(nvalues - 1);
+
+  for (size_t i = 0; i < nvalues - 1; ++i) {
+    EXPECT_TRUE(a->push(values[i]));
+    EXPECT_EQ(i + 1, a->block_count_acquire());
+    EXPECT_EQ(values[i], a->at(i));
+  }
+  EXPECT_FALSE(a->push(values[nvalues - 1]));
+
+  TestAccess::block_array_set_block_count(a, 0);
+  OopBlockArray::destroy(a);
+}
+
+class OopStorageBlockArrayTestWithArray : public OopStorageBlockArrayTest {
+public:
+  OopStorageBlockArrayTestWithArray() : a(OopBlockArray::create(nvalues)) {
+    for (size_t i = 0; i < nvalues; ++i) {
+      a->push(values[i]);
+    }
   }
 
-  const OopBlock* active_block = list.chead();
-  const OopBlock* allocate_block = list2.ctail();
-  for (size_t i = 0; i < nvalues; ++i) {
-    EXPECT_EQ(active_block, allocate_block);
-    active_block = list.next(*active_block);
-    allocate_block = list2.prev(*allocate_block);
+  ~OopStorageBlockArrayTestWithArray() {
+    TestAccess::block_array_set_block_count(a, 0);
+    OopBlockArray::destroy(a);
   }
-  EXPECT_EQ(NULL_BLOCK, active_block);
-  EXPECT_EQ(NULL_BLOCK, allocate_block);
+
+  OopBlockArray* a;
+};
+
+TEST_F(OopStorageBlockArrayTestWithArray, remove0) {
+  a->remove(values[0]);
+  EXPECT_EQ(nvalues - 1, a->block_count_acquire());
+  EXPECT_EQ(values[nvalues - 1], a->at(0));
+  for (size_t i = 1; i < nvalues - 1; ++i) {
+    EXPECT_EQ(values[i], a->at(i));
+  }
+}
 
-  for (size_t i = 0; i < nvalues; ++i) {
-    list2.unlink(*values[i]);
+TEST_F(OopStorageBlockArrayTestWithArray, remove3) {
+  a->remove(values[3]);
+  EXPECT_EQ(nvalues - 1, a->block_count_acquire());
+  for (size_t i = 0; i < 3; ++i) {
+    EXPECT_EQ(values[i], a->at(i));
   }
-  EXPECT_TRUE(is_list_empty(list2));
+  EXPECT_EQ(values[nvalues - 1], a->at(3));
+  for (size_t i = 4; i < nvalues - 1; ++i) {
+    EXPECT_EQ(values[i], a->at(i));
+  }
+}
 
-  active_block = list.chead();
-  for (size_t i = 0; i < nvalues; ++i) {
-    EXPECT_EQ(active_block, values[i]);
-    active_block = list.next(*active_block);
+TEST_F(OopStorageBlockArrayTestWithArray, remove_last) {
+  a->remove(values[nvalues - 1]);
+  EXPECT_EQ(nvalues - 1, a->block_count_acquire());
+  for (size_t i = 0; i < nvalues - 1; ++i) {
+    EXPECT_EQ(values[i], a->at(i));
   }
-  EXPECT_EQ(NULL_BLOCK, active_block);
 }