8015255: NPG: Don't waste fragment at the end of a VirtualSpaceNode before retiring it.

Tue, 15 Oct 2013 13:56:46 +0200

author
mgerdin
date
Tue, 15 Oct 2013 13:56:46 +0200
changeset 6004
1d1ea10fe09f
parent 5946
bf9e50c573ad
child 6005
91a88c8450f4

8015255: NPG: Don't waste fragment at the end of a VirtualSpaceNode before retiring it.
Summary: Chunk up the last piece of committed memory in a VSN when getting a new one.
Reviewed-by: stefank, jmasa

src/share/vm/memory/metaspace.cpp file | annotate | diff | comparison | revisions
src/share/vm/prims/jni.cpp file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/vm/memory/metaspace.cpp	Thu Oct 17 06:29:58 2013 -0700
     1.2 +++ b/src/share/vm/memory/metaspace.cpp	Tue Oct 15 13:56:46 2013 +0200
     1.3 @@ -75,8 +75,7 @@
     1.4    ClassSmallChunk = 256,
     1.5    SmallChunk = 512,
     1.6    ClassMediumChunk = 4 * K,
     1.7 -  MediumChunk = 8 * K,
     1.8 -  HumongousChunkGranularity = 8
     1.9 +  MediumChunk = 8 * K
    1.10  };
    1.11  
    1.12  static ChunkIndex next_chunk_index(ChunkIndex i) {
    1.13 @@ -92,6 +91,7 @@
    1.14  
    1.15  // Manages the global free lists of chunks.
    1.16  class ChunkManager : public CHeapObj<mtInternal> {
    1.17 +  friend class TestVirtualSpaceNodeTest;
    1.18  
    1.19    // Free list of chunks of different sizes.
    1.20    //   SpecializedChunk
    1.21 @@ -257,6 +257,8 @@
    1.22    // VirtualSpace
    1.23    Metachunk* first_chunk() { return (Metachunk*) bottom(); }
    1.24  
    1.25 +  // Committed but unused space in the virtual space
    1.26 +  size_t free_words_in_vs() const;
    1.27   public:
    1.28  
    1.29    VirtualSpaceNode(size_t byte_size);
    1.30 @@ -301,7 +303,6 @@
    1.31    // used and capacity in this single entry in the list
    1.32    size_t used_words_in_vs() const;
    1.33    size_t capacity_words_in_vs() const;
    1.34 -  size_t free_words_in_vs() const;
    1.35  
    1.36    bool initialize();
    1.37  
    1.38 @@ -319,6 +320,13 @@
    1.39    // in the node from any freelist.
    1.40    void purge(ChunkManager* chunk_manager);
    1.41  
    1.42 +  // If an allocation doesn't fit in the current node a new node is created.
    1.43 +  // Allocate chunks out of the remaining committed space in this node
    1.44 +  // to avoid wasting that memory.
    1.45 +  // This always adds up because all the chunk sizes are multiples of
    1.46 +  // the smallest chunk size.
    1.47 +  void retire(ChunkManager* chunk_manager);
    1.48 +
    1.49  #ifdef ASSERT
    1.50    // Debug support
    1.51    void mangle();
    1.52 @@ -461,6 +469,10 @@
    1.53    // and is typically followed by the allocation of a chunk.
    1.54    bool create_new_virtual_space(size_t vs_word_size);
    1.55  
    1.56 +  // Chunk up the unused committed space in the current
    1.57 +  // virtual space and add the chunks to the free list.
    1.58 +  void retire_current_virtual_space();
    1.59 +
    1.60   public:
    1.61    VirtualSpaceList(size_t word_size);
    1.62    VirtualSpaceList(ReservedSpace rs);
    1.63 @@ -624,10 +636,12 @@
    1.64    bool is_class() { return _mdtype == Metaspace::ClassType; }
    1.65  
    1.66    // Accessors
    1.67 -  size_t specialized_chunk_size() { return SpecializedChunk; }
    1.68 -  size_t small_chunk_size() { return (size_t) is_class() ? ClassSmallChunk : SmallChunk; }
    1.69 -  size_t medium_chunk_size() { return (size_t) is_class() ? ClassMediumChunk : MediumChunk; }
    1.70 -  size_t medium_chunk_bunch() { return medium_chunk_size() * MediumChunkMultiple; }
    1.71 +  size_t specialized_chunk_size() { return (size_t) is_class() ? ClassSpecializedChunk : SpecializedChunk; }
    1.72 +  size_t small_chunk_size()       { return (size_t) is_class() ? ClassSmallChunk : SmallChunk; }
    1.73 +  size_t medium_chunk_size()      { return (size_t) is_class() ? ClassMediumChunk : MediumChunk; }
    1.74 +  size_t medium_chunk_bunch()     { return medium_chunk_size() * MediumChunkMultiple; }
    1.75 +
    1.76 +  size_t smallest_chunk_size()  { return specialized_chunk_size(); }
    1.77  
    1.78    size_t allocated_blocks_words() const { return _allocated_blocks_words; }
    1.79    size_t allocated_blocks_bytes() const { return _allocated_blocks_words * BytesPerWord; }
    1.80 @@ -1056,6 +1070,35 @@
    1.81  #endif
    1.82  }
    1.83  
    1.84 +void VirtualSpaceList::retire_current_virtual_space() {
    1.85 +  assert_lock_strong(SpaceManager::expand_lock());
    1.86 +
    1.87 +  VirtualSpaceNode* vsn = current_virtual_space();
    1.88 +
    1.89 +  ChunkManager* cm = is_class() ? Metaspace::chunk_manager_class() :
    1.90 +                                  Metaspace::chunk_manager_metadata();
    1.91 +
    1.92 +  vsn->retire(cm);
    1.93 +}
    1.94 +
    1.95 +void VirtualSpaceNode::retire(ChunkManager* chunk_manager) {
    1.96 +  for (int i = (int)MediumIndex; i >= (int)ZeroIndex; --i) {
    1.97 +    ChunkIndex index = (ChunkIndex)i;
    1.98 +    size_t chunk_size = chunk_manager->free_chunks(index)->size();
    1.99 +
   1.100 +    while (free_words_in_vs() >= chunk_size) {
   1.101 +      DEBUG_ONLY(verify_container_count();)
   1.102 +      Metachunk* chunk = get_chunk_vs(chunk_size);
   1.103 +      assert(chunk != NULL, "allocation should have been successful");
   1.104 +
   1.105 +      chunk_manager->return_chunks(index, chunk);
   1.106 +      chunk_manager->inc_free_chunks_total(chunk_size);
   1.107 +      DEBUG_ONLY(verify_container_count();)
   1.108 +    }
   1.109 +  }
   1.110 +  assert(free_words_in_vs() == 0, "should be empty now");
   1.111 +}
   1.112 +
   1.113  VirtualSpaceList::VirtualSpaceList(size_t word_size) :
   1.114                                     _is_class(false),
   1.115                                     _virtual_space_list(NULL),
   1.116 @@ -1181,6 +1224,7 @@
   1.117    if (vs_expanded) {
   1.118      return true;
   1.119    }
   1.120 +  retire_current_virtual_space();
   1.121  
   1.122    // Get another virtual space.
   1.123    size_t grow_vs_words = MAX2((size_t)VirtualSpaceSize, preferred_words);
   1.124 @@ -1902,12 +1946,12 @@
   1.125      chunk_word_size = medium_chunk_size();
   1.126    }
   1.127  
   1.128 -  // Might still need a humongous chunk.  Enforce an
   1.129 -  // eight word granularity to facilitate reuse (some
   1.130 -  // wastage but better chance of reuse).
   1.131 +  // Might still need a humongous chunk.  Enforce
   1.132 +  // humongous allocations sizes to be aligned up to
   1.133 +  // the smallest chunk size.
   1.134    size_t if_humongous_sized_chunk =
   1.135      align_size_up(word_size + Metachunk::overhead(),
   1.136 -                  HumongousChunkGranularity);
   1.137 +                  smallest_chunk_size());
   1.138    chunk_word_size =
   1.139      MAX2((size_t) chunk_word_size, if_humongous_sized_chunk);
   1.140  
   1.141 @@ -2151,10 +2195,10 @@
   1.142      }
   1.143      assert(humongous_chunks->word_size() == (size_t)
   1.144             align_size_up(humongous_chunks->word_size(),
   1.145 -                             HumongousChunkGranularity),
   1.146 +                             smallest_chunk_size()),
   1.147             err_msg("Humongous chunk size is wrong: word size " SIZE_FORMAT
   1.148                     " granularity %d",
   1.149 -                   humongous_chunks->word_size(), HumongousChunkGranularity));
   1.150 +                   humongous_chunks->word_size(), smallest_chunk_size()));
   1.151      Metachunk* next_humongous_chunks = humongous_chunks->next();
   1.152      humongous_chunks->container()->dec_container_count();
   1.153      chunk_manager()->humongous_dictionary()->return_chunk(humongous_chunks);
   1.154 @@ -3494,4 +3538,94 @@
   1.155    TestMetaspaceAuxTest::test();
   1.156  }
   1.157  
   1.158 +class TestVirtualSpaceNodeTest {
   1.159 +  static void chunk_up(size_t words_left, size_t& num_medium_chunks,
   1.160 +                                          size_t& num_small_chunks,
   1.161 +                                          size_t& num_specialized_chunks) {
   1.162 +    num_medium_chunks = words_left / MediumChunk;
   1.163 +    words_left = words_left % MediumChunk;
   1.164 +
   1.165 +    num_small_chunks = words_left / SmallChunk;
   1.166 +    words_left = words_left % SmallChunk;
   1.167 +    // how many specialized chunks can we get?
   1.168 +    num_specialized_chunks = words_left / SpecializedChunk;
   1.169 +    assert(words_left % SpecializedChunk == 0, "should be nothing left");
   1.170 +  }
   1.171 +
   1.172 + public:
   1.173 +  static void test() {
   1.174 +    MutexLockerEx ml(SpaceManager::expand_lock(), Mutex::_no_safepoint_check_flag);
   1.175 +    const size_t vsn_test_size_words = MediumChunk  * 4;
   1.176 +    const size_t vsn_test_size_bytes = vsn_test_size_words * BytesPerWord;
   1.177 +
   1.178 +    // The chunk sizes must be multiples of eachother, or this will fail
   1.179 +    STATIC_ASSERT(MediumChunk % SmallChunk == 0);
   1.180 +    STATIC_ASSERT(SmallChunk % SpecializedChunk == 0);
   1.181 +
   1.182 +    { // No committed memory in VSN
   1.183 +      ChunkManager cm(SpecializedChunk, SmallChunk, MediumChunk);
   1.184 +      VirtualSpaceNode vsn(vsn_test_size_bytes);
   1.185 +      vsn.initialize();
   1.186 +      vsn.retire(&cm);
   1.187 +      assert(cm.sum_free_chunks_count() == 0, "did not commit any memory in the VSN");
   1.188 +    }
   1.189 +
   1.190 +    { // All of VSN is committed, half is used by chunks
   1.191 +      ChunkManager cm(SpecializedChunk, SmallChunk, MediumChunk);
   1.192 +      VirtualSpaceNode vsn(vsn_test_size_bytes);
   1.193 +      vsn.initialize();
   1.194 +      vsn.expand_by(vsn_test_size_words, vsn_test_size_words);
   1.195 +      vsn.get_chunk_vs(MediumChunk);
   1.196 +      vsn.get_chunk_vs(MediumChunk);
   1.197 +      vsn.retire(&cm);
   1.198 +      assert(cm.sum_free_chunks_count() == 2, "should have been memory left for 2 medium chunks");
   1.199 +      assert(cm.sum_free_chunks() == 2*MediumChunk, "sizes should add up");
   1.200 +    }
   1.201 +
   1.202 +    { // 4 pages of VSN is committed, some is used by chunks
   1.203 +      ChunkManager cm(SpecializedChunk, SmallChunk, MediumChunk);
   1.204 +      VirtualSpaceNode vsn(vsn_test_size_bytes);
   1.205 +      const size_t page_chunks = 4 * (size_t)os::vm_page_size() / BytesPerWord;
   1.206 +      assert(page_chunks < MediumChunk, "Test expects medium chunks to be at least 4*page_size");
   1.207 +      vsn.initialize();
   1.208 +      vsn.expand_by(page_chunks, page_chunks);
   1.209 +      vsn.get_chunk_vs(SmallChunk);
   1.210 +      vsn.get_chunk_vs(SpecializedChunk);
   1.211 +      vsn.retire(&cm);
   1.212 +
   1.213 +      // committed - used = words left to retire
   1.214 +      const size_t words_left = page_chunks - SmallChunk - SpecializedChunk;
   1.215 +
   1.216 +      size_t num_medium_chunks, num_small_chunks, num_spec_chunks;
   1.217 +      chunk_up(words_left, num_medium_chunks, num_small_chunks, num_spec_chunks);
   1.218 +
   1.219 +      assert(num_medium_chunks == 0, "should not get any medium chunks");
   1.220 +      assert(cm.sum_free_chunks_count() == (num_small_chunks + num_spec_chunks), "should be space for 3 chunks");
   1.221 +      assert(cm.sum_free_chunks() == words_left, "sizes should add up");
   1.222 +    }
   1.223 +
   1.224 +    { // Half of VSN is committed, a humongous chunk is used
   1.225 +      ChunkManager cm(SpecializedChunk, SmallChunk, MediumChunk);
   1.226 +      VirtualSpaceNode vsn(vsn_test_size_bytes);
   1.227 +      vsn.initialize();
   1.228 +      vsn.expand_by(MediumChunk * 2, MediumChunk * 2);
   1.229 +      vsn.get_chunk_vs(MediumChunk + SpecializedChunk); // Humongous chunks will be aligned up to MediumChunk + SpecializedChunk
   1.230 +      vsn.retire(&cm);
   1.231 +
   1.232 +      const size_t words_left = MediumChunk * 2 - (MediumChunk + SpecializedChunk);
   1.233 +      size_t num_medium_chunks, num_small_chunks, num_spec_chunks;
   1.234 +      chunk_up(words_left, num_medium_chunks, num_small_chunks, num_spec_chunks);
   1.235 +
   1.236 +      assert(num_medium_chunks == 0, "should not get any medium chunks");
   1.237 +      assert(cm.sum_free_chunks_count() == (num_small_chunks + num_spec_chunks), "should be space for 3 chunks");
   1.238 +      assert(cm.sum_free_chunks() == words_left, "sizes should add up");
   1.239 +    }
   1.240 +
   1.241 +  }
   1.242 +};
   1.243 +
   1.244 +void TestVirtualSpaceNode_test() {
   1.245 +  TestVirtualSpaceNodeTest::test();
   1.246 +}
   1.247 +
   1.248  #endif
     2.1 --- a/src/share/vm/prims/jni.cpp	Thu Oct 17 06:29:58 2013 -0700
     2.2 +++ b/src/share/vm/prims/jni.cpp	Tue Oct 15 13:56:46 2013 +0200
     2.3 @@ -5060,6 +5060,7 @@
     2.4  void TestVirtualSpace_test();
     2.5  void TestMetaspaceAux_test();
     2.6  void TestMetachunk_test();
     2.7 +void TestVirtualSpaceNode_test();
     2.8  #if INCLUDE_ALL_GCS
     2.9  void TestG1BiasedArray_test();
    2.10  #endif
    2.11 @@ -5072,6 +5073,7 @@
    2.12      run_unit_test(TestVirtualSpace_test());
    2.13      run_unit_test(TestMetaspaceAux_test());
    2.14      run_unit_test(TestMetachunk_test());
    2.15 +    run_unit_test(TestVirtualSpaceNode_test());
    2.16      run_unit_test(GlobalDefinitions::test_globals());
    2.17      run_unit_test(GCTimerAllTest::all());
    2.18      run_unit_test(arrayOopDesc::test_max_array_length());

mercurial