test/hotspot/gtest/memory/test_metaspace_allocation.cpp
changeset 49365 825f006619e5
child 49389 9ef2eee8ca7c
equal deleted inserted replaced
49347:edb65305d3ac 49365:825f006619e5
       
     1 /*
       
     2  * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
       
     3  * Copyright (c) 2018, SAP.
       
     4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     5  *
       
     6  * This code is free software; you can redistribute it and/or modify it
       
     7  * under the terms of the GNU General Public License version 2 only, as
       
     8  * published by the Free Software Foundation.
       
     9  *
       
    10  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    13  * version 2 for more details (a copy is included in the LICENSE file that
       
    14  * accompanied this code).
       
    15  *
       
    16  * You should have received a copy of the GNU General Public License version
       
    17  * 2 along with this work; if not, write to the Free Software Foundation,
       
    18  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    19  *
       
    20  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    21  * or visit www.oracle.com if you need additional information or have any
       
    22  * questions.
       
    23  */
       
    24 
       
    25 #include "precompiled.hpp"
       
    26 #include "memory/allocation.inline.hpp"
       
    27 #include "memory/metaspace.hpp"
       
    28 #include "runtime/mutex.hpp"
       
    29 #include "runtime/os.hpp"
       
    30 #include "utilities/align.hpp"
       
    31 #include "utilities/debug.hpp"
       
    32 #include "utilities/globalDefinitions.hpp"
       
    33 #include "utilities/ostream.hpp"
       
    34 #include "unittest.hpp"
       
    35 
       
    36 #define NUM_PARALLEL_METASPACES                 50
       
    37 #define MAX_PER_METASPACE_ALLOCATION_WORDSIZE   (512 * K)
       
    38 
       
    39 //#define DEBUG_VERBOSE true
       
    40 
       
    41 #ifdef DEBUG_VERBOSE
       
    42 
       
    43 struct chunkmanager_statistics_t {
       
    44   int num_specialized_chunks;
       
    45   int num_small_chunks;
       
    46   int num_medium_chunks;
       
    47   int num_humongous_chunks;
       
    48 };
       
    49 
       
    50 extern void test_metaspace_retrieve_chunkmanager_statistics(Metaspace::MetadataType mdType, chunkmanager_statistics_t* out);
       
    51 
       
    52 static void print_chunkmanager_statistics(outputStream* st, Metaspace::MetadataType mdType) {
       
    53   chunkmanager_statistics_t stat;
       
    54   test_metaspace_retrieve_chunkmanager_statistics(mdType, &stat);
       
    55   st->print_cr("free chunks: %d / %d / %d / %d", stat.num_specialized_chunks, stat.num_small_chunks,
       
    56                stat.num_medium_chunks, stat.num_humongous_chunks);
       
    57 }
       
    58 
       
    59 #endif
       
    60 
       
    61 struct chunk_geometry_t {
       
    62   size_t specialized_chunk_word_size;
       
    63   size_t small_chunk_word_size;
       
    64   size_t medium_chunk_word_size;
       
    65 };
       
    66 
       
    67 extern void test_metaspace_retrieve_chunk_geometry(Metaspace::MetadataType mdType, chunk_geometry_t* out);
       
    68 
       
    69 
       
    70 class MetaspaceAllocationTest : public ::testing::Test {
       
    71 protected:
       
    72 
       
    73   struct {
       
    74     size_t allocated;
       
    75     Mutex* lock;
       
    76     Metaspace* space;
       
    77     bool is_empty() const { return allocated == 0; }
       
    78     bool is_full() const { return allocated >= MAX_PER_METASPACE_ALLOCATION_WORDSIZE; }
       
    79   } _spaces[NUM_PARALLEL_METASPACES];
       
    80 
       
    81   chunk_geometry_t _chunk_geometry;
       
    82 
       
    83   virtual void SetUp() {
       
    84     ::memset(_spaces, 0, sizeof(_spaces));
       
    85     test_metaspace_retrieve_chunk_geometry(Metaspace::NonClassType, &_chunk_geometry);
       
    86   }
       
    87 
       
    88   virtual void TearDown() {
       
    89     for (int i = 0; i < NUM_PARALLEL_METASPACES; i ++) {
       
    90       if (_spaces[i].space != NULL) {
       
    91         delete _spaces[i].space;
       
    92         delete _spaces[i].lock;
       
    93       }
       
    94     }
       
    95   }
       
    96 
       
    97   void create_space(int i) {
       
    98     assert(i >= 0 && i < NUM_PARALLEL_METASPACES, "Sanity");
       
    99     assert(_spaces[i].space == NULL && _spaces[i].allocated == 0, "Sanity");
       
   100     if (_spaces[i].lock == NULL) {
       
   101       _spaces[i].lock = new Mutex(Monitor::native, "gtest-MetaspaceAllocationTest-lock", false, Monitor::_safepoint_check_never);
       
   102       ASSERT_TRUE(_spaces[i].lock != NULL);
       
   103     }
       
   104     // Let every ~10th space be an anonymous one to test different allocation patterns.
       
   105     const Metaspace::MetaspaceType msType = (os::random() % 100 < 10) ?
       
   106       Metaspace::AnonymousMetaspaceType : Metaspace::StandardMetaspaceType;
       
   107     _spaces[i].space = new Metaspace(_spaces[i].lock, msType);
       
   108     _spaces[i].allocated = 0;
       
   109     ASSERT_TRUE(_spaces[i].space != NULL);
       
   110   }
       
   111 
       
   112   // Returns the index of a random space where index is [0..metaspaces) and which is
       
   113   //   empty, non-empty or full.
       
   114   // Returns -1 if no matching space exists.
       
   115   enum fillgrade { fg_empty, fg_non_empty, fg_full };
       
   116   int get_random_matching_space(int metaspaces, fillgrade fg) {
       
   117     const int start_index = os::random() % metaspaces;
       
   118     int i = start_index;
       
   119     do {
       
   120       if (fg == fg_empty && _spaces[i].is_empty()) {
       
   121         return i;
       
   122       } else if ((fg == fg_full && _spaces[i].is_full()) ||
       
   123                  (fg == fg_non_empty && !_spaces[i].is_full() && !_spaces[i].is_empty())) {
       
   124         return i;
       
   125       }
       
   126       i ++;
       
   127       if (i == metaspaces) {
       
   128         i = 0;
       
   129       }
       
   130     } while (i != start_index);
       
   131     return -1;
       
   132   }
       
   133 
       
   134   int get_random_emtpy_space(int metaspaces) { return get_random_matching_space(metaspaces, fg_empty); }
       
   135   int get_random_non_emtpy_space(int metaspaces) { return get_random_matching_space(metaspaces, fg_non_empty); }
       
   136   int get_random_full_space(int metaspaces) { return get_random_matching_space(metaspaces, fg_full); }
       
   137 
       
   138   void do_test(Metaspace::MetadataType mdType, int metaspaces, int phases, int allocs_per_phase,
       
   139                float probability_for_large_allocations // 0.0-1.0
       
   140   ) {
       
   141     // Alternate between breathing in (allocating n blocks for a random Metaspace) and
       
   142     // breathing out (deleting a random Metaspace). The intent is to stress the coalescation
       
   143     // and splitting of free chunks.
       
   144     int phases_done = 0;
       
   145     bool allocating = true;
       
   146     while (phases_done < phases) {
       
   147       bool force_switch = false;
       
   148       if (allocating) {
       
   149         // Allocate space from metaspace, with a preference for completely empty spaces. This
       
   150         // should provide a good mixture of metaspaces in the virtual space.
       
   151         int index = get_random_emtpy_space(metaspaces);
       
   152         if (index == -1) {
       
   153           index = get_random_non_emtpy_space(metaspaces);
       
   154         }
       
   155         if (index == -1) {
       
   156           // All spaces are full, switch to freeing.
       
   157           force_switch = true;
       
   158         } else {
       
   159           // create space if it does not yet exist.
       
   160           if (_spaces[index].space == NULL) {
       
   161             create_space(index);
       
   162           }
       
   163           // Allocate a bunch of blocks from it. Mostly small stuff but mix in large allocations
       
   164           //  to force humongous chunk allocations.
       
   165           int allocs_done = 0;
       
   166           while (allocs_done < allocs_per_phase && !_spaces[index].is_full()) {
       
   167             size_t size = 0;
       
   168             int r = os::random() % 1000;
       
   169             if ((float)r < probability_for_large_allocations * 1000.0) {
       
   170               size = (os::random() % _chunk_geometry.medium_chunk_word_size) + _chunk_geometry.medium_chunk_word_size;
       
   171             } else {
       
   172               size = os::random() % 64;
       
   173             }
       
   174             MetaWord* const p = _spaces[index].space->allocate(size, mdType);
       
   175             if (p == NULL) {
       
   176               // We very probably did hit the metaspace "until-gc" limit.
       
   177 #ifdef DEBUG_VERBOSE
       
   178               tty->print_cr("OOM for " SIZE_FORMAT " words. ", size);
       
   179 #endif
       
   180               // Just switch to deallocation and resume tests.
       
   181               force_switch = true;
       
   182               break;
       
   183             } else {
       
   184               _spaces[index].allocated += size;
       
   185               allocs_done ++;
       
   186             }
       
   187           }
       
   188         }
       
   189       } else {
       
   190         // freeing: find a metaspace and delete it, with preference for completely filled spaces.
       
   191         int index = get_random_full_space(metaspaces);
       
   192         if (index == -1) {
       
   193           index = get_random_non_emtpy_space(metaspaces);
       
   194         }
       
   195         if (index == -1) {
       
   196           force_switch = true;
       
   197         } else {
       
   198           assert(_spaces[index].space != NULL && _spaces[index].allocated > 0, "Sanity");
       
   199           delete _spaces[index].space;
       
   200           _spaces[index].space = NULL;
       
   201           _spaces[index].allocated = 0;
       
   202         }
       
   203       }
       
   204 
       
   205       if (force_switch) {
       
   206         allocating = !allocating;
       
   207       } else {
       
   208         // periodically switch between allocating and freeing, but prefer allocation because
       
   209         // we want to intermingle allocations of multiple metaspaces.
       
   210         allocating = os::random() % 5 < 4;
       
   211       }
       
   212       phases_done ++;
       
   213 #ifdef DEBUG_VERBOSE
       
   214       int metaspaces_in_use = 0;
       
   215       size_t total_allocated = 0;
       
   216       for (int i = 0; i < metaspaces; i ++) {
       
   217         if (_spaces[i].allocated > 0) {
       
   218           total_allocated += _spaces[i].allocated;
       
   219           metaspaces_in_use ++;
       
   220         }
       
   221       }
       
   222       tty->print("%u:\tspaces: %d total words: " SIZE_FORMAT "\t\t\t", phases_done, metaspaces_in_use, total_allocated);
       
   223       print_chunkmanager_statistics(tty, mdType);
       
   224 #endif
       
   225     }
       
   226 #ifdef DEBUG_VERBOSE
       
   227     tty->print_cr("Test finished. ");
       
   228     MetaspaceAux::print_metaspace_map(tty, mdType);
       
   229     print_chunkmanager_statistics(tty, mdType);
       
   230 #endif
       
   231   }
       
   232 };
       
   233 
       
   234 
       
   235 
       
   236 TEST_F(MetaspaceAllocationTest, chunk_geometry) {
       
   237   ASSERT_GT(_chunk_geometry.specialized_chunk_word_size, (size_t) 0);
       
   238   ASSERT_GT(_chunk_geometry.small_chunk_word_size, _chunk_geometry.specialized_chunk_word_size);
       
   239   ASSERT_EQ(_chunk_geometry.small_chunk_word_size % _chunk_geometry.specialized_chunk_word_size, (size_t)0);
       
   240   ASSERT_GT(_chunk_geometry.medium_chunk_word_size, _chunk_geometry.small_chunk_word_size);
       
   241   ASSERT_EQ(_chunk_geometry.medium_chunk_word_size % _chunk_geometry.small_chunk_word_size, (size_t)0);
       
   242 }
       
   243 
       
   244 
       
   245 TEST_VM_F(MetaspaceAllocationTest, single_space_nonclass) {
       
   246   do_test(Metaspace::NonClassType, 1, 1000, 100, 0);
       
   247 }
       
   248 
       
   249 TEST_VM_F(MetaspaceAllocationTest, single_space_class) {
       
   250   do_test(Metaspace::ClassType, 1, 1000, 100, 0);
       
   251 }
       
   252 
       
   253 TEST_VM_F(MetaspaceAllocationTest, multi_space_nonclass) {
       
   254   do_test(Metaspace::NonClassType, NUM_PARALLEL_METASPACES, 100, 1000, 0.0);
       
   255 }
       
   256 
       
   257 TEST_VM_F(MetaspaceAllocationTest, multi_space_class) {
       
   258   do_test(Metaspace::ClassType, NUM_PARALLEL_METASPACES, 100, 1000, 0.0);
       
   259 }
       
   260 
       
   261 TEST_VM_F(MetaspaceAllocationTest, multi_space_nonclass_2) {
       
   262   // many metaspaces, with humongous chunks mixed in.
       
   263   do_test(Metaspace::NonClassType, NUM_PARALLEL_METASPACES, 100, 1000, .006f);
       
   264 }
       
   265