8213587: Speed up CDS dump time by using resizable hashtables
Reviewed-by: jiangli, coleenp, gziemski
This commit is contained in:
parent
6d3df94e5e
commit
6d269930fd
@ -389,8 +389,8 @@ Klass* ClassListParser::load_current_class(TRAPS) {
|
||||
InstanceKlass* ik = InstanceKlass::cast(klass);
|
||||
int id = this->id();
|
||||
SystemDictionaryShared::update_shared_entry(ik, id);
|
||||
InstanceKlass* old = table()->lookup(id);
|
||||
if (old != NULL && old != ik) {
|
||||
InstanceKlass** old_ptr = table()->lookup(id);
|
||||
if (old_ptr != NULL) {
|
||||
error("Duplicated ID %d for class %s", id, _class_name);
|
||||
}
|
||||
table()->add(id, ik);
|
||||
@ -404,11 +404,12 @@ bool ClassListParser::is_loading_from_source() {
|
||||
}
|
||||
|
||||
InstanceKlass* ClassListParser::lookup_class_by_id(int id) {
|
||||
InstanceKlass* klass = table()->lookup(id);
|
||||
if (klass == NULL) {
|
||||
InstanceKlass** klass_ptr = table()->lookup(id);
|
||||
if (klass_ptr == NULL) {
|
||||
error("Class ID %d has not been defined", id);
|
||||
}
|
||||
return klass;
|
||||
assert(*klass_ptr != NULL, "must be");
|
||||
return *klass_ptr;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2018, 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,30 +27,12 @@
|
||||
|
||||
#include "utilities/exceptions.hpp"
|
||||
#include "utilities/globalDefinitions.hpp"
|
||||
#include "utilities/hashtable.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
#include "utilities/hashtable.inline.hpp"
|
||||
|
||||
class CDSClassInfo;
|
||||
|
||||
// Look up from ID -> InstanceKlass*
|
||||
class ID2KlassTable : public Hashtable<InstanceKlass*, mtClass> {
|
||||
class ID2KlassTable : public KVHashtable<int, InstanceKlass*, mtInternal> {
|
||||
public:
|
||||
ID2KlassTable() : Hashtable<InstanceKlass*, mtClass>(1987, sizeof(HashtableEntry<InstanceKlass*, mtClass>)) { }
|
||||
void add(int id, InstanceKlass* klass) {
|
||||
unsigned int hash = (unsigned int)id;
|
||||
HashtableEntry<InstanceKlass*, mtClass>* entry = new_entry(hash, klass);
|
||||
add_entry(hash_to_index(hash), entry);
|
||||
}
|
||||
|
||||
InstanceKlass* lookup(int id) {
|
||||
unsigned int hash = (unsigned int)id;
|
||||
int index = hash_to_index(id);
|
||||
for (HashtableEntry<InstanceKlass*, mtClass>* e = bucket(index); e != NULL; e = e->next()) {
|
||||
if (e->hash() == hash) {
|
||||
return e->literal();
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
ID2KlassTable() : KVHashtable<int, InstanceKlass*, mtInternal>(1987) {}
|
||||
};
|
||||
|
||||
class ClassListParser : public StackObj {
|
||||
|
@ -25,7 +25,6 @@
|
||||
#include "precompiled.hpp"
|
||||
#include "classfile/classFileParser.hpp"
|
||||
#include "classfile/classFileStream.hpp"
|
||||
#include "classfile/classListParser.hpp"
|
||||
#include "classfile/classLoader.inline.hpp"
|
||||
#include "classfile/classLoaderExt.hpp"
|
||||
#include "classfile/classLoaderData.inline.hpp"
|
||||
@ -257,7 +256,6 @@ void ClassLoaderExt::finalize_shared_paths_misc_info() {
|
||||
// the "source:" in the class list file (see classListParser.cpp), and can be a directory or
|
||||
// a JAR file.
|
||||
InstanceKlass* ClassLoaderExt::load_class(Symbol* name, const char* path, TRAPS) {
|
||||
|
||||
assert(name != NULL, "invariant");
|
||||
assert(DumpSharedSpaces, "this function is only used with -Xshare:dump");
|
||||
ResourceMark rm(THREAD);
|
||||
|
@ -27,7 +27,7 @@
|
||||
|
||||
#include "oops/array.hpp"
|
||||
#include "oops/symbol.hpp"
|
||||
#include "utilities/hashtable.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
|
||||
|
||||
template <
|
||||
|
@ -67,7 +67,6 @@ Dictionary::~Dictionary() {
|
||||
}
|
||||
assert(number_of_entries() == 0, "should have removed all entries");
|
||||
assert(new_entry_free_list() == NULL, "entry present on Dictionary's free list");
|
||||
free_buckets();
|
||||
}
|
||||
|
||||
DictionaryEntry* Dictionary::new_entry(unsigned int hash, InstanceKlass* klass) {
|
||||
|
@ -352,7 +352,6 @@ ModuleEntryTable::~ModuleEntryTable() {
|
||||
}
|
||||
assert(number_of_entries() == 0, "should have removed all entries");
|
||||
assert(new_entry_free_list() == NULL, "entry present on ModuleEntryTable's free list");
|
||||
free_buckets();
|
||||
}
|
||||
|
||||
ModuleEntry* ModuleEntryTable::new_entry(unsigned int hash, Handle module_handle,
|
||||
|
@ -191,7 +191,6 @@ PackageEntryTable::~PackageEntryTable() {
|
||||
}
|
||||
assert(number_of_entries() == 0, "should have removed all entries");
|
||||
assert(new_entry_free_list() == NULL, "entry present on PackageEntryTable's free list");
|
||||
free_buckets();
|
||||
}
|
||||
|
||||
PackageEntry* PackageEntryTable::new_entry(unsigned int hash, Symbol* name, ModuleEntry* module) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 2018, 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
|
||||
@ -76,7 +76,9 @@ G1CodeRootSetTable::~G1CodeRootSetTable() {
|
||||
}
|
||||
}
|
||||
assert(number_of_entries() == 0, "should have removed all entries");
|
||||
free_buckets();
|
||||
// Each of the entries in new_entry_free_list() have been allocated in
|
||||
// G1CodeRootSetTable::new_entry(). We never call the block allocator
|
||||
// in BasicHashtable::new_entry().
|
||||
for (BasicHashtableEntry<mtGC>* e = new_entry_free_list(); e != NULL; e = new_entry_free_list()) {
|
||||
FREE_C_HEAP_ARRAY(char, e);
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2018, 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
|
||||
@ -55,13 +55,15 @@ void MetaspaceClosure::push_impl(MetaspaceClosure::Ref* ref, Writability w) {
|
||||
}
|
||||
|
||||
bool UniqueMetaspaceClosure::do_ref(MetaspaceClosure::Ref* ref, bool read_only) {
|
||||
bool* found = _has_been_visited.get(ref->obj());
|
||||
bool* found = _has_been_visited.lookup(ref->obj());
|
||||
if (found != NULL) {
|
||||
assert(*found == read_only, "must be");
|
||||
return false; // Already visited: no need to iterate embedded pointers.
|
||||
} else {
|
||||
bool isnew = _has_been_visited.put(ref->obj(), read_only);
|
||||
assert(isnew, "sanity");
|
||||
_has_been_visited.add(ref->obj(), read_only);
|
||||
if (_has_been_visited.maybe_grow(MAX_TABLE_SIZE)) {
|
||||
log_info(cds, hashtables)("Expanded _has_been_visited table to %d", _has_been_visited.table_size());
|
||||
}
|
||||
do_unique_ref(ref, read_only);
|
||||
return true; // Saw this for the first time: iterate the embedded pointers.
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2018, 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
|
||||
@ -29,7 +29,7 @@
|
||||
#include "memory/allocation.hpp"
|
||||
#include "oops/array.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
#include "utilities/resourceHash.hpp"
|
||||
#include "utilities/hashtable.inline.hpp"
|
||||
|
||||
// The metadata hierarchy is separate from the oop hierarchy
|
||||
class MetaspaceObj; // no C++ vtable
|
||||
@ -258,25 +258,19 @@ public:
|
||||
|
||||
// This is a special MetaspaceClosure that visits each unique MetaspaceObj once.
|
||||
class UniqueMetaspaceClosure : public MetaspaceClosure {
|
||||
static const int INITIAL_TABLE_SIZE = 15889;
|
||||
static const int MAX_TABLE_SIZE = 1000000;
|
||||
|
||||
// Do not override. Returns true if we are discovering ref->obj() for the first time.
|
||||
virtual bool do_ref(Ref* ref, bool read_only);
|
||||
|
||||
public:
|
||||
// Gets called the first time we discover an object.
|
||||
virtual void do_unique_ref(Ref* ref, bool read_only) = 0;
|
||||
UniqueMetaspaceClosure() : _has_been_visited(INITIAL_TABLE_SIZE) {}
|
||||
|
||||
private:
|
||||
static unsigned my_hash(const address& a) {
|
||||
return primitive_hash<address>(a);
|
||||
}
|
||||
static bool my_equals(const address& a0, const address& a1) {
|
||||
return primitive_equals<address>(a0, a1);
|
||||
}
|
||||
ResourceHashtable<
|
||||
address, bool,
|
||||
UniqueMetaspaceClosure::my_hash, // solaris compiler doesn't like: primitive_hash<address>
|
||||
UniqueMetaspaceClosure::my_equals, // solaris compiler doesn't like: primitive_equals<address>
|
||||
15889, // prime number
|
||||
ResourceObj::C_HEAP> _has_been_visited;
|
||||
KVHashtable<address, bool, mtInternal> _has_been_visited;
|
||||
};
|
||||
|
||||
#endif // SHARE_VM_MEMORY_METASPACE_ITERATOR_HPP
|
||||
|
@ -64,6 +64,7 @@
|
||||
#include "utilities/align.hpp"
|
||||
#include "utilities/bitMap.hpp"
|
||||
#include "utilities/defaultStream.hpp"
|
||||
#include "utilities/hashtable.inline.hpp"
|
||||
#if INCLUDE_G1GC
|
||||
#include "gc/g1/g1CollectedHeap.hpp"
|
||||
#endif
|
||||
@ -1067,26 +1068,19 @@ public:
|
||||
// metaspace data into their final location in the shared regions.
|
||||
|
||||
class ArchiveCompactor : AllStatic {
|
||||
static const int INITIAL_TABLE_SIZE = 8087;
|
||||
static const int MAX_TABLE_SIZE = 1000000;
|
||||
|
||||
static DumpAllocStats* _alloc_stats;
|
||||
static SortedSymbolClosure* _ssc;
|
||||
|
||||
static unsigned my_hash(const address& a) {
|
||||
return primitive_hash<address>(a);
|
||||
}
|
||||
static bool my_equals(const address& a0, const address& a1) {
|
||||
return primitive_equals<address>(a0, a1);
|
||||
}
|
||||
typedef ResourceHashtable<
|
||||
address, address,
|
||||
ArchiveCompactor::my_hash, // solaris compiler doesn't like: primitive_hash<address>
|
||||
ArchiveCompactor::my_equals, // solaris compiler doesn't like: primitive_equals<address>
|
||||
16384, ResourceObj::C_HEAP> RelocationTable;
|
||||
typedef KVHashtable<address, address, mtInternal> RelocationTable;
|
||||
static RelocationTable* _new_loc_table;
|
||||
|
||||
public:
|
||||
static void initialize() {
|
||||
_alloc_stats = new(ResourceObj::C_HEAP, mtInternal)DumpAllocStats;
|
||||
_new_loc_table = new(ResourceObj::C_HEAP, mtInternal)RelocationTable;
|
||||
_new_loc_table = new RelocationTable(INITIAL_TABLE_SIZE);
|
||||
}
|
||||
static DumpAllocStats* alloc_stats() {
|
||||
return _alloc_stats;
|
||||
@ -1136,15 +1130,17 @@ public:
|
||||
newtop = _rw_region.top();
|
||||
}
|
||||
memcpy(p, obj, bytes);
|
||||
bool isnew = _new_loc_table->put(obj, (address)p);
|
||||
assert(_new_loc_table->lookup(obj) == NULL, "each object can be relocated at most once");
|
||||
_new_loc_table->add(obj, (address)p);
|
||||
log_trace(cds)("Copy: " PTR_FORMAT " ==> " PTR_FORMAT " %d", p2i(obj), p2i(p), bytes);
|
||||
assert(isnew, "must be");
|
||||
|
||||
if (_new_loc_table->maybe_grow(MAX_TABLE_SIZE)) {
|
||||
log_info(cds, hashtables)("Expanded _new_loc_table to %d", _new_loc_table->table_size());
|
||||
}
|
||||
_alloc_stats->record(ref->msotype(), int(newtop - oldtop), read_only);
|
||||
}
|
||||
|
||||
static address get_new_loc(MetaspaceClosure::Ref* ref) {
|
||||
address* pp = _new_loc_table->get(ref->obj());
|
||||
address* pp = _new_loc_table->lookup(ref->obj());
|
||||
assert(pp != NULL, "must be");
|
||||
return *pp;
|
||||
}
|
||||
@ -1288,7 +1284,7 @@ public:
|
||||
|
||||
static Klass* get_relocated_klass(Klass* orig_klass) {
|
||||
assert(DumpSharedSpaces, "dump time only");
|
||||
address* pp = _new_loc_table->get((address)orig_klass);
|
||||
address* pp = _new_loc_table->lookup((address)orig_klass);
|
||||
assert(pp != NULL, "must be");
|
||||
Klass* klass = (Klass*)(*pp);
|
||||
assert(klass->is_klass(), "must be");
|
||||
|
@ -1261,4 +1261,17 @@ static inline void* dereference_vptr(const void* addr) {
|
||||
typedef const char* ccstr;
|
||||
typedef const char* ccstrlist; // represents string arguments which accumulate
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
// Default hash/equals functions used by ResourceHashtable and KVHashtable
|
||||
|
||||
template<typename K> unsigned primitive_hash(const K& k) {
|
||||
unsigned hash = (unsigned)((uintptr_t)k);
|
||||
return hash ^ (hash >> 3); // just in case we're dealing with aligned ptrs
|
||||
}
|
||||
|
||||
template<typename K> bool primitive_equals(const K& k0, const K& k1) {
|
||||
return k0 == k1;
|
||||
}
|
||||
|
||||
|
||||
#endif // SHARE_VM_UTILITIES_GLOBALDEFINITIONS_HPP
|
||||
|
@ -65,6 +65,7 @@ template <MEMFLAGS F> BasicHashtableEntry<F>* BasicHashtable<F>::new_entry(unsig
|
||||
len = 1 << log2_intptr(len); // round down to power of 2
|
||||
assert(len >= _entry_size, "");
|
||||
_first_free_entry = NEW_C_HEAP_ARRAY2(char, len, F, CURRENT_PC);
|
||||
_entry_blocks->append(_first_free_entry);
|
||||
_end_block = _first_free_entry + len;
|
||||
}
|
||||
entry = (BasicHashtableEntry<F>*)_first_free_entry;
|
||||
@ -86,7 +87,9 @@ template <class T, MEMFLAGS F> HashtableEntry<T, F>* Hashtable<T, F>::new_entry(
|
||||
}
|
||||
|
||||
// Version of hashtable entry allocation that allocates in the C heap directly.
|
||||
// The allocator in blocks is preferable but doesn't have free semantics.
|
||||
// The block allocator in BasicHashtable has less fragmentation, but the memory is not freed until
|
||||
// the whole table is freed. Use allocate_new_entry() if you want to individually free the memory
|
||||
// used by each entry
|
||||
template <class T, MEMFLAGS F> HashtableEntry<T, F>* Hashtable<T, F>::allocate_new_entry(unsigned int hashValue, T obj) {
|
||||
HashtableEntry<T, F>* entry = (HashtableEntry<T, F>*) NEW_C_HEAP_ARRAY(char, this->entry_size(), F);
|
||||
|
||||
@ -203,6 +206,20 @@ template <MEMFLAGS F> bool BasicHashtable<F>::resize(int new_size) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <MEMFLAGS F> bool BasicHashtable<F>::maybe_grow(int max_size, int load_factor) {
|
||||
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
|
||||
|
||||
if (table_size() >= max_size) {
|
||||
return false;
|
||||
}
|
||||
if (number_of_entries() / table_size() > load_factor) {
|
||||
resize(MIN2<int>(table_size() * 2, max_size));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Dump footprint and bucket length statistics
|
||||
//
|
||||
// Note: if you create a new subclass of Hashtable<MyNewType, F>, you will need to
|
||||
|
@ -25,11 +25,11 @@
|
||||
#ifndef SHARE_VM_UTILITIES_HASHTABLE_HPP
|
||||
#define SHARE_VM_UTILITIES_HASHTABLE_HPP
|
||||
|
||||
#include "classfile/classLoaderData.hpp"
|
||||
#include "memory/allocation.hpp"
|
||||
#include "oops/oop.hpp"
|
||||
#include "oops/symbol.hpp"
|
||||
#include "runtime/handles.hpp"
|
||||
#include "utilities/growableArray.hpp"
|
||||
|
||||
// This is a generic hashtable, designed to be used for the symbol
|
||||
// and string tables.
|
||||
@ -146,6 +146,7 @@ public:
|
||||
BasicHashtable(int table_size, int entry_size);
|
||||
BasicHashtable(int table_size, int entry_size,
|
||||
HashtableBucket<F>* buckets, int number_of_entries);
|
||||
~BasicHashtable();
|
||||
|
||||
// Bucket handling
|
||||
int hash_to_index(unsigned int full_hash) const {
|
||||
@ -163,6 +164,7 @@ private:
|
||||
char* _end_block;
|
||||
int _entry_size;
|
||||
volatile int _number_of_entries;
|
||||
GrowableArray<char*>* _entry_blocks;
|
||||
|
||||
protected:
|
||||
|
||||
@ -233,6 +235,9 @@ public:
|
||||
|
||||
bool resize(int new_size);
|
||||
|
||||
// Grow the number of buckets if the average entries per bucket is over the load_factor
|
||||
bool maybe_grow(int max_size, int load_factor = 8);
|
||||
|
||||
template <class T> void verify_table(const char* table_name) PRODUCT_RETURN;
|
||||
};
|
||||
|
||||
@ -279,4 +284,55 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// A subclass of BasicHashtable that allows you to do a simple K -> V mapping
|
||||
// without using tons of boilerplate code.
|
||||
template<
|
||||
typename K, typename V, MEMFLAGS F,
|
||||
unsigned (*HASH) (K const&) = primitive_hash<K>,
|
||||
bool (*EQUALS)(K const&, K const&) = primitive_equals<K>
|
||||
>
|
||||
class KVHashtable : public BasicHashtable<F> {
|
||||
class KVHashtableEntry : public BasicHashtableEntry<F> {
|
||||
public:
|
||||
K _key;
|
||||
V _value;
|
||||
KVHashtableEntry* next() {
|
||||
return (KVHashtableEntry*)BasicHashtableEntry<F>::next();
|
||||
}
|
||||
};
|
||||
|
||||
protected:
|
||||
KVHashtableEntry* bucket(int i) const {
|
||||
return (KVHashtableEntry*)BasicHashtable<F>::bucket(i);
|
||||
}
|
||||
|
||||
KVHashtableEntry* new_entry(unsigned int hashValue, K key, V value) {
|
||||
KVHashtableEntry* entry = (KVHashtableEntry*)BasicHashtable<F>::new_entry(hashValue);
|
||||
entry->_key = key;
|
||||
entry->_value = value;
|
||||
return entry;
|
||||
}
|
||||
|
||||
public:
|
||||
KVHashtable(int table_size) : BasicHashtable<F>(table_size, sizeof(KVHashtableEntry)) {}
|
||||
|
||||
void add(K key, V value) {
|
||||
unsigned int hash = HASH(key);
|
||||
KVHashtableEntry* entry = new_entry(hash, key, value);
|
||||
BasicHashtable<F>::add_entry(BasicHashtable<F>::hash_to_index(hash), entry);
|
||||
}
|
||||
|
||||
V* lookup(K key) {
|
||||
unsigned int hash = HASH(key);
|
||||
int index = BasicHashtable<F>::hash_to_index(hash);
|
||||
for (KVHashtableEntry* e = bucket(index); e != NULL; e = e->next()) {
|
||||
if (e->hash() == hash && e->_key == key) {
|
||||
return &(e->_value);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif // SHARE_VM_UTILITIES_HASHTABLE_HPP
|
||||
|
@ -54,6 +54,13 @@ template <MEMFLAGS F> inline BasicHashtable<F>::BasicHashtable(int table_size, i
|
||||
_buckets = buckets;
|
||||
}
|
||||
|
||||
template <MEMFLAGS F> inline BasicHashtable<F>::~BasicHashtable() {
|
||||
for (int i = 0; i < _entry_blocks->length(); i++) {
|
||||
FREE_C_HEAP_ARRAY(char, _entry_blocks->at(i));
|
||||
}
|
||||
delete _entry_blocks;
|
||||
free_buckets();
|
||||
}
|
||||
|
||||
template <MEMFLAGS F> inline void BasicHashtable<F>::initialize(int table_size, int entry_size,
|
||||
int number_of_entries) {
|
||||
@ -64,6 +71,7 @@ template <MEMFLAGS F> inline void BasicHashtable<F>::initialize(int table_size,
|
||||
_first_free_entry = NULL;
|
||||
_end_block = NULL;
|
||||
_number_of_entries = number_of_entries;
|
||||
_entry_blocks = new(ResourceObj::C_HEAP, F) GrowableArray<char*>(4, true, F);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2012, 2018, 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
|
||||
@ -32,15 +32,6 @@ template<typename K> struct ResourceHashtableFns {
|
||||
typedef bool (*equals_fn)(K const&, K const&);
|
||||
};
|
||||
|
||||
template<typename K> unsigned primitive_hash(const K& k) {
|
||||
unsigned hash = (unsigned)((uintptr_t)k);
|
||||
return hash ^ (hash >> 3); // just in case we're dealing with aligned ptrs
|
||||
}
|
||||
|
||||
template<typename K> bool primitive_equals(const K& k0, const K& k1) {
|
||||
return k0 == k1;
|
||||
}
|
||||
|
||||
template<
|
||||
typename K, typename V,
|
||||
// xlC does not compile this:
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2017, 2018, 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
|
||||
@ -29,17 +29,8 @@ import jdk.test.lib.cds.CDSOptions;
|
||||
public class AppCDSOptions extends CDSOptions {
|
||||
public String appJar;
|
||||
|
||||
// Application classes to be archived
|
||||
public String[] appClasses;
|
||||
|
||||
public AppCDSOptions setAppJar(String appJar) {
|
||||
this.appJar = appJar;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AppCDSOptions setAppClasses(String[] appClasses) {
|
||||
this.appClasses = appClasses;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
88
test/hotspot/jtreg/runtime/appcds/LotsOfClasses.java
Normal file
88
test/hotspot/jtreg/runtime/appcds/LotsOfClasses.java
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 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.
|
||||
*
|
||||
*/
|
||||
|
||||
import java.net.URI;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import jdk.test.lib.cds.CDSTestUtils;
|
||||
import jdk.test.lib.cds.CDSOptions;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Try to archive lots of classes by searching for classes from the jrt:/ file system. With JDK 12
|
||||
* this will produce an archive with over 30,000 classes.
|
||||
* @requires vm.cds
|
||||
* @library /test/lib
|
||||
* @run driver/timeout=500 LotsOfClasses
|
||||
*/
|
||||
|
||||
public class LotsOfClasses {
|
||||
static Pattern pattern;
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
ArrayList<String> list = new ArrayList<>();
|
||||
findAllClasses(list);
|
||||
|
||||
CDSOptions opts = new CDSOptions();
|
||||
opts.setClassList(list);
|
||||
opts.addSuffix("--add-modules");
|
||||
opts.addSuffix("ALL-SYSTEM");
|
||||
opts.addSuffix("-Xlog:hashtables");
|
||||
|
||||
OutputAnalyzer out = CDSTestUtils.createArchive(opts);
|
||||
CDSTestUtils.checkDump(out);
|
||||
}
|
||||
|
||||
static void findAllClasses(ArrayList<String> list) throws Throwable {
|
||||
// Find all the classes in the jrt file system
|
||||
pattern = Pattern.compile("/modules/[a-z.]*[a-z]+/([^-]*)[.]class");
|
||||
FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
|
||||
Path base = fs.getPath("/modules/");
|
||||
find(base, list);
|
||||
}
|
||||
|
||||
static void find(Path p, ArrayList<String> list) throws Throwable {
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(p)) {
|
||||
for (Path entry: stream) {
|
||||
Matcher matcher = pattern.matcher(entry.toString());
|
||||
if (matcher.find()) {
|
||||
String className = matcher.group(1);
|
||||
list.add(className);
|
||||
//System.out.println(className);
|
||||
}
|
||||
try {
|
||||
find(entry, list);
|
||||
} catch (Throwable t) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -95,17 +95,17 @@ public class TestCommon extends CDSTestUtils {
|
||||
|
||||
// Create AppCDS archive using most common args - convenience method
|
||||
// Legacy name preserved for compatibility
|
||||
public static OutputAnalyzer dump(String appJar, String appClasses[],
|
||||
public static OutputAnalyzer dump(String appJar, String classList[],
|
||||
String... suffix) throws Exception {
|
||||
return createArchive(appJar, appClasses, suffix);
|
||||
return createArchive(appJar, classList, suffix);
|
||||
}
|
||||
|
||||
|
||||
// Create AppCDS archive using most common args - convenience method
|
||||
public static OutputAnalyzer createArchive(String appJar, String appClasses[],
|
||||
public static OutputAnalyzer createArchive(String appJar, String classList[],
|
||||
String... suffix) throws Exception {
|
||||
AppCDSOptions opts = (new AppCDSOptions()).setAppJar(appJar)
|
||||
.setAppClasses(appClasses);
|
||||
AppCDSOptions opts = (new AppCDSOptions()).setAppJar(appJar);
|
||||
opts.setClassList(classList);
|
||||
opts.addSuffix(suffix);
|
||||
return createArchive(opts);
|
||||
}
|
||||
@ -115,7 +115,6 @@ public class TestCommon extends CDSTestUtils {
|
||||
throws Exception {
|
||||
|
||||
ArrayList<String> cmd = new ArrayList<String>();
|
||||
File classList = makeClassList(opts.appClasses);
|
||||
startNewArchiveName();
|
||||
|
||||
for (String p : opts.prefix) cmd.add(p);
|
||||
@ -129,13 +128,17 @@ public class TestCommon extends CDSTestUtils {
|
||||
}
|
||||
|
||||
cmd.add("-Xshare:dump");
|
||||
cmd.add("-XX:ExtraSharedClassListFile=" + classList.getPath());
|
||||
|
||||
if (opts.archiveName == null)
|
||||
opts.archiveName = getCurrentArchiveName();
|
||||
|
||||
cmd.add("-XX:SharedArchiveFile=" + opts.archiveName);
|
||||
|
||||
if (opts.classList != null) {
|
||||
File classListFile = makeClassList(opts.classList);
|
||||
cmd.add("-XX:ExtraSharedClassListFile=" + classListFile.getPath());
|
||||
}
|
||||
|
||||
for (String s : opts.suffix) cmd.add(s);
|
||||
|
||||
String[] cmdLine = cmd.toArray(new String[cmd.size()]);
|
||||
@ -235,9 +238,9 @@ public class TestCommon extends CDSTestUtils {
|
||||
|
||||
|
||||
// A common operation: dump, then check results
|
||||
public static OutputAnalyzer testDump(String appJar, String appClasses[],
|
||||
public static OutputAnalyzer testDump(String appJar, String classList[],
|
||||
String... suffix) throws Exception {
|
||||
OutputAnalyzer output = dump(appJar, appClasses, suffix);
|
||||
OutputAnalyzer output = dump(appJar, classList, suffix);
|
||||
output.shouldContain("Loading classes to share");
|
||||
output.shouldHaveExitValue(0);
|
||||
return output;
|
||||
@ -245,11 +248,11 @@ public class TestCommon extends CDSTestUtils {
|
||||
|
||||
|
||||
/**
|
||||
* Simple test -- dump and execute appJar with the given appClasses in classlist.
|
||||
* Simple test -- dump and execute appJar with the given classList in classlist.
|
||||
*/
|
||||
public static OutputAnalyzer test(String appJar, String appClasses[], String... args)
|
||||
public static OutputAnalyzer test(String appJar, String classList[], String... args)
|
||||
throws Exception {
|
||||
testDump(appJar, appClasses);
|
||||
testDump(appJar, classList);
|
||||
|
||||
OutputAnalyzer output = exec(appJar, args);
|
||||
return checkExec(output);
|
||||
|
@ -33,6 +33,9 @@ public class CDSOptions {
|
||||
public ArrayList<String> suffix = new ArrayList<String>();
|
||||
public boolean useSystemArchive = false;
|
||||
|
||||
// classes to be archived
|
||||
public String[] classList;
|
||||
|
||||
// Indicate whether to append "-version" when using CDS Archive.
|
||||
// Most of tests will use '-version'
|
||||
public boolean useVersion = true;
|
||||
@ -74,4 +77,16 @@ public class CDSOptions {
|
||||
this.useSystemArchive = use;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CDSOptions setClassList(String[] list) {
|
||||
this.classList = list;
|
||||
return this;
|
||||
}
|
||||
public CDSOptions setClassList(ArrayList<String> list) {
|
||||
String array[] = new String[list.size()];
|
||||
list.toArray(array);
|
||||
this.classList = array;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -248,6 +248,11 @@ public class CDSTestUtils {
|
||||
opts.archiveName = getDefaultArchiveName();
|
||||
cmd.add("-XX:SharedArchiveFile=./" + opts.archiveName);
|
||||
|
||||
if (opts.classList != null) {
|
||||
File classListFile = makeClassList(opts.classList);
|
||||
cmd.add("-XX:ExtraSharedClassListFile=" + classListFile.getPath());
|
||||
}
|
||||
|
||||
for (String s : opts.suffix) cmd.add(s);
|
||||
|
||||
String[] cmdLine = cmd.toArray(new String[cmd.size()]);
|
||||
|
Loading…
Reference in New Issue
Block a user