src/hotspot/share/memory/metaspace/virtualSpaceList.cpp
author stuefe
Thu, 12 Sep 2019 07:57:00 +0200
branchstuefe-new-metaspace-branch
changeset 58099 5aeb07390c74
parent 58063 bdf136b8ae0e
permissions -rw-r--r--
Fixes for Windows x64

/*
 * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 2018, 2019 SAP SE. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */


#include "precompiled.hpp"
#include "logging/log.hpp"
#include "memory/metaspace.hpp"
#include "memory/metaspace/chunkManager.hpp"
#include "memory/metaspace/counter.hpp"
#include "memory/metaspace/commitLimiter.hpp"
#include "memory/metaspace/counter.hpp"
#include "memory/metaspace/virtualSpaceList.hpp"
#include "memory/metaspace/virtualSpaceNode.hpp"
#include "runtime/mutexLocker.hpp"


namespace metaspace {

static int next_node_id = 0;

// Create a new, empty, expandable list.
VirtualSpaceList::VirtualSpaceList(const char* name, CommitLimiter* commit_limiter)
  : _name(name),
    _first_node(NULL),
    _can_expand(true),
    _can_purge(true),
    _commit_limiter(commit_limiter),
    _reserved_words_counter(),
    _committed_words_counter()
{
}

// Create a new list. The list will contain one node only, which uses the given ReservedSpace.
// It will be not expandable beyond that first node.
VirtualSpaceList::VirtualSpaceList(const char* name, ReservedSpace rs, CommitLimiter* commit_limiter)
: _name(name),
  _first_node(NULL),
  _can_expand(false),
  _can_purge(false),
  _commit_limiter(commit_limiter),
  _reserved_words_counter(),
  _committed_words_counter()
{
  // Create the first node spanning the existing ReservedSpace. This will be the only node created
  // for this list since we cannot expand.
  VirtualSpaceNode* vsn = VirtualSpaceNode::create_node(next_node_id++,
                                                        rs, _commit_limiter,
                                                        &_reserved_words_counter, &_committed_words_counter);
  assert(vsn != NULL, "node creation failed");
  _first_node = vsn;
  _first_node->set_next(NULL);
  _nodes_counter.increment();
}

VirtualSpaceList::~VirtualSpaceList() {
  // Note: normally, there is no reason ever to delete a vslist since they are
  // global objects, but for gtests it makes sense to allow this.
  VirtualSpaceNode* vsn = _first_node;
  VirtualSpaceNode* vsn2 = vsn;
  while (vsn != NULL) {
    vsn2 = vsn->next();
    delete vsn;
    vsn = vsn2;
  }
}

// Create a new node and append it to the list. After
// this function, _current_node shall point to a new empty node.
// List must be expandable for this to work.
void VirtualSpaceList::create_new_node() {
  assert(_can_expand, "List is not expandable");
  assert_lock_strong(MetaspaceExpand_lock);

  VirtualSpaceNode* vsn = VirtualSpaceNode::create_node(next_node_id ++,
                                                        Settings::virtual_space_node_default_word_size(),
                                                        _commit_limiter,
                                                        &_reserved_words_counter, &_committed_words_counter);
  assert(vsn != NULL, "node creation failed");
  vsn->set_next(_first_node);
  _first_node = vsn;
  _nodes_counter.increment();
}

// Allocate a root chunk from this list.
// Note: this just returns a chunk whose memory is reserved; no memory is committed yet.
// Hence, before using this chunk, it must be committed.
// Also, no limits are checked, since no committing takes place.
Metachunk*  VirtualSpaceList::allocate_root_chunk() {
  assert_lock_strong(MetaspaceExpand_lock);

  log_debug(metaspace)("VirtualSpaceList %s: allocate root chunk.", _name);

  if (_first_node == NULL ||
      _first_node->free_words() == 0) {

    // The current node is fully used up.
    log_debug(metaspace)("VirtualSpaceList %s: need new node.", _name);

    // Since all allocations from a VirtualSpaceNode happen in
    // root-chunk-size units, and the node size must be root-chunk-size aligned,
    // we should never have left-over space.
    assert(_first_node == NULL ||
           _first_node->free_words() == 0, "Sanity");

    if (_can_expand) {
      create_new_node();
    } else {
      return NULL; // We cannot expand this list.
    }
  }

  Metachunk* c = _first_node->allocate_root_chunk();

  assert(c != NULL, "This should have worked");

  return c;

}

// Attempts to purge nodes. This will remove and delete nodes which only contain free chunks.
// The free chunks are removed from the freelists before the nodes are deleted.
// Return number of purged nodes.
int VirtualSpaceList::purge(MetachunkListCluster* freelists) {

  // Note: I am not sure all that purging business is even necessary anymore
  // since we have a good reclaim mechanism in place. Need to measure.

  assert_lock_strong(MetaspaceExpand_lock);

  if (_can_purge == false) {
    log_debug(metaspace)("VirtualSpaceList %s: cannot purge this list.", _name);
    return 0;
  }

  log_debug(metaspace)("VirtualSpaceList %s: purging...", _name);

  VirtualSpaceNode* vsn = _first_node;
  VirtualSpaceNode* prev_vsn = NULL;
  int num = 0, num_purged = 0;
  while (vsn != NULL) {
    VirtualSpaceNode* next_vsn = vsn->next();
    bool purged = vsn->attempt_purge(freelists);
    if (purged) {
      // Note: from now on do not dereference vsn!
      log_debug(metaspace)("VirtualSpaceList %s: purged node @" PTR_FORMAT, _name, p2i(vsn));
      if (_first_node == vsn) {
        _first_node = next_vsn;
      }
      DEBUG_ONLY(vsn = (VirtualSpaceNode*)((uintptr_t)(0xdeadbeef));)
      if (prev_vsn != NULL) {
        prev_vsn->set_next(next_vsn);
      }
      num_purged ++;
      _nodes_counter.decrement();
    } else {
      prev_vsn = vsn;
    }
    vsn = next_vsn;
    num ++;
  }

  log_debug(metaspace)("VirtualSpaceList %s: purged %d/%d nodes.", _name, num_purged, num);

  return num_purged;

}

// Print all nodes in this space list.
void VirtualSpaceList::print_on(outputStream* st) const {
  MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag);

  st->print_cr("vsl %s:", _name);
  const VirtualSpaceNode* vsn = _first_node;
  int n = 0;
  while (vsn != NULL) {
    st->print("- node #%d: ", n);
    vsn->print_on(st);
    vsn = vsn->next();
    n ++;
  }
  st->print_cr("- total %d nodes, " SIZE_FORMAT " reserved words, " SIZE_FORMAT " committed words.",
               n, reserved_words(), committed_words());
}

#ifdef ASSERT
void VirtualSpaceList::verify_locked(bool slow) const {

  assert_lock_strong(MetaspaceExpand_lock);

  assert(_name != NULL, "Sanity");

  int n = 0;

  if (_first_node != NULL) {

    size_t total_reserved_words = 0;
    size_t total_committed_words = 0;
    const VirtualSpaceNode* vsn = _first_node;
    while (vsn != NULL) {
      n ++;
      vsn->verify(slow);
      total_reserved_words += vsn->word_size();
      total_committed_words += vsn->committed_words();
      vsn = vsn->next();
    }

    _nodes_counter.check(n);
    _reserved_words_counter.check(total_reserved_words);
    _committed_words_counter.check(total_committed_words);

  } else {

    _reserved_words_counter.check(0);
    _committed_words_counter.check(0);

  }
}

void VirtualSpaceList::verify(bool slow) const {
  MutexLocker fcl(MetaspaceExpand_lock, Mutex::_no_safepoint_check_flag);
  verify_locked(slow);
}
#endif

// Returns true if this pointer is contained in one of our nodes.
bool VirtualSpaceList::contains(const MetaWord* p) const {
  const VirtualSpaceNode* vsn = _first_node;
  while (vsn != NULL) {
    if (vsn->contains(p)) {
      return true;
    }
    vsn = vsn->next();
  }
  return false;
}

VirtualSpaceList* VirtualSpaceList::_vslist_class = NULL;
VirtualSpaceList* VirtualSpaceList::_vslist_nonclass = NULL;

void VirtualSpaceList::set_vslist_class(VirtualSpaceList* vsl) {
  assert(_vslist_class == NULL, "Sanity");
  _vslist_class = vsl;
}

void VirtualSpaceList::set_vslist_nonclass(VirtualSpaceList* vsl) {
  assert(_vslist_nonclass == NULL, "Sanity");
  _vslist_nonclass = vsl;
}

} // namespace metaspace