From 6d269930fdd31ea0cb78f2e1c7f8dcaae9182996 Mon Sep 17 00:00:00 2001 From: Ioi Lam Date: Tue, 20 Nov 2018 20:00:15 -0800 Subject: [PATCH] 8213587: Speed up CDS dump time by using resizable hashtables Reviewed-by: jiangli, coleenp, gziemski --- .../share/classfile/classListParser.cpp | 11 +-- .../share/classfile/classListParser.hpp | 28 ++---- .../share/classfile/classLoaderExt.cpp | 2 - .../share/classfile/compactHashtable.hpp | 2 +- src/hotspot/share/classfile/dictionary.cpp | 1 - src/hotspot/share/classfile/moduleEntry.cpp | 1 - src/hotspot/share/classfile/packageEntry.cpp | 1 - src/hotspot/share/gc/g1/g1CodeCacheRemSet.cpp | 6 +- src/hotspot/share/memory/metaspaceClosure.cpp | 10 ++- src/hotspot/share/memory/metaspaceClosure.hpp | 22 ++--- src/hotspot/share/memory/metaspaceShared.cpp | 30 +++---- .../share/utilities/globalDefinitions.hpp | 13 +++ src/hotspot/share/utilities/hashtable.cpp | 19 +++- src/hotspot/share/utilities/hashtable.hpp | 58 +++++++++++- .../share/utilities/hashtable.inline.hpp | 8 ++ src/hotspot/share/utilities/resourceHash.hpp | 11 +-- .../jtreg/runtime/appcds/AppCDSOptions.java | 11 +-- .../jtreg/runtime/appcds/LotsOfClasses.java | 88 +++++++++++++++++++ .../jtreg/runtime/appcds/TestCommon.java | 27 +++--- test/lib/jdk/test/lib/cds/CDSOptions.java | 15 ++++ test/lib/jdk/test/lib/cds/CDSTestUtils.java | 5 ++ 21 files changed, 264 insertions(+), 105 deletions(-) create mode 100644 test/hotspot/jtreg/runtime/appcds/LotsOfClasses.java diff --git a/src/hotspot/share/classfile/classListParser.cpp b/src/hotspot/share/classfile/classListParser.cpp index 92898d084c681..65d26a8a15176 100644 --- a/src/hotspot/share/classfile/classListParser.cpp +++ b/src/hotspot/share/classfile/classListParser.cpp @@ -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; } diff --git a/src/hotspot/share/classfile/classListParser.hpp b/src/hotspot/share/classfile/classListParser.hpp index 4fe9750749efc..2d82f00b87d4f 100644 --- a/src/hotspot/share/classfile/classListParser.hpp +++ b/src/hotspot/share/classfile/classListParser.hpp @@ -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 { +class ID2KlassTable : public KVHashtable { public: - ID2KlassTable() : Hashtable(1987, sizeof(HashtableEntry)) { } - void add(int id, InstanceKlass* klass) { - unsigned int hash = (unsigned int)id; - HashtableEntry* 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* e = bucket(index); e != NULL; e = e->next()) { - if (e->hash() == hash) { - return e->literal(); - } - } - return NULL; - } + ID2KlassTable() : KVHashtable(1987) {} }; class ClassListParser : public StackObj { diff --git a/src/hotspot/share/classfile/classLoaderExt.cpp b/src/hotspot/share/classfile/classLoaderExt.cpp index 4303ab9c40cd9..de6e19239899f 100644 --- a/src/hotspot/share/classfile/classLoaderExt.cpp +++ b/src/hotspot/share/classfile/classLoaderExt.cpp @@ -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); diff --git a/src/hotspot/share/classfile/compactHashtable.hpp b/src/hotspot/share/classfile/compactHashtable.hpp index 10deca17696ad..3eb31c647b1fd 100644 --- a/src/hotspot/share/classfile/compactHashtable.hpp +++ b/src/hotspot/share/classfile/compactHashtable.hpp @@ -27,7 +27,7 @@ #include "oops/array.hpp" #include "oops/symbol.hpp" -#include "utilities/hashtable.hpp" +#include "utilities/growableArray.hpp" template < diff --git a/src/hotspot/share/classfile/dictionary.cpp b/src/hotspot/share/classfile/dictionary.cpp index b0b6b82b26673..12c4d7e787f5a 100644 --- a/src/hotspot/share/classfile/dictionary.cpp +++ b/src/hotspot/share/classfile/dictionary.cpp @@ -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) { diff --git a/src/hotspot/share/classfile/moduleEntry.cpp b/src/hotspot/share/classfile/moduleEntry.cpp index 42bddf68558d5..dc0db180914ae 100644 --- a/src/hotspot/share/classfile/moduleEntry.cpp +++ b/src/hotspot/share/classfile/moduleEntry.cpp @@ -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, diff --git a/src/hotspot/share/classfile/packageEntry.cpp b/src/hotspot/share/classfile/packageEntry.cpp index 48526bf290d74..4863ec75f22dc 100644 --- a/src/hotspot/share/classfile/packageEntry.cpp +++ b/src/hotspot/share/classfile/packageEntry.cpp @@ -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) { diff --git a/src/hotspot/share/gc/g1/g1CodeCacheRemSet.cpp b/src/hotspot/share/gc/g1/g1CodeCacheRemSet.cpp index 36e5dce737b41..5bdd348e865d6 100644 --- a/src/hotspot/share/gc/g1/g1CodeCacheRemSet.cpp +++ b/src/hotspot/share/gc/g1/g1CodeCacheRemSet.cpp @@ -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* e = new_entry_free_list(); e != NULL; e = new_entry_free_list()) { FREE_C_HEAP_ARRAY(char, e); } diff --git a/src/hotspot/share/memory/metaspaceClosure.cpp b/src/hotspot/share/memory/metaspaceClosure.cpp index c5027d02ac970..e8e7ae22b45e5 100644 --- a/src/hotspot/share/memory/metaspaceClosure.cpp +++ b/src/hotspot/share/memory/metaspaceClosure.cpp @@ -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. } diff --git a/src/hotspot/share/memory/metaspaceClosure.hpp b/src/hotspot/share/memory/metaspaceClosure.hpp index fa67089376c2f..b3f4946449c94 100644 --- a/src/hotspot/share/memory/metaspaceClosure.hpp +++ b/src/hotspot/share/memory/metaspaceClosure.hpp @@ -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 @@ class MetaspaceClosure { // 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
(a); - } - static bool my_equals(const address& a0, const address& a1) { - return primitive_equals
(a0, a1); - } - ResourceHashtable< - address, bool, - UniqueMetaspaceClosure::my_hash, // solaris compiler doesn't like: primitive_hash
- UniqueMetaspaceClosure::my_equals, // solaris compiler doesn't like: primitive_equals
- 15889, // prime number - ResourceObj::C_HEAP> _has_been_visited; + KVHashtable _has_been_visited; }; #endif // SHARE_VM_MEMORY_METASPACE_ITERATOR_HPP diff --git a/src/hotspot/share/memory/metaspaceShared.cpp b/src/hotspot/share/memory/metaspaceShared.cpp index 6bc0ef0468c19..e8ea4f337643b 100644 --- a/src/hotspot/share/memory/metaspaceShared.cpp +++ b/src/hotspot/share/memory/metaspaceShared.cpp @@ -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 @@ class SortedSymbolClosure: public SymbolClosure { // 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
(a); - } - static bool my_equals(const address& a0, const address& a1) { - return primitive_equals
(a0, a1); - } - typedef ResourceHashtable< - address, address, - ArchiveCompactor::my_hash, // solaris compiler doesn't like: primitive_hash
- ArchiveCompactor::my_equals, // solaris compiler doesn't like: primitive_equals
- 16384, ResourceObj::C_HEAP> RelocationTable; + typedef KVHashtable 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 @@ class ArchiveCompactor : AllStatic { 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 @@ class ArchiveCompactor : AllStatic { 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"); diff --git a/src/hotspot/share/utilities/globalDefinitions.hpp b/src/hotspot/share/utilities/globalDefinitions.hpp index 601e900381ac3..72b31ea5a8094 100644 --- a/src/hotspot/share/utilities/globalDefinitions.hpp +++ b/src/hotspot/share/utilities/globalDefinitions.hpp @@ -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 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 bool primitive_equals(const K& k0, const K& k1) { + return k0 == k1; +} + + #endif // SHARE_VM_UTILITIES_GLOBALDEFINITIONS_HPP diff --git a/src/hotspot/share/utilities/hashtable.cpp b/src/hotspot/share/utilities/hashtable.cpp index c4e626803373e..70bcc3698094c 100644 --- a/src/hotspot/share/utilities/hashtable.cpp +++ b/src/hotspot/share/utilities/hashtable.cpp @@ -65,6 +65,7 @@ template BasicHashtableEntry* BasicHashtable::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*)_first_free_entry; @@ -86,7 +87,9 @@ template HashtableEntry* Hashtable::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 HashtableEntry* Hashtable::allocate_new_entry(unsigned int hashValue, T obj) { HashtableEntry* entry = (HashtableEntry*) NEW_C_HEAP_ARRAY(char, this->entry_size(), F); @@ -203,6 +206,20 @@ template bool BasicHashtable::resize(int new_size) { return true; } +template bool BasicHashtable::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(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, you will need to diff --git a/src/hotspot/share/utilities/hashtable.hpp b/src/hotspot/share/utilities/hashtable.hpp index bff7016e0d733..beccd9da5791c 100644 --- a/src/hotspot/share/utilities/hashtable.hpp +++ b/src/hotspot/share/utilities/hashtable.hpp @@ -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 @@ template class BasicHashtable : public CHeapObj { BasicHashtable(int table_size, int entry_size); BasicHashtable(int table_size, int entry_size, HashtableBucket* buckets, int number_of_entries); + ~BasicHashtable(); // Bucket handling int hash_to_index(unsigned int full_hash) const { @@ -163,6 +164,7 @@ template class BasicHashtable : public CHeapObj { char* _end_block; int _entry_size; volatile int _number_of_entries; + GrowableArray* _entry_blocks; protected: @@ -233,6 +235,9 @@ template class BasicHashtable : public CHeapObj { 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 void verify_table(const char* table_name) PRODUCT_RETURN; }; @@ -279,4 +284,55 @@ template class Hashtable : public BasicHashtable { } }; +// 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, + bool (*EQUALS)(K const&, K const&) = primitive_equals + > +class KVHashtable : public BasicHashtable { + class KVHashtableEntry : public BasicHashtableEntry { + public: + K _key; + V _value; + KVHashtableEntry* next() { + return (KVHashtableEntry*)BasicHashtableEntry::next(); + } + }; + +protected: + KVHashtableEntry* bucket(int i) const { + return (KVHashtableEntry*)BasicHashtable::bucket(i); + } + + KVHashtableEntry* new_entry(unsigned int hashValue, K key, V value) { + KVHashtableEntry* entry = (KVHashtableEntry*)BasicHashtable::new_entry(hashValue); + entry->_key = key; + entry->_value = value; + return entry; + } + +public: + KVHashtable(int table_size) : BasicHashtable(table_size, sizeof(KVHashtableEntry)) {} + + void add(K key, V value) { + unsigned int hash = HASH(key); + KVHashtableEntry* entry = new_entry(hash, key, value); + BasicHashtable::add_entry(BasicHashtable::hash_to_index(hash), entry); + } + + V* lookup(K key) { + unsigned int hash = HASH(key); + int index = BasicHashtable::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 diff --git a/src/hotspot/share/utilities/hashtable.inline.hpp b/src/hotspot/share/utilities/hashtable.inline.hpp index 6f06d1f6879a8..4f3f9520156ff 100644 --- a/src/hotspot/share/utilities/hashtable.inline.hpp +++ b/src/hotspot/share/utilities/hashtable.inline.hpp @@ -54,6 +54,13 @@ template inline BasicHashtable::BasicHashtable(int table_size, i _buckets = buckets; } +template inline BasicHashtable::~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 inline void BasicHashtable::initialize(int table_size, int entry_size, int number_of_entries) { @@ -64,6 +71,7 @@ template inline void BasicHashtable::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(4, true, F); } diff --git a/src/hotspot/share/utilities/resourceHash.hpp b/src/hotspot/share/utilities/resourceHash.hpp index 6941d407ad823..a5f663d3eae3e 100644 --- a/src/hotspot/share/utilities/resourceHash.hpp +++ b/src/hotspot/share/utilities/resourceHash.hpp @@ -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 struct ResourceHashtableFns { typedef bool (*equals_fn)(K const&, K const&); }; -template 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 bool primitive_equals(const K& k0, const K& k1) { - return k0 == k1; -} - template< typename K, typename V, // xlC does not compile this: diff --git a/test/hotspot/jtreg/runtime/appcds/AppCDSOptions.java b/test/hotspot/jtreg/runtime/appcds/AppCDSOptions.java index 6db82a771abb0..ecc7e0b725d6e 100644 --- a/test/hotspot/jtreg/runtime/appcds/AppCDSOptions.java +++ b/test/hotspot/jtreg/runtime/appcds/AppCDSOptions.java @@ -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 @@ 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; - } - } diff --git a/test/hotspot/jtreg/runtime/appcds/LotsOfClasses.java b/test/hotspot/jtreg/runtime/appcds/LotsOfClasses.java new file mode 100644 index 0000000000000..6cf58ed4653f3 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/LotsOfClasses.java @@ -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 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 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 list) throws Throwable { + try (DirectoryStream 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) {} + } + } + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/TestCommon.java b/test/hotspot/jtreg/runtime/appcds/TestCommon.java index 4b51ac49899a5..6f9960e5aaa70 100644 --- a/test/hotspot/jtreg/runtime/appcds/TestCommon.java +++ b/test/hotspot/jtreg/runtime/appcds/TestCommon.java @@ -95,17 +95,17 @@ public static void deletePriorArchives() { // 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 static OutputAnalyzer createArchive(AppCDSOptions opts) throws Exception { ArrayList cmd = new ArrayList(); - File classList = makeClassList(opts.appClasses); startNewArchiveName(); for (String p : opts.prefix) cmd.add(p); @@ -129,13 +128,17 @@ public static OutputAnalyzer createArchive(AppCDSOptions opts) } 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 static OutputAnalyzer execModule(String prefix[], String upgrademodulepat // 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 static OutputAnalyzer testDump(String appJar, String appClasses[], /** - * 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); diff --git a/test/lib/jdk/test/lib/cds/CDSOptions.java b/test/lib/jdk/test/lib/cds/CDSOptions.java index 3f457e2074401..c6667c576d923 100644 --- a/test/lib/jdk/test/lib/cds/CDSOptions.java +++ b/test/lib/jdk/test/lib/cds/CDSOptions.java @@ -33,6 +33,9 @@ public class CDSOptions { public ArrayList suffix = new ArrayList(); 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 CDSOptions setUseSystemArchive(boolean use) { this.useSystemArchive = use; return this; } + + public CDSOptions setClassList(String[] list) { + this.classList = list; + return this; + } + public CDSOptions setClassList(ArrayList list) { + String array[] = new String[list.size()]; + list.toArray(array); + this.classList = array; + return this; + } + } diff --git a/test/lib/jdk/test/lib/cds/CDSTestUtils.java b/test/lib/jdk/test/lib/cds/CDSTestUtils.java index 2c84bb7e1d849..c69f8cef71ab9 100644 --- a/test/lib/jdk/test/lib/cds/CDSTestUtils.java +++ b/test/lib/jdk/test/lib/cds/CDSTestUtils.java @@ -248,6 +248,11 @@ public static OutputAnalyzer createArchive(CDSOptions opts) 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()]);