Path: blob/master/src/hotspot/share/memory/metaspace/blockTree.hpp
40957 views
/*1* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.2* Copyright (c) 2020 SAP SE. All rights reserved.3* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.4*5* This code is free software; you can redistribute it and/or modify it6* under the terms of the GNU General Public License version 2 only, as7* published by the Free Software Foundation.8*9* This code is distributed in the hope that it will be useful, but WITHOUT10* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or11* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License12* version 2 for more details (a copy is included in the LICENSE file that13* accompanied this code).14*15* You should have received a copy of the GNU General Public License version16* 2 along with this work; if not, write to the Free Software Foundation,17* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.18*19* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA20* or visit www.oracle.com if you need additional information or have any21* questions.22*23*/2425#ifndef SHARE_MEMORY_METASPACE_BLOCKTREE_HPP26#define SHARE_MEMORY_METASPACE_BLOCKTREE_HPP2728#include "memory/allocation.hpp"29#include "memory/metaspace/chunklevel.hpp"30#include "memory/metaspace/counters.hpp"31#include "utilities/debug.hpp"32#include "utilities/globalDefinitions.hpp"3334namespace metaspace {3536// BlockTree is a rather simple binary search tree. It is used to37// manage small to medium free memory blocks (see class FreeBlocks).38//39// There is no separation between payload (managed blocks) and nodes: the40// memory blocks themselves are the nodes, with the block size being the key.41//42// We store node pointer information in these blocks when storing them. That43// imposes a minimum size to the managed memory blocks.44// See get_raw_word_size_for_requested_word_size() (msCommon.hpp).45//46// We want to manage many memory blocks of the same size, but we want47// to prevent the tree from blowing up and degenerating into a list. Therefore48// there is only one node for each unique block size; subsequent blocks of the49// same size are stacked below that first node:50//51// +-----+52// | 100 |53// +-----+54// / \55// +-----+56// | 80 |57// +-----+58// / | \59// / +-----+ \60// +-----+ | 80 | +-----+61// | 70 | +-----+ | 85 |62// +-----+ | +-----+63// +-----+64// | 80 |65// +-----+66//67//68// Todo: This tree is unbalanced. It would be a good fit for a red-black tree.69// In order to make this a red-black tree, we need an algorithm which can deal70// with nodes which are their own payload (most red-black tree implementations71// swap payloads of their nodes at some point, see e.g. j.u.TreeSet).72// A good example is the Linux kernel rbtree, which is a clean, easy-to-read73// implementation.7475class BlockTree: public CHeapObj<mtMetaspace> {7677struct Node {7879static const intptr_t _canary_value =80NOT_LP64(0x4e4f4445) LP64_ONLY(0x4e4f44454e4f4445ULL); // "NODE" resp "NODENODE"8182// Note: we afford us the luxury of an always-there canary value.83// The space for that is there (these nodes are only used to manage larger blocks,84// see FreeBlocks::MaxSmallBlocksWordSize).85// It is initialized in debug and release, but only automatically tested86// in debug.87const intptr_t _canary;8889// Normal tree node stuff...90// (Note: all null if this is a stacked node)91Node* _parent;92Node* _left;93Node* _right;9495// Blocks with the same size are put in a list with this node as head.96Node* _next;9798// Word size of node. Note that size cannot be larger than max metaspace size,99// so this could be very well a 32bit value (in case we ever make this a balancing100// tree and need additional space for weighting information).101const size_t _word_size;102103Node(size_t word_size) :104_canary(_canary_value),105_parent(NULL),106_left(NULL),107_right(NULL),108_next(NULL),109_word_size(word_size)110{}111112#ifdef ASSERT113bool valid() const {114return _canary == _canary_value &&115_word_size >= sizeof(Node) &&116_word_size < chunklevel::MAX_CHUNK_WORD_SIZE;117}118#endif119};120121// Needed for verify() and print_tree()122struct walkinfo;123124#ifdef ASSERT125// Run a quick check on a node; upon suspicion dive into a full tree check.126void check_node(const Node* n) const { if (!n->valid()) verify(); }127#endif128129public:130131// Minimum word size a block has to be to be added to this structure (note ceil division).132const static size_t MinWordSize =133(sizeof(Node) + sizeof(MetaWord) - 1) / sizeof(MetaWord);134135private:136137Node* _root;138139MemRangeCounter _counter;140141// Given a node n, add it to the list starting at head142static void add_to_list(Node* n, Node* head) {143assert(head->_word_size == n->_word_size, "sanity");144n->_next = head->_next;145head->_next = n;146DEBUG_ONLY(n->_left = n->_right = n->_parent = NULL;)147}148149// Given a node list starting at head, remove one of the follow up nodes from150// that list and return it. The head node gets not modified and remains in the151// tree.152// List must contain at least one other node.153static Node* remove_from_list(Node* head) {154assert(head->_next != NULL, "sanity");155Node* n = head->_next;156head->_next = n->_next;157return n;158}159160// Given a node c and a node p, wire up c as left child of p.161static void set_left_child(Node* p, Node* c) {162p->_left = c;163if (c != NULL) {164assert(c->_word_size < p->_word_size, "sanity");165c->_parent = p;166}167}168169// Given a node c and a node p, wire up c as right child of p.170static void set_right_child(Node* p, Node* c) {171p->_right = c;172if (c != NULL) {173assert(c->_word_size > p->_word_size, "sanity");174c->_parent = p;175}176}177178// Given a node n, return its successor in the tree179// (node with the next-larger size).180static Node* successor(Node* n) {181Node* succ = NULL;182if (n->_right != NULL) {183// If there is a right child, search the left-most184// child of that child.185succ = n->_right;186while (succ->_left != NULL) {187succ = succ->_left;188}189} else {190succ = n->_parent;191Node* n2 = n;192// As long as I am the right child of my parent, search upward193while (succ != NULL && n2 == succ->_right) {194n2 = succ;195succ = succ->_parent;196}197}198return succ;199}200201// Given a node, replace it with a replacement node as a child for its parent.202// If the node is root and has no parent, sets it as root.203void replace_node_in_parent(Node* child, Node* replace) {204Node* parent = child->_parent;205if (parent != NULL) {206if (parent->_left == child) { // Child is left child207set_left_child(parent, replace);208} else {209set_right_child(parent, replace);210}211} else {212assert(child == _root, "must be root");213_root = replace;214if (replace != NULL) {215replace->_parent = NULL;216}217}218return;219}220221// Given a node n and an insertion point, insert n under insertion point.222void insert(Node* insertion_point, Node* n) {223assert(n->_parent == NULL, "Sanity");224for (;;) {225DEBUG_ONLY(check_node(insertion_point);)226if (n->_word_size == insertion_point->_word_size) {227add_to_list(n, insertion_point); // parent stays NULL in this case.228break;229} else if (n->_word_size > insertion_point->_word_size) {230if (insertion_point->_right == NULL) {231set_right_child(insertion_point, n);232break;233} else {234insertion_point = insertion_point->_right;235}236} else {237if (insertion_point->_left == NULL) {238set_left_child(insertion_point, n);239break;240} else {241insertion_point = insertion_point->_left;242}243}244}245}246247// Given a node and a wish size, search this node and all children for248// the node closest (equal or larger sized) to the size s.249Node* find_closest_fit(Node* n, size_t s) {250Node* best_match = NULL;251while (n != NULL) {252DEBUG_ONLY(check_node(n);)253if (n->_word_size >= s) {254best_match = n;255if (n->_word_size == s) {256break; // perfect match or max depth reached257}258n = n->_left;259} else {260n = n->_right;261}262}263return best_match;264}265266// Given a wish size, search the whole tree for a267// node closest (equal or larger sized) to the size s.268Node* find_closest_fit(size_t s) {269if (_root != NULL) {270return find_closest_fit(_root, s);271}272return NULL;273}274275// Given a node n, remove it from the tree and repair tree.276void remove_node_from_tree(Node* n) {277assert(n->_next == NULL, "do not delete a node which has a non-empty list");278279if (n->_left == NULL && n->_right == NULL) {280replace_node_in_parent(n, NULL);281282} else if (n->_left == NULL && n->_right != NULL) {283replace_node_in_parent(n, n->_right);284285} else if (n->_left != NULL && n->_right == NULL) {286replace_node_in_parent(n, n->_left);287288} else {289// Node has two children.290291// 1) Find direct successor (the next larger node).292Node* succ = successor(n);293294// There has to be a successor since n->right was != NULL...295assert(succ != NULL, "must be");296297// ... and it should not have a left child since successor298// is supposed to be the next larger node, so it must be the mostleft node299// in the sub tree rooted at n->right300assert(succ->_left == NULL, "must be");301assert(succ->_word_size > n->_word_size, "sanity");302303Node* successor_parent = succ->_parent;304Node* successor_right_child = succ->_right;305306// Remove successor from its parent.307if (successor_parent == n) {308309// special case: successor is a direct child of n. Has to be the right child then.310assert(n->_right == succ, "sanity");311312// Just replace n with this successor.313replace_node_in_parent(n, succ);314315// Take over n's old left child, too.316// We keep the successor's right child.317set_left_child(succ, n->_left);318} else {319// If the successors parent is not n, we are deeper in the tree,320// the successor has to be the left child of its parent.321assert(successor_parent->_left == succ, "sanity");322323// The right child of the successor (if there was one) replaces324// the successor at its parent's left child.325set_left_child(successor_parent, succ->_right);326327// and the successor replaces n at its parent328replace_node_in_parent(n, succ);329330// and takes over n's old children331set_left_child(succ, n->_left);332set_right_child(succ, n->_right);333}334}335}336337#ifdef ASSERT338void zap_range(MetaWord* p, size_t word_size);339// Helper for verify()340void verify_node_pointer(const Node* n) const;341#endif // ASSERT342343public:344345BlockTree() : _root(NULL) {}346347// Add a memory block to the tree. Its content will be overwritten.348void add_block(MetaWord* p, size_t word_size) {349DEBUG_ONLY(zap_range(p, word_size));350assert(word_size >= MinWordSize, "invalid block size " SIZE_FORMAT, word_size);351Node* n = new(p) Node(word_size);352if (_root == NULL) {353_root = n;354} else {355insert(_root, n);356}357_counter.add(word_size);358}359360// Given a word_size, search and return the smallest block that is equal or361// larger than that size. Upon return, *p_real_word_size contains the actual362// block size.363MetaWord* remove_block(size_t word_size, size_t* p_real_word_size) {364assert(word_size >= MinWordSize, "invalid block size " SIZE_FORMAT, word_size);365366Node* n = find_closest_fit(word_size);367368if (n != NULL) {369DEBUG_ONLY(check_node(n);)370assert(n->_word_size >= word_size, "sanity");371372if (n->_next != NULL) {373// If the node is head of a chain of same sized nodes, we leave it alone374// and instead remove one of the follow up nodes (which is simpler than375// removing the chain head node and then having to graft the follow up376// node into its place in the tree).377n = remove_from_list(n);378} else {379remove_node_from_tree(n);380}381382MetaWord* p = (MetaWord*)n;383*p_real_word_size = n->_word_size;384385_counter.sub(n->_word_size);386387DEBUG_ONLY(zap_range(p, n->_word_size));388return p;389}390return NULL;391}392393// Returns number of blocks in this structure394unsigned count() const { return _counter.count(); }395396// Returns total size, in words, of all elements.397size_t total_size() const { return _counter.total_size(); }398399bool is_empty() const { return _root == NULL; }400401DEBUG_ONLY(void print_tree(outputStream* st) const;)402DEBUG_ONLY(void verify() const;)403};404405} // namespace metaspace406407#endif // SHARE_MEMORY_METASPACE_BLOCKTREE_HPP408409410