< prev index next >

src/hotspot/share/memory/metaspace/metachunk.hpp

Print this page
rev 57511 : [mq]: metaspace-improvement

@@ -22,152 +22,215 @@
  *
  */
 #ifndef SHARE_MEMORY_METASPACE_METACHUNK_HPP
 #define SHARE_MEMORY_METASPACE_METACHUNK_HPP
 
-#include "memory/metaspace/metabase.hpp"
-#include "memory/metaspace/metaspaceCommon.hpp"
+
+#include "memory/metaspace/counter.hpp"
+#include "memory/metaspace/abstractPool.hpp"
+#include "memory/metaspace/chunkLevel.hpp"
 #include "utilities/debug.hpp"
 #include "utilities/globalDefinitions.hpp"
 
-class MetachunkTest;
+
 
 namespace metaspace {
 
 class VirtualSpaceNode;
 
 //  Metachunk - Quantum of allocation from a Virtualspace
 //    Metachunks are reused (when freed are put on a global freelist) and
 //    have no permanent association to a SpaceManager.
 
-//            +--------------+ <- end    --+       --+
+//            +--------------+ <- end    ----+         --+
 //            |              |             |         |
 //            |              |             | free    |
+//            |              |               |
+//            |              |               |           | size (aka capacity)
 //            |              |             |         |
-//            |              |             |         | size | capacity
-//            |              |             |         |
-//            |              | <- top   -- +         |
+//            | -----------  | <- top     -- +           |
 //            |              |             |         |
 //            |              |             | used    |
-//            |              |             |         |
-//            |              |             |         |
-//            +--------------+ <- bottom --+       --+
-
-enum ChunkOrigin {
-  // Chunk normally born (via take_from_committed)
-  origin_normal = 1,
-  // Chunk was born as padding chunk
-  origin_pad = 2,
-  // Chunk was born as leftover chunk in VirtualSpaceNode::retire
-  origin_leftover = 3,
-  // Chunk was born as result of a merge of smaller chunks
-  origin_merge = 4,
-  // Chunk was born as result of a split of a larger chunk
-  origin_split = 5,
-
-  origin_minimum = origin_normal,
-  origin_maximum = origin_split,
-  origins_count = origin_maximum + 1
-};
-
-inline bool is_valid_chunkorigin(ChunkOrigin origin) {
-  return origin == origin_normal ||
-    origin == origin_pad ||
-    origin == origin_leftover ||
-    origin == origin_merge ||
-    origin == origin_split;
-}
-
-class Metachunk : public Metabase<Metachunk> {
-
-  friend class ::MetachunkTest;
-
-  // The VirtualSpaceNode containing this chunk.
-  VirtualSpaceNode* const _container;
-
-  // Current allocation top.
-  MetaWord* _top;
-
-  // A 32bit sentinel for debugging purposes.
-  enum { CHUNK_SENTINEL = 0x4d4554EF,  // "MET"
-         CHUNK_SENTINEL_INVALID = 0xFEEEEEEF
-  };
-
-  uint32_t _sentinel;
-
-  const ChunkIndex _chunk_type;
-  const bool _is_class;
-  // Whether the chunk is free (in freelist) or in use by some class loader.
-  bool _is_tagged_free;
+//            +--------------+ <- start   -- +        -- +
 
-  ChunkOrigin _origin;
-  int _use_count;
+// Note: this is a chunk **descriptor**. The real Payload area lives in metaspace,
+// this class lives somewhere else.
+class Metachunk {
+
+  // Todo: compact this node. A lot of things can be expressed more tighter.
+
+  // A chunk header is kept in a list:
+  // - in the list of used chunks inside a SpaceManager, if it is in use
+  // - in the list of free chunks inside a ChunkManager, if it is free
+  // - in the freelist of dead headers inside the MetaChunkHeaderPool,
+  //   if it is dead (e.g. result of chunk merging).
+  Metachunk* _prev;
+  Metachunk* _next;
+
+  chklvl_t _level; // aka size.
+
+  // true: free, owned by ChunkManager
+  // false: in-use, owned by SpaceManager
+  // if dead, meaningless
+  bool _is_free;
+
+  // start of chunk memory; NULL if dead.
+  MetaWord* _base;
+
+  // Used words.
+  size_t _used_words;
+
+  // Guaranteed-to-be-committed-words, counted from base
+  //  (This is a performance optimization. The underlying VirtualSpaceNode knows
+  //  which granules are committed; but we want to avoid asking it unnecessarily
+  //  in Metachunk::allocate(), so we keep a limit until which we are guaranteed
+  //  to have committed memory under us.)
+  size_t _committed_words;
+
+  // the chunk tree node this header is hanging under; NULL if dead.
+  u2 _tree_node_ref;
+
+  // We need unfortunately a back link to the virtual space node
+  // for splitting and merging nodes.
+  VirtualSpaceNode* _vsnode;
+
+  MetaWord* top() const           { return base() + _used_words; }
+
+public:
+
+  Metachunk()
+    : _prev(NULL), _next(NULL),
+      _level(chklvl::ROOT_CHUNK_LEVEL),
+      _is_free(true),
+      _base(NULL),
+      _used_words(0),
+      _committed_words(0),
+      _tree_node_ref(0),
+      _vsnode(NULL)
+  {}
+
+  size_t word_size() const        { return chklvl::word_size_for_level(_level); }
+
+  MetaWord* base() const          { return _base; }
+  void set_base(MetaWord* p)      { _base = p; }
+  MetaWord* end() const           { return base() + word_size(); }
+
+  void set_prev(Metachunk* c)     { _prev = c; }
+  Metachunk* prev() const         { return _prev; }
+  void set_next(Metachunk* c)     { _next = c; }
+  Metachunk* next() const         { return _next; }
+  // Remove chunk from whatever list it lives in by wiring next with previous.
+  void remove_from_list();
+
+  bool is_free() const            { return _is_free; }
+  bool is_in_use() const          { return !_is_free; }
+  void set_free(bool v)           { _is_free = v; }
+
+
+  void inc_level()                { _level ++; DEBUG_ONLY(chklvl::is_valid_level(_level);) }
+  void dec_level()                { _level --; DEBUG_ONLY(chklvl::is_valid_level(_level);) }
+  void set_level(chklvl_t v)      { _level = v; DEBUG_ONLY(chklvl::is_valid_level(_level);) }
+  chklvl_t level() const          { return _level; }
+
+  void set_tree_node_ref(u2 v)    { _tree_node_ref = v; }
+  u2 tree_node_ref() const        { return _tree_node_ref; }
+
+  VirtualSpaceNode* vsnode() const        { return _vsnode; }
+  void set_vsnode(VirtualSpaceNode* n)    { _vsnode = n; }
+
+  size_t used_words() const       { return _used_words; }
+  size_t free_words() const       { return word_size() - used_words(); }
+  size_t free_below_committed_words() const   { return committed_words() - used_words(); }
+  void reset_used_words()         { _used_words = 0; }
+
+  size_t committed_words() const      { return _committed_words; }
+  void set_committed_words(size_t v)  { _committed_words = v; }
+  bool is_fully_committed() const     { return committed_words() == word_size(); }
+
+  // Ensure that chunk is committed up to at least word_size words.
+  // Fails if we hit a commit limit.
+  bool ensure_committed(size_t word_size);
+
+  // Alignment of an allocation.
+  static const size_t allocation_alignment_bytes = 8;
+  static const size_t allocation_alignment_words = allocation_alignment_bytes / BytesPerWord;
+
+  // Allocation from a chunk
+
+  // Allocate word_size words from this chunk.
+  //
+  // May cause memory to be committed. That may fail if we hit a commit limit. In that case,
+  //  NULL is returned and p_did_hit_commit_limit will be set to true.
+  // If the remainder portion of the chunk was too small to hold the allocation,
+  //  NULL is returned and p_did_hit_commit_limit will be set to false.
+  MetaWord* allocate(size_t word_size, bool* p_did_hit_commit_limit);
+
+  // Wipe this object to look as if it were default constructed.
+  void wipe() {
+    _prev = NULL; _next = NULL;
+    _level = chklvl::ROOT_CHUNK_LEVEL;
+    _is_free = true;
+    _base = NULL;
+    _used_words = 0;
+    _committed_words = 0;
+    _tree_node_ref = 0;
+    _vsnode = NULL;
+  }
 
-  MetaWord* initial_top() const { return (MetaWord*)this + overhead(); }
-  MetaWord* top() const         { return _top; }
+  //// Debug stuff ////
+  DEBUG_ONLY(void verify(bool slow) const;)
 
- public:
-  // Metachunks are allocated out of a MetadataVirtualSpace and
-  // and use some of its space to describe itself (plus alignment
-  // considerations).  Metadata is allocated in the rest of the chunk.
-  // This size is the overhead of maintaining the Metachunk within
-  // the space.
-
-  // Alignment of each allocation in the chunks.
-  static size_t object_alignment();
-
-  // Size of the Metachunk header, in words, including alignment.
-  static size_t overhead();
-
-  Metachunk(ChunkIndex chunktype, bool is_class, size_t word_size, VirtualSpaceNode* container);
-
-  MetaWord* allocate(size_t word_size);
-
-  VirtualSpaceNode* container() const { return _container; }
-
-  MetaWord* bottom() const { return (MetaWord*) this; }
-
-  // Reset top to bottom so chunk can be reused.
-  void reset_empty() { _top = initial_top(); clear_next(); clear_prev(); }
-  bool is_empty() { return _top == initial_top(); }
-
-  // used (has been allocated)
-  // free (available for future allocations)
-  size_t word_size() const { return size(); }
-  size_t used_word_size() const;
-  size_t free_word_size() const;
-
-  bool is_tagged_free() { return _is_tagged_free; }
-  void set_is_tagged_free(bool v) { _is_tagged_free = v; }
-
-  bool contains(const void* ptr) { return bottom() <= ptr && ptr < _top; }
+};
 
-  void print_on(outputStream* st) const;
 
-  bool is_valid_sentinel() const        { return _sentinel == CHUNK_SENTINEL; }
-  void remove_sentinel()                { _sentinel = CHUNK_SENTINEL_INVALID; }
+class MetachunkList {
 
-  int get_use_count() const             { return _use_count; }
-  void inc_use_count()                  { _use_count ++; }
+  Metachunk* _first;
+  IntCounter _num;
 
-  ChunkOrigin get_origin() const        { return _origin; }
-  void set_origin(ChunkOrigin orig)     { _origin = orig; }
+public:
 
-  ChunkIndex get_chunk_type() const     { return _chunk_type; }
-  bool is_class() const                 { return _is_class; }
+  MetachunkList() : _first(NULL), _num() {}
 
-  DEBUG_ONLY(void mangle(juint word_value);)
-  DEBUG_ONLY(void verify() const;)
+  Metachunk* first() const { return _first; }
+  int size() const { return _num.get(); }
+
+  void add(Metachunk* c) {
+    c->set_next(_first);
+    _first = c;
+    _num.increment();
+  }
+
+  // Remove first node unless empty. Returns node or NULL.
+  Metachunk* remove_first() {
+    Metachunk* c = _first;
+    if (c != NULL) {
+      _first = c->next();
+      _num.decrement();
+    }
+    return c;
+  }
+
+  // Remove given chunk from list. List must contain that chunk.
+  void remove(Metachunk* c) {
+    assert(contains(c), "Does not contain this chunk");
+    c->remove_from_list();
+    _num.decrement();
+  }
+
+  // Manually decrement counter; needed for cases where chunks
+  // have been manually removed from the list without informing
+  // the list, e.g. chunk merging, see chunkManager::return_chunk().
+  void dec_counter_by(int v) {
+    _num.decrement_by(v);
+  }
+
+#ifdef ASSERT
+  bool contains(const Metachunk* c) const;
+  void verify(bool slow) const;
+#endif
 
 };
 
-
-// Helper function that does a bunch of checks for a chunk.
-DEBUG_ONLY(void do_verify_chunk(Metachunk* chunk);)
-
-// Given a Metachunk, update its in-use information (both in the
-// chunk and the occupancy map).
-void do_update_in_use_info_for_chunk(Metachunk* chunk, bool inuse);
-
 } // namespace metaspace
 
 #endif // SHARE_MEMORY_METASPACE_METACHUNK_HPP
< prev index next >