8269004: Implement ResizableResourceHashtable

Reviewed-by: coleenp, kbarrett
This commit is contained in:
Ioi Lam 2021-07-05 02:29:23 +00:00
parent 390d1025ca
commit 4da52eaf53
7 changed files with 253 additions and 58 deletions

@ -54,7 +54,7 @@
volatile Thread* ClassListParser::_parsing_thread = NULL;
ClassListParser* ClassListParser::_instance = NULL;
ClassListParser::ClassListParser(const char* file) : _id2klass_table(INITIAL_TABLE_SIZE) {
ClassListParser::ClassListParser(const char* file) : _id2klass_table(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE) {
_classlist_file = file;
_file = NULL;
// Use os::open() because neither fopen() nor os::fopen()
@ -507,7 +507,7 @@ void ClassListParser::populate_cds_indy_info(const constantPoolHandle &pool, int
}
}
bool ClassListParser::is_matching_cp_entry(constantPoolHandle &pool, int cp_index, TRAPS) {
bool ClassListParser::is_matching_cp_entry(const constantPoolHandle &pool, int cp_index, TRAPS) {
ResourceMark rm(THREAD);
CDSIndyInfo cii;
populate_cds_indy_info(pool, cp_index, &cii, CHECK_0);
@ -643,11 +643,14 @@ Klass* ClassListParser::load_current_class(Symbol* class_name_symbol, TRAPS) {
InstanceKlass* ik = InstanceKlass::cast(klass);
int id = this->id();
SystemDictionaryShared::update_shared_entry(ik, id);
InstanceKlass** old_ptr = table()->lookup(id);
if (old_ptr != NULL) {
bool created;
id2klass_table()->put_if_absent(id, ik, &created);
if (!created) {
error("Duplicated ID %d for class %s", id, _class_name);
}
table()->add(id, ik);
if (id2klass_table()->maybe_grow()) {
log_info(cds, hashtables)("Expanded id2klass_table() to %d", id2klass_table()->table_size());
}
}
return klass;
@ -658,7 +661,7 @@ bool ClassListParser::is_loading_from_source() {
}
InstanceKlass* ClassListParser::lookup_class_by_id(int id) {
InstanceKlass** klass_ptr = table()->lookup(id);
InstanceKlass** klass_ptr = id2klass_table()->get(id);
if (klass_ptr == NULL) {
error("Class ID %d has not been defined", id);
}

@ -28,11 +28,12 @@
#include "utilities/exceptions.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/hashtable.inline.hpp"
#include "utilities/resizeableResourceHash.hpp"
#define LAMBDA_PROXY_TAG "@lambda-proxy"
#define LAMBDA_FORM_TAG "@lambda-form-invoker"
class constantPoolHandle;
class Thread;
class CDSIndyInfo {
@ -66,7 +67,9 @@ public:
};
class ClassListParser : public StackObj {
typedef KVHashtable<int, InstanceKlass*, mtInternal> ID2KlassTable;
// Must be C_HEAP allocated -- we don't want nested resource allocations.
typedef ResizeableResourceHashtable<int, InstanceKlass*,
ResourceObj::C_HEAP, mtClassShared> ID2KlassTable;
enum {
_unspecified = -999,
@ -80,7 +83,9 @@ class ClassListParser : public StackObj {
_line_buf_size = _max_allowed_line_len + _line_buf_extra
};
static const int INITIAL_TABLE_SIZE = 1987;
// Use a small initial size in debug build to test resizing logic
static const int INITIAL_TABLE_SIZE = DEBUG_ONLY(17) NOT_DEBUG(1987);
static const int MAX_TABLE_SIZE = 61333;
static volatile Thread* _parsing_thread; // the thread that created _instance
static ClassListParser* _instance; // the singleton.
const char* _classlist_file;
@ -106,13 +111,13 @@ class ClassListParser : public StackObj {
bool parse_int_option(const char* option_name, int* value);
bool parse_uint_option(const char* option_name, int* value);
InstanceKlass* load_class_from_source(Symbol* class_name, TRAPS);
ID2KlassTable* table() {
ID2KlassTable* id2klass_table() {
return &_id2klass_table;
}
InstanceKlass* lookup_class_by_id(int id);
void print_specified_interfaces();
void print_actual_interfaces(InstanceKlass *ik);
bool is_matching_cp_entry(constantPoolHandle &pool, int cp_index, TRAPS);
bool is_matching_cp_entry(const constantPoolHandle &pool, int cp_index, TRAPS);
void resolve_indy(JavaThread* current, Symbol* class_name_symbol);
void resolve_indy_impl(Symbol* class_name_symbol, TRAPS);
@ -161,7 +166,7 @@ public:
return _super;
}
void check_already_loaded(const char* which, int id) {
if (_id2klass_table.lookup(id) == NULL) {
if (!id2klass_table()->contains(id)) {
error("%s id %d is not yet loaded", which, id);
}
}

@ -70,6 +70,7 @@
#include "runtime/sharedRuntime.hpp"
#include "runtime/vmThread.hpp"
#include "runtime/vmOperations.hpp"
#include "services/memTracker.hpp"
#include "utilities/align.hpp"
#include "utilities/bitMap.inline.hpp"
#include "utilities/ostream.hpp"

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2017, 2021, Oracle and/or its affiliates. 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
@ -95,11 +95,11 @@ MetaspaceClosure::~MetaspaceClosure() {
bool UniqueMetaspaceClosure::do_ref(MetaspaceClosure::Ref* ref, bool read_only) {
bool created;
_has_been_visited.add_if_absent(ref->obj(), read_only, &created);
_has_been_visited.put_if_absent(ref->obj(), read_only, &created);
if (!created) {
return false; // Already visited: no need to iterate embedded pointers.
} else {
if (_has_been_visited.maybe_grow(MAX_TABLE_SIZE)) {
if (_has_been_visited.maybe_grow()) {
log_info(cds, hashtables)("Expanded _has_been_visited table to %d", _has_been_visited.table_size());
}
return do_unique_ref(ref, read_only);

@ -31,8 +31,8 @@
#include "oops/array.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/hashtable.inline.hpp"
#include "utilities/macros.hpp"
#include "utilities/resizeableResourceHash.hpp"
#include <type_traits>
// The metadata hierarchy is separate from the oop hierarchy
@ -393,10 +393,11 @@ class UniqueMetaspaceClosure : public MetaspaceClosure {
public:
// Gets called the first time we discover an object.
virtual bool do_unique_ref(Ref* ref, bool read_only) = 0;
UniqueMetaspaceClosure() : _has_been_visited(INITIAL_TABLE_SIZE) {}
UniqueMetaspaceClosure() : _has_been_visited(INITIAL_TABLE_SIZE, MAX_TABLE_SIZE) {}
private:
KVHashtable<address, bool, mtInternal> _has_been_visited;
ResizeableResourceHashtable<address, bool, ResourceObj::C_HEAP,
mtClassShared> _has_been_visited;
};
#endif // SHARE_MEMORY_METASPACECLOSURE_HPP

@ -0,0 +1,136 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. 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.
*
*/
#ifndef SHARE_UTILITIES_RESIZEABLERESOURCEHASH_HPP
#define SHARE_UTILITIES_RESIZEABLERESOURCEHASH_HPP
#include "utilities/resourceHash.hpp"
template<
typename K, typename V,
ResourceObj::allocation_type ALLOC_TYPE,
MEMFLAGS MEM_TYPE>
class ResizeableResourceHashtableStorage : public ResourceObj {
using Node = ResourceHashtableNode<K, V>;
protected:
unsigned _table_size;
Node** _table;
ResizeableResourceHashtableStorage(unsigned table_size) {
_table_size = table_size;
_table = alloc_table(table_size);
}
~ResizeableResourceHashtableStorage() {
if (ALLOC_TYPE == C_HEAP) {
FREE_C_HEAP_ARRAY(Node*, _table);
}
}
Node** alloc_table(unsigned table_size) {
Node** table;
if (ALLOC_TYPE == C_HEAP) {
table = NEW_C_HEAP_ARRAY(Node*, table_size, MEM_TYPE);
} else {
table = NEW_RESOURCE_ARRAY(Node*, table_size);
}
memset(table, 0, table_size * sizeof(Node*));
return table;
}
unsigned table_size() const {
return _table_size;
}
Node** table() const {
return _table;
}
};
template<
typename K, typename V,
ResourceObj::allocation_type ALLOC_TYPE = ResourceObj::RESOURCE_AREA,
MEMFLAGS MEM_TYPE = mtInternal,
unsigned (*HASH) (K const&) = primitive_hash<K>,
bool (*EQUALS)(K const&, K const&) = primitive_equals<K>
>
class ResizeableResourceHashtable : public ResourceHashtableBase<
ResizeableResourceHashtableStorage<K, V, ALLOC_TYPE, MEM_TYPE>,
K, V, HASH, EQUALS, ALLOC_TYPE, MEM_TYPE> {
unsigned _max_size;
using BASE = ResourceHashtableBase<ResizeableResourceHashtableStorage<K, V, ALLOC_TYPE, MEM_TYPE>,
K, V, HASH, EQUALS, ALLOC_TYPE, MEM_TYPE>;
using Node = ResourceHashtableNode<K, V>;
NONCOPYABLE(ResizeableResourceHashtable);
public:
ResizeableResourceHashtable(unsigned size, unsigned max_size = 0)
: BASE(size), _max_size(max_size) {
assert(size <= 0x3fffffff && max_size <= 0x3fffffff, "avoid overflow in resize");
}
bool maybe_grow(int load_factor = 8) {
unsigned old_size = BASE::_table_size;
if (old_size >= _max_size) {
return false;
}
if (BASE::number_of_entries() / int(old_size) > load_factor) {
unsigned new_size = MIN2<unsigned>(old_size * 2, _max_size);
resize(old_size, new_size);
return true;
} else {
return false;
}
}
void resize(unsigned old_size, unsigned new_size) {
Node** old_table = BASE::_table;
Node** new_table = BASE::alloc_table(new_size);
Node* const* bucket = old_table;
while (bucket < &old_table[old_size]) {
Node* node = *bucket;
while (node != NULL) {
Node* next = node->_next;
unsigned hash = HASH(node->_key);
unsigned index = hash % new_size;
node->_next = new_table[index];
new_table[index] = node;
node = next;
}
++bucket;
}
if (ALLOC_TYPE == ResourceObj::C_HEAP) {
FREE_C_HEAP_ARRAY(Node*, old_table);
}
BASE::_table = new_table;
BASE::_table_size = new_size;
}
};
#endif // SHARE_UTILITIES_RESIZEABLERESOURCEHASH_HPP

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2021, Oracle and/or its affiliates. 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
@ -27,44 +27,45 @@
#include "memory/allocation.hpp"
template<typename K, typename V>
class ResourceHashtableNode : public ResourceObj {
public:
unsigned _hash;
K _key;
V _value;
ResourceHashtableNode* _next;
ResourceHashtableNode(unsigned hash, K const& key, V const& value) :
_hash(hash), _key(key), _value(value), _next(NULL) {}
// Create a node with a default-constructed value.
ResourceHashtableNode(unsigned hash, K const& key) :
_hash(hash), _key(key), _value(), _next(NULL) {}
};
template<
class STORAGE,
typename K, typename V,
// xlC does not compile this:
// http://stackoverflow.com/questions/8532961/template-argument-of-type-that-is-defined-by-inner-typedef-from-other-template-c
//typename ResourceHashtableFns<K>::hash_fn HASH = primitive_hash<K>,
//typename ResourceHashtableFns<K>::equals_fn EQUALS = primitive_equals<K>,
unsigned (*HASH) (K const&) = primitive_hash<K>,
bool (*EQUALS)(K const&, K const&) = primitive_equals<K>,
unsigned SIZE = 256,
ResourceObj::allocation_type ALLOC_TYPE = ResourceObj::RESOURCE_AREA,
MEMFLAGS MEM_TYPE = mtInternal
unsigned (*HASH) (K const&),
bool (*EQUALS)(K const&, K const&),
ResourceObj::allocation_type ALLOC_TYPE,
MEMFLAGS MEM_TYPE
>
class ResourceHashtable : public ResourceObj {
class ResourceHashtableBase : public STORAGE {
using Node = ResourceHashtableNode<K, V>;
private:
int _number_of_entries;
class Node : public ResourceObj {
public:
unsigned _hash;
K _key;
V _value;
Node* _next;
Node(unsigned hash, K const& key, V const& value) :
_hash(hash), _key(key), _value(value), _next(NULL) {}
// Create a node with a default-constructed value.
Node(unsigned hash, K const& key) :
_hash(hash), _key(key), _value(), _next(NULL) {}
};
Node* _table[SIZE];
Node** bucket_at(unsigned index) const {
Node** t = table();
return &t[index];
}
// Returns a pointer to where the node where the value would reside if
// it's in the table.
Node** lookup_node(unsigned hash, K const& key) {
unsigned index = hash % SIZE;
Node** ptr = &_table[index];
unsigned index = hash % table_size();
Node** ptr = bucket_at(index);
while (*ptr != NULL) {
Node* node = *ptr;
if (node->_hash == hash && EQUALS(key, node->_key)) {
@ -77,16 +78,21 @@ class ResourceHashtable : public ResourceObj {
Node const** lookup_node(unsigned hash, K const& key) const {
return const_cast<Node const**>(
const_cast<ResourceHashtable*>(this)->lookup_node(hash, key));
const_cast<ResourceHashtableBase*>(this)->lookup_node(hash, key));
}
public:
ResourceHashtable() { memset(_table, 0, SIZE * sizeof(Node*)); }
protected:
Node** table() const { return STORAGE::table(); }
~ResourceHashtable() {
if (ALLOC_TYPE == C_HEAP) {
Node* const* bucket = _table;
while (bucket < &_table[SIZE]) {
ResourceHashtableBase() : STORAGE(), _number_of_entries(0) {}
ResourceHashtableBase(unsigned size) : STORAGE(size), _number_of_entries(0) {}
NONCOPYABLE(ResourceHashtableBase);
~ResourceHashtableBase() {
if (ALLOC_TYPE == ResourceObj::C_HEAP) {
Node* const* bucket = table();
const unsigned sz = table_size();
while (bucket < bucket_at(sz)) {
Node* node = *bucket;
while (node != NULL) {
Node* cur = node;
@ -98,6 +104,10 @@ class ResourceHashtable : public ResourceObj {
}
}
public:
unsigned table_size() const { return STORAGE::table_size(); }
int number_of_entries() const { return _number_of_entries; }
bool contains(K const& key) const {
return get(key) != NULL;
}
@ -125,6 +135,7 @@ class ResourceHashtable : public ResourceObj {
return false;
} else {
*ptr = new (ALLOC_TYPE, MEM_TYPE) Node(hv, key, value);
_number_of_entries ++;
return true;
}
}
@ -140,6 +151,7 @@ class ResourceHashtable : public ResourceObj {
if (*ptr == NULL) {
*ptr = new (ALLOC_TYPE, MEM_TYPE) Node(hv, key);
*p_created = true;
_number_of_entries ++;
} else {
*p_created = false;
}
@ -157,6 +169,7 @@ class ResourceHashtable : public ResourceObj {
if (*ptr == NULL) {
*ptr = new (ALLOC_TYPE, MEM_TYPE) Node(hv, key, value);
*p_created = true;
_number_of_entries ++;
} else {
*p_created = false;
}
@ -171,9 +184,10 @@ class ResourceHashtable : public ResourceObj {
Node* node = *ptr;
if (node != NULL) {
*ptr = node->_next;
if (ALLOC_TYPE == C_HEAP) {
if (ALLOC_TYPE == ResourceObj::C_HEAP) {
delete node;
}
_number_of_entries --;
return true;
}
return false;
@ -184,8 +198,9 @@ class ResourceHashtable : public ResourceObj {
// the iteration is cancelled.
template<class ITER>
void iterate(ITER* iter) const {
Node* const* bucket = _table;
while (bucket < &_table[SIZE]) {
Node* const* bucket = table();
const unsigned sz = table_size();
while (bucket < bucket_at(sz)) {
Node* node = *bucket;
while (node != NULL) {
bool cont = iter->do_entry(node->_key, node->_value);
@ -197,5 +212,39 @@ class ResourceHashtable : public ResourceObj {
}
};
template<unsigned TABLE_SIZE, typename K, typename V>
class FixedResourceHashtableStorage : public ResourceObj {
using Node = ResourceHashtableNode<K, V>;
Node* _table[TABLE_SIZE];
protected:
FixedResourceHashtableStorage() : _table() {}
~FixedResourceHashtableStorage() = default;
constexpr unsigned table_size() const {
return TABLE_SIZE;
}
Node** table() const {
return const_cast<Node**>(_table);
}
};
template<
typename K, typename V,
unsigned (*HASH) (K const&) = primitive_hash<K>,
bool (*EQUALS)(K const&, K const&) = primitive_equals<K>,
unsigned SIZE = 256,
ResourceObj::allocation_type ALLOC_TYPE = ResourceObj::RESOURCE_AREA,
MEMFLAGS MEM_TYPE = mtInternal
>
class ResourceHashtable : public ResourceHashtableBase<
FixedResourceHashtableStorage<SIZE, K, V>,
K, V, HASH, EQUALS, ALLOC_TYPE, MEM_TYPE> {
NONCOPYABLE(ResourceHashtable);
public:
ResourceHashtable() : ResourceHashtableBase<FixedResourceHashtableStorage<SIZE, K, V>,
K, V, HASH, EQUALS, ALLOC_TYPE, MEM_TYPE>() {}
};
#endif // SHARE_UTILITIES_RESOURCEHASH_HPP