--- a/hotspot/make/aix/makefiles/ppc64.make Tue Dec 23 16:42:48 2014 -0800
+++ b/hotspot/make/aix/makefiles/ppc64.make Tue Dec 23 19:04:23 2014 -0800
@@ -46,7 +46,9 @@
# - 1540-1090 (I) The destructor of "..." might not be called.
# - 1500-010: (W) WARNING in ...: Infinite loop. Program may not stop.
# There are several infinite loops in the vm, suppress.
-CFLAGS += -qsuppress=1540-1090 -qsuppress=1500-010
+# - 1540-1639 (I) The behavior of long type bit fields has changed ...
+# ... long type bit fields now default to long, not int.
+CFLAGS += -qsuppress=1540-1090 -qsuppress=1500-010 -qsuppress=1540-1639
# Suppress
# - 540-1088 (W) The exception specification is being ignored.
--- a/hotspot/make/aix/makefiles/xlc.make Tue Dec 23 16:42:48 2014 -0800
+++ b/hotspot/make/aix/makefiles/xlc.make Tue Dec 23 19:04:23 2014 -0800
@@ -124,7 +124,7 @@
# MAPFLAG = -Xlinker --version-script=FILENAME
# Build shared library
-SHARED_FLAG = -q64 -b64 -bexpall -G -bnoentry -qmkshrobj -brtl -bnolibpath
+SHARED_FLAG = -q64 -b64 -bexpall -G -bnoentry -qmkshrobj -brtl -bnolibpath -bernotok
#------------------------------------------------------------------------
# Debug flags
--- a/hotspot/src/share/vm/classfile/classLoaderData.cpp Tue Dec 23 16:42:48 2014 -0800
+++ b/hotspot/src/share/vm/classfile/classLoaderData.cpp Tue Dec 23 19:04:23 2014 -0800
@@ -786,17 +786,12 @@
MetadataOnStackMark md_on_stack(has_redefined_a_class);
if (has_redefined_a_class) {
- // purge_previous_versions also cleans weak method links. Because
- // one method's MDO can reference another method from another
- // class loader, we need to first clean weak method links for all
- // class loaders here. Below, we can then free redefined methods
- // for all class loaders.
for (ClassLoaderData* data = _head; data != NULL; data = data->next()) {
data->classes_do(InstanceKlass::purge_previous_versions);
}
}
- // Need to purge the previous version before deallocating.
+ // Should purge the previous version before deallocating.
free_deallocate_lists();
}
@@ -834,8 +829,6 @@
void ClassLoaderDataGraph::free_deallocate_lists() {
for (ClassLoaderData* cld = _head; cld != NULL; cld = cld->next()) {
- // We need to keep this data until InstanceKlass::purge_previous_version has been
- // called on all alive classes. See the comment in ClassLoaderDataGraph::clean_metaspaces.
cld->free_deallocate_list();
}
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/classfile/compactHashtable.cpp Tue Dec 23 19:04:23 2014 -0800
@@ -0,0 +1,417 @@
+/*
+ * Copyright (c) 1997, 2014, 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.
+ *
+ */
+
+#include "precompiled.hpp"
+#include "classfile/javaClasses.hpp"
+#include "memory/metaspaceShared.hpp"
+#include "utilities/numberSeq.hpp"
+#include <sys/stat.h>
+
+/////////////////////////////////////////////////////
+//
+// The compact hash table writer implementations
+//
+CompactHashtableWriter::CompactHashtableWriter(const char* table_name,
+ int num_entries,
+ CompactHashtableStats* stats) {
+ assert(DumpSharedSpaces, "dump-time only");
+ _table_name = table_name;
+ _num_entries = num_entries;
+ _num_buckets = number_of_buckets(_num_entries);
+ _buckets = NEW_C_HEAP_ARRAY(Entry*, _num_buckets, mtSymbol);
+ memset(_buckets, 0, sizeof(Entry*) * _num_buckets);
+
+ /* bucket sizes table */
+ _bucket_sizes = NEW_C_HEAP_ARRAY(juint, _num_buckets, mtSymbol);
+ memset(_bucket_sizes, 0, sizeof(juint) * _num_buckets);
+
+ stats->hashentry_count = _num_entries;
+ // Compact buckets' entries will have only the 4-byte offset, but
+ // we don't know how many there will be at this point. So use a
+ // conservative estimate here. The size is adjusted later when we
+ // write out the buckets.
+ stats->hashentry_bytes = _num_entries * 8;
+ stats->bucket_count = _num_buckets;
+ stats->bucket_bytes = (_num_buckets + 1) * (sizeof(juint));
+ _stats = stats;
+
+ // See compactHashtable.hpp for table layout
+ _required_bytes = sizeof(juint) * 2; // _base_address, written as 2 juints
+ _required_bytes+= sizeof(juint) + // num_entries
+ sizeof(juint) + // num_buckets
+ stats->hashentry_bytes +
+ stats->bucket_bytes;
+}
+
+CompactHashtableWriter::~CompactHashtableWriter() {
+ for (int index = 0; index < _num_buckets; index++) {
+ Entry* next = NULL;
+ for (Entry* tent = _buckets[index]; tent; tent = next) {
+ next = tent->next();
+ delete tent;
+ }
+ }
+
+ FREE_C_HEAP_ARRAY(juint, _bucket_sizes);
+ FREE_C_HEAP_ARRAY(Entry*, _buckets);
+}
+
+// Calculate the number of buckets in the temporary hash table
+int CompactHashtableWriter::number_of_buckets(int num_entries) {
+ const int buksize = (int)SharedSymbolTableBucketSize;
+ int num_buckets = (num_entries + buksize - 1) / buksize;
+ num_buckets = (num_buckets + 1) & (~0x01);
+
+ return num_buckets;
+}
+
+// Add a symbol entry to the temporary hash table
+void CompactHashtableWriter::add(unsigned int hash, Entry* entry) {
+ int index = hash % _num_buckets;
+ entry->set_next(_buckets[index]);
+ _buckets[index] = entry;
+ _bucket_sizes[index] ++;
+}
+
+// Write the compact table's bucket infos
+juint* CompactHashtableWriter::dump_table(juint* p, juint** first_bucket,
+ NumberSeq* summary) {
+ int index;
+ juint* compact_table = p;
+ // Find the start of the buckets, skip the compact_bucket_infos table
+ // and the table end offset.
+ juint offset = _num_buckets + 1;
+ *first_bucket = compact_table + offset;
+
+ for (index = 0; index < _num_buckets; index++) {
+ int bucket_size = _bucket_sizes[index];
+ if (bucket_size == 1) {
+ // bucket with one entry is compacted and only has the symbol offset
+ compact_table[index] = BUCKET_INFO(offset, COMPACT_BUCKET_TYPE);
+ offset += bucket_size; // each entry contains symbol offset only
+ } else {
+ // regular bucket, each entry is a symbol (hash, offset) pair
+ compact_table[index] = BUCKET_INFO(offset, REGULAR_BUCKET_TYPE);
+ offset += bucket_size * 2; // each hash entry is 2 juints
+ }
+ if (offset & ~BUCKET_OFFSET_MASK) {
+ vm_exit_during_initialization("CompactHashtableWriter::dump_table: Overflow! "
+ "Too many symbols.");
+ }
+ summary->add(bucket_size);
+ }
+ // Mark the end of the table
+ compact_table[_num_buckets] = BUCKET_INFO(offset, TABLEEND_BUCKET_TYPE);
+
+ return compact_table;
+}
+
+// Write the compact table's entries
+juint* CompactHashtableWriter::dump_buckets(juint* compact_table, juint* p,
+ NumberSeq* summary) {
+ uintx base_address = uintx(MetaspaceShared::shared_rs()->base());
+ uintx max_delta = uintx(MetaspaceShared::shared_rs()->size());
+ assert(max_delta <= 0x7fffffff, "range check");
+ int num_compact_buckets = 0;
+
+ assert(p != NULL, "sanity");
+ for (int index = 0; index < _num_buckets; index++) {
+ juint count = 0;
+ int bucket_size = _bucket_sizes[index];
+ int bucket_type = BUCKET_TYPE(compact_table[index]);
+
+ if (bucket_size == 1) {
+ assert(bucket_type == COMPACT_BUCKET_TYPE, "Bad bucket type");
+ num_compact_buckets ++;
+ }
+ for (Entry* tent = _buckets[index]; tent;
+ tent = tent->next()) {
+ if (bucket_type == REGULAR_BUCKET_TYPE) {
+ *p++ = juint(tent->hash()); // write symbol hash
+ }
+ uintx deltax = uintx(tent->value()) - base_address;
+ assert(deltax < max_delta, "range check");
+ juint delta = juint(deltax);
+ *p++ = delta; // write symbol offset
+ count ++;
+ }
+ assert(count == _bucket_sizes[index], "sanity");
+ }
+
+ // Adjust the hashentry_bytes in CompactHashtableStats. Each compact
+ // bucket saves 4-byte.
+ _stats->hashentry_bytes -= num_compact_buckets * 4;
+
+ return p;
+}
+
+// Write the compact table
+void CompactHashtableWriter::dump(char** top, char* end) {
+ NumberSeq summary;
+ char* old_top = *top;
+ juint* p = (juint*)(*top);
+
+ uintx base_address = uintx(MetaspaceShared::shared_rs()->base());
+
+ *p++ = high(base_address);
+ *p++ = low (base_address); // base address
+ *p++ = _num_entries; // number of entries in the table
+ *p++ = _num_buckets; // number of buckets in the table
+
+ juint* first_bucket = NULL;
+ juint* compact_table = dump_table(p, &first_bucket, &summary);
+ juint* bucket_end = dump_buckets(compact_table, first_bucket, &summary);
+
+ assert(bucket_end <= (juint*)end, "cannot write past end");
+ *top = (char*)bucket_end;
+
+ if (PrintSharedSpaces) {
+ double avg_cost = 0.0;
+ if (_num_entries > 0) {
+ avg_cost = double(_required_bytes)/double(_num_entries);
+ }
+ tty->print_cr("Shared %s table stats -------- base: " PTR_FORMAT, _table_name, (intptr_t)base_address);
+ tty->print_cr("Number of entries : %9d", _num_entries);
+ tty->print_cr("Total bytes used : %9d", (int)((*top) - old_top));
+ tty->print_cr("Average bytes per entry : %9.3f", avg_cost);
+ tty->print_cr("Average bucket size : %9.3f", summary.avg());
+ tty->print_cr("Variance of bucket size : %9.3f", summary.variance());
+ tty->print_cr("Std. dev. of bucket size: %9.3f", summary.sd());
+ tty->print_cr("Maximum bucket size : %9d", (int)summary.maximum());
+ }
+}
+
+/////////////////////////////////////////////////////////////
+//
+// The CompactHashtable implementation
+//
+template <class T, class N> const char* CompactHashtable<T, N>::init(const char* buffer) {
+ assert(!DumpSharedSpaces, "run-time only");
+ juint*p = (juint*)buffer;
+ juint upper = *p++;
+ juint lower = *p++;
+ _base_address = uintx(jlong_from(upper, lower));
+ _entry_count = *p++;
+ _bucket_count = *p++;
+ _buckets = p;
+ _table_end_offset = BUCKET_OFFSET(p[_bucket_count]); // located at the end of the bucket_info table
+
+ juint *end = _buckets + _table_end_offset;
+ return (const char*)end;
+}
+
+// Explicitly instantiate these types
+template class CompactHashtable<Symbol*, char>;
+
+#ifndef O_BINARY // if defined (Win32) use binary files.
+#define O_BINARY 0 // otherwise do nothing.
+#endif
+
+////////////////////////////////////////////////////////
+//
+// HashtableTextDump
+//
+HashtableTextDump::HashtableTextDump(const char* filename) : _fd(-1) {
+ struct stat st;
+ if (os::stat(filename, &st) != 0) {
+ quit("Unable to get hashtable dump file size", filename);
+ }
+ _size = st.st_size;
+ _fd = open(filename, O_RDONLY | O_BINARY, 0);
+ if (_fd < 0) {
+ quit("Unable to open hashtable dump file", filename);
+ }
+ _base = os::map_memory(_fd, filename, 0, NULL, _size, true, false);
+ if (_base == NULL) {
+ quit("Unable to map hashtable dump file", filename);
+ }
+ _p = _base;
+ _end = _base + st.st_size;
+ _filename = filename;
+}
+
+HashtableTextDump::~HashtableTextDump() {
+ os::unmap_memory((char*)_base, _size);
+ if (_fd >= 0) {
+ close(_fd);
+ }
+}
+
+void HashtableTextDump::quit(const char* err, const char* msg) {
+ vm_exit_during_initialization(err, msg);
+}
+
+void HashtableTextDump::corrupted(const char *p) {
+ char info[60];
+ sprintf(info, "corrupted at pos %d", (int)(p - _base));
+ quit(info, _filename);
+}
+
+bool HashtableTextDump::skip_newline() {
+ if (_p[0] == '\r' && _p[1] == '\n') {
+ _p += 2;
+ } else if (_p[0] == '\n') {
+ _p += 1;
+ } else {
+ corrupted(_p);
+ }
+ return true;
+}
+
+int HashtableTextDump::skip(char must_be_char) {
+ corrupted_if(remain() < 1);
+ corrupted_if(*_p++ != must_be_char);
+ return 0;
+}
+
+void HashtableTextDump::skip_past(char c) {
+ for (;;) {
+ corrupted_if(remain() < 1);
+ if (*_p++ == c) {
+ return;
+ }
+ }
+}
+
+void HashtableTextDump::check_version(const char* ver) {
+ int len = (int)strlen(ver);
+ corrupted_if(remain() < len);
+ if (strncmp(_p, ver, len) != 0) {
+ quit("wrong version of hashtable dump file", _filename);
+ }
+ _p += len;
+ skip_newline();
+}
+
+
+int HashtableTextDump::scan_prefix() {
+ // Expect /[0-9]+: /
+ int utf8_length = get_num(':');
+ if (*_p != ' ') {
+ corrupted(_p);
+ }
+ _p++;
+ return utf8_length;
+}
+
+int HashtableTextDump::scan_prefix2() {
+ // Expect /[0-9]+ (-|)[0-9]+: /
+ int utf8_length = get_num(' ');
+ if (*_p == '-') {
+ _p++;
+ }
+ (void)get_num(':');
+ if (*_p != ' ') {
+ corrupted(_p);
+ }
+ _p++;
+ return utf8_length;
+}
+
+jchar HashtableTextDump::unescape(const char* from, const char* end, int count) {
+ jchar value = 0;
+
+ corrupted_if(from + count > end);
+
+ for (int i=0; i<count; i++) {
+ char c = *from++;
+ switch (c) {
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ value = (value << 4) + c - '0';
+ break;
+ case 'a': case 'b': case 'c':
+ case 'd': case 'e': case 'f':
+ value = (value << 4) + 10 + c - 'a';
+ break;
+ case 'A': case 'B': case 'C':
+ case 'D': case 'E': case 'F':
+ value = (value << 4) + 10 + c - 'A';
+ break;
+ default:
+ ShouldNotReachHere();
+ }
+ }
+ return value;
+}
+
+void HashtableTextDump::get_utf8(char* utf8_buffer, int utf8_length) {
+ // cache in local vars
+ const char* from = _p;
+ const char* end = _end;
+ char* to = utf8_buffer;
+ int n = utf8_length;
+
+ for (; n > 0 && from < end; n--) {
+ if (*from != '\\') {
+ *to++ = *from++;
+ } else {
+ corrupted_if(from + 2 > end);
+ char c = from[1];
+ from += 2;
+ switch (c) {
+ case 'x':
+ {
+ jchar value = unescape(from, end, 2);
+ from += 2;
+ assert(value <= 0xff, "sanity");
+ *to++ = (char)(value & 0xff);
+ }
+ break;
+ case 't': *to++ = '\t'; break;
+ case 'n': *to++ = '\n'; break;
+ case 'r': *to++ = '\r'; break;
+ case '\\': *to++ = '\\'; break;
+ default:
+ ShouldNotReachHere();
+ }
+ }
+ }
+ corrupted_if(n > 0); // expected more chars but file has ended
+ _p = from;
+ skip_newline();
+}
+
+// NOTE: the content is NOT the same as
+// UTF8::as_quoted_ascii(const char* utf8_str, int utf8_length, char* buf, int buflen).
+// We want to escape \r\n\t so that output [1] is more readable; [2] can be more easily
+// parsed by scripts; [3] quickly processed by HashtableTextDump::get_utf8()
+void HashtableTextDump::put_utf8(outputStream* st, const char* utf8_string, int utf8_length) {
+ const char *c = utf8_string;
+ const char *end = c + utf8_length;
+ for (; c < end; c++) {
+ switch (*c) {
+ case '\t': st->print("\\t"); break;
+ case '\r': st->print("\\r"); break;
+ case '\n': st->print("\\n"); break;
+ case '\\': st->print("\\\\"); break;
+ default:
+ if (isprint(*c)) {
+ st->print("%c", *c);
+ } else {
+ st->print("\\x%02x", ((unsigned int)*c) & 0xff);
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/src/share/vm/classfile/compactHashtable.hpp Tue Dec 23 19:04:23 2014 -0800
@@ -0,0 +1,405 @@
+/*
+ * Copyright (c) 1997, 2014, 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_VM_CLASSFILE_COMPACTHASHTABLE_HPP
+#define SHARE_VM_CLASSFILE_COMPACTHASHTABLE_HPP
+
+#include "classfile/stringTable.hpp"
+#include "classfile/symbolTable.hpp"
+#include "memory/allocation.inline.hpp"
+#include "oops/symbol.hpp"
+#include "services/diagnosticCommand.hpp"
+#include "utilities/hashtable.hpp"
+
+class NumberSeq;
+
+// Stats for symbol tables in the CDS archive
+class CompactHashtableStats VALUE_OBJ_CLASS_SPEC {
+public:
+ int hashentry_count;
+ int hashentry_bytes;
+ int bucket_count;
+ int bucket_bytes;
+};
+
+/////////////////////////////////////////////////////////////////////////
+//
+// The compact hash table writer. Used at dump time for writing out
+// the compact table to the shared archive.
+//
+// At dump time, the CompactHashtableWriter obtains all entries from the
+// symbol table and adds them to a new temporary hash table. The hash
+// table size (number of buckets) is calculated using
+// '(num_entries + bucket_size - 1) / bucket_size'. The default bucket
+// size is 4 and can be changed by -XX:SharedSymbolTableBucketSize option.
+// 4 is chosen because it produces smaller sized bucket on average for
+// faster lookup. It also has relatively small number of empty buckets and
+// good distribution of the entries.
+//
+// We use a simple hash function (symbol_hash % num_bucket) for the table.
+// The new table is compacted when written out. Please see comments
+// above the CompactHashtable class for the table layout detail. The bucket
+// offsets are written to the archive as part of the compact table. The
+// bucket offset is encoded in the low 30-bit (0-29) and the bucket type
+// (regular or compact) are encoded in bit[31, 30]. For buckets with more
+// than one entry, both symbol hash and symbol offset are written to the
+// table. For buckets with only one entry, only the symbol offset is written
+// to the table and the buckets are tagged as compact in their type bits.
+// Buckets without entry are skipped from the table. Their offsets are
+// still written out for faster lookup.
+//
+class CompactHashtableWriter: public StackObj {
+public:
+ class Entry: public CHeapObj<mtSymbol> {
+ Entry* _next;
+ unsigned int _hash;
+ void* _literal;
+
+ public:
+ Entry(unsigned int hash, Symbol *symbol) : _next(NULL), _hash(hash), _literal(symbol) {}
+
+ void *value() {
+ return _literal;
+ }
+ Symbol *symbol() {
+ return (Symbol*)_literal;
+ }
+ unsigned int hash() {
+ return _hash;
+ }
+ Entry *next() {return _next;}
+ void set_next(Entry *p) {_next = p;}
+ }; // class CompactHashtableWriter::Entry
+
+private:
+ static int number_of_buckets(int num_entries);
+
+ const char* _table_name;
+ int _num_entries;
+ int _num_buckets;
+ juint* _bucket_sizes;
+ Entry** _buckets;
+ int _required_bytes;
+ CompactHashtableStats* _stats;
+
+public:
+ // This is called at dump-time only
+ CompactHashtableWriter(const char* table_name, int num_entries, CompactHashtableStats* stats);
+ ~CompactHashtableWriter();
+
+ int get_required_bytes() {
+ return _required_bytes;
+ }
+
+ void add(unsigned int hash, Symbol* symbol) {
+ add(hash, new Entry(hash, symbol));
+ }
+
+private:
+ void add(unsigned int hash, Entry* entry);
+ juint* dump_table(juint* p, juint** first_bucket, NumberSeq* summary);
+ juint* dump_buckets(juint* table, juint* p, NumberSeq* summary);
+
+public:
+ void dump(char** top, char* end);
+};
+
+#define REGULAR_BUCKET_TYPE 0
+#define COMPACT_BUCKET_TYPE 1
+#define TABLEEND_BUCKET_TYPE 3
+#define BUCKET_OFFSET_MASK 0x3FFFFFFF
+#define BUCKET_OFFSET(info) ((info) & BUCKET_OFFSET_MASK)
+#define BUCKET_TYPE_SHIFT 30
+#define BUCKET_TYPE(info) (((info) & ~BUCKET_OFFSET_MASK) >> BUCKET_TYPE_SHIFT)
+#define BUCKET_INFO(offset, type) (((type) << BUCKET_TYPE_SHIFT) | ((offset) & BUCKET_OFFSET_MASK))
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// CompactHashtable is used to stored the CDS archive's symbol table. Used
+// at runtime only to access the compact table from the archive.
+//
+// Because these tables are read-only (no entries can be added/deleted) at run-time
+// and tend to have large number of entries, we try to minimize the footprint
+// cost per entry.
+//
+// Layout of compact symbol table in the shared archive:
+//
+// uintx base_address;
+// juint num_symbols;
+// juint num_buckets;
+// juint bucket_infos[num_buckets+1]; // bit[31,30]: type; bit[29-0]: offset
+// juint table[]
+//
+// -----------------------------------
+// | base_address | num_symbols |
+// |---------------------------------|
+// | num_buckets | bucket_info0 |
+// |---------------------------------|
+// | bucket_info1 | bucket_info2 |
+// | bucket_info3 ... |
+// | .... | table_end_info |
+// |---------------------------------|
+// | entry0 |
+// | entry1 |
+// | entry2 |
+// | |
+// | ... |
+// -----------------------------------
+//
+// The size of the bucket_info table is 'num_buckets + 1'. Each entry of the
+// bucket_info table is a 32-bit encoding of the bucket type and bucket offset,
+// with the type in the left-most 2-bit and offset in the remaining 30-bit.
+// The last entry is a special type. It contains the offset of the last
+// bucket end. We use that information when traversing the compact table.
+//
+// There are two types of buckets, regular buckets and compact buckets. The
+// compact buckets have '01' in their highest 2-bit, and regular buckets have
+// '00' in their highest 2-bit.
+//
+// For normal buckets, each symbol's entry is 8 bytes in the table[]:
+// juint hash; /* symbol hash */
+// juint offset; /* Symbol* sym = (Symbol*)(base_address + offset) */
+//
+// For compact buckets, each entry has only the 4-byte 'offset' in the table[].
+//
+// See CompactHashtable::lookup() for how the table is searched at runtime.
+// See CompactHashtableWriter::dump() for how the table is written at CDS
+// dump time.
+//
+template <class T, class N> class CompactHashtable VALUE_OBJ_CLASS_SPEC {
+ uintx _base_address;
+ juint _entry_count;
+ juint _bucket_count;
+ juint _table_end_offset;
+ juint* _buckets;
+
+ inline bool equals(T entry, const char* name, int len) {
+ if (entry->equals(name, len)) {
+ assert(entry->refcount() == -1, "must be shared");
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+public:
+ CompactHashtable() {
+ _entry_count = 0;
+ _bucket_count = 0;
+ _table_end_offset = 0;
+ _buckets = 0;
+ }
+ const char* init(const char *buffer);
+
+ // Lookup an entry from the compact table
+ inline T lookup(const N* name, unsigned int hash, int len) {
+ if (_entry_count > 0) {
+ assert(!DumpSharedSpaces, "run-time only");
+ int index = hash % _bucket_count;
+ juint bucket_info = _buckets[index];
+ juint bucket_offset = BUCKET_OFFSET(bucket_info);
+ int bucket_type = BUCKET_TYPE(bucket_info);
+ juint* bucket = _buckets + bucket_offset;
+ juint* bucket_end = _buckets;
+
+ if (bucket_type == COMPACT_BUCKET_TYPE) {
+ // the compact bucket has one entry with symbol offset only
+ T entry = (T)((void*)(_base_address + bucket[0]));
+ if (equals(entry, name, len)) {
+ return entry;
+ }
+ } else {
+ // This is a regular bucket, which has more than one
+ // entries. Each entry is a pair of symbol (hash, offset).
+ // Seek until the end of the bucket.
+ bucket_end += BUCKET_OFFSET(_buckets[index + 1]);
+ while (bucket < bucket_end) {
+ unsigned int h = (unsigned int)(bucket[0]);
+ if (h == hash) {
+ juint offset = bucket[1];
+ T entry = (T)((void*)(_base_address + offset));
+ if (equals(entry, name, len)) {
+ return entry;
+ }
+ }
+ bucket += 2;
+ }
+ }
+ }
+ return NULL;
+ }
+};
+
+////////////////////////////////////////////////////////////////////////
+//
+// Read/Write the contents of a hashtable textual dump (created by
+// SymbolTable::dump).
+// Because the dump file may be big (hundred of MB in extreme cases),
+// we use mmap for fast access when reading it.
+//
+class HashtableTextDump VALUE_OBJ_CLASS_SPEC {
+ int _fd;
+ const char* _base;
+ const char* _p;
+ const char* _end;
+ const char* _filename;
+ size_t _size;
+public:
+ HashtableTextDump(const char* filename);
+ ~HashtableTextDump();
+
+ void quit(const char* err, const char* msg);
+
+ inline int remain() {
+ return (int)(_end - _p);
+ }
+
+ void corrupted(const char *p);
+
+ inline void corrupted_if(bool cond) {
+ if (cond) {
+ corrupted(_p);
+ }
+ }
+
+ bool skip_newline();
+ int skip(char must_be_char);
+ void skip_past(char c);
+ void check_version(const char* ver);
+
+ inline int get_num(char delim) {
+ const char* p = _p;
+ const char* end = _end;
+ int num = 0;
+
+ while (p < end) {
+ char c = *p ++;
+ if ('0' <= c && c <= '9') {
+ num = num * 10 + (c - '0');
+ } else if (c == delim) {
+ _p = p;
+ return num;
+ } else {
+ corrupted(p-1);
+ }
+ }
+ corrupted(_end);
+ ShouldNotReachHere();
+ return 0;
+ }
+
+ int scan_prefix();
+ int scan_prefix2();
+
+ jchar unescape(const char* from, const char* end, int count);
+ void get_utf8(char* utf8_buffer, int utf8_length);
+ static void put_utf8(outputStream* st, const char* utf8_string, int utf8_length);
+};
+
+///////////////////////////////////////////////////////////////////////
+//
+// jcmd command support for symbol table and string table dumping:
+// VM.symboltable -verbose: for dumping the symbol table
+// VM.stringtable -verbose: for dumping the string table
+//
+class VM_DumpHashtable : public VM_Operation {
+private:
+ outputStream* _out;
+ int _which;
+ bool _verbose;
+public:
+ enum {
+ DumpSymbols = 1 << 0,
+ DumpStrings = 1 << 1,
+ DumpSysDict = 1 << 2 // not implemented yet
+ };
+ VM_DumpHashtable(outputStream* out, int which, bool verbose) {
+ _out = out;
+ _which = which;
+ _verbose = verbose;
+ }
+
+ virtual VMOp_Type type() const { return VMOp_DumpHashtable; }
+
+ virtual void doit() {
+ switch (_which) {
+ case DumpSymbols:
+ SymbolTable::dump(_out, _verbose);
+ break;
+ case DumpStrings:
+ StringTable::dump(_out, _verbose);
+ break;
+ default:
+ ShouldNotReachHere();
+ }
+ }
+};
+
+class SymboltableDCmd : public DCmdWithParser {
+protected:
+ DCmdArgument<bool> _verbose;
+public:
+ SymboltableDCmd(outputStream* output, bool heap);
+ static const char* name() {
+ return "VM.symboltable";
+ }
+ static const char* description() {
+ return "Dump symbol table.";
+ }
+ static const char* impact() {
+ return "Medium: Depends on Java content.";
+ }
+ static const JavaPermission permission() {
+ JavaPermission p = {"java.lang.management.ManagementPermission",
+ "monitor", NULL};
+ return p;
+ }
+ static int num_arguments();
+ virtual void execute(DCmdSource source, TRAPS);
+};
+
+class StringtableDCmd : public DCmdWithParser {
+protected:
+ DCmdArgument<bool> _verbose;
+public:
+ StringtableDCmd(outputStream* output, bool heap);
+ static const char* name() {
+ return "VM.stringtable";
+ }
+ static const char* description() {
+ return "Dump string table.";
+ }
+ static const char* impact() {
+ return "Medium: Depends on Java content.";
+ }
+ static const JavaPermission permission() {
+ JavaPermission p = {"java.lang.management.ManagementPermission",
+ "monitor", NULL};
+ return p;
+ }
+ static int num_arguments();
+ virtual void execute(DCmdSource source, TRAPS);
+};
+
+#endif // SHARE_VM_CLASSFILE_COMPACTHASHTABLE_HPP
--- a/hotspot/src/share/vm/classfile/stringTable.cpp Tue Dec 23 16:42:48 2014 -0800
+++ b/hotspot/src/share/vm/classfile/stringTable.cpp Tue Dec 23 19:04:23 2014 -0800
@@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "classfile/altHashing.hpp"
+#include "classfile/compactHashtable.hpp"
#include "classfile/javaClasses.hpp"
#include "classfile/stringTable.hpp"
#include "classfile/systemDictionary.hpp"
@@ -379,8 +380,36 @@
}
}
-void StringTable::dump(outputStream* st) {
- the_table()->dump_table(st, "StringTable");
+void StringTable::dump(outputStream* st, bool verbose) {
+ if (!verbose) {
+ the_table()->dump_table(st, "StringTable");
+ } else {
+ Thread* THREAD = Thread::current();
+ st->print_cr("VERSION: 1.1");
+ for (int i = 0; i < the_table()->table_size(); ++i) {
+ HashtableEntry<oop, mtSymbol>* p = the_table()->bucket(i);
+ for ( ; p != NULL; p = p->next()) {
+ oop s = p->literal();
+ typeArrayOop value = java_lang_String::value(s);
+ int offset = java_lang_String::offset(s);
+ int length = java_lang_String::length(s);
+
+ if (length <= 0) {
+ st->print("%d: ", length);
+ } else {
+ ResourceMark rm(THREAD);
+ jchar* chars = (jchar*)value->char_at_addr(offset);
+ int utf8_length = UNICODE::utf8_length(chars, length);
+ char* utf8_string = NEW_RESOURCE_ARRAY(char, utf8_length + 1);
+ UNICODE::convert_to_utf8(chars, length, utf8_string);
+
+ st->print("%d: ", utf8_length);
+ HashtableTextDump::put_utf8(st, utf8_string, utf8_length);
+ }
+ st->cr();
+ }
+ }
+ }
}
StringTable::VerifyRetTypes StringTable::compare_entries(
@@ -558,3 +587,28 @@
_needs_rehashing = false;
_the_table = new_table;
}
+
+// Utility for dumping strings
+StringtableDCmd::StringtableDCmd(outputStream* output, bool heap) :
+ DCmdWithParser(output, heap),
+ _verbose("-verbose", "Dump the content of each string in the table",
+ "BOOLEAN", false, "false") {
+ _dcmdparser.add_dcmd_option(&_verbose);
+}
+
+void StringtableDCmd::execute(DCmdSource source, TRAPS) {
+ VM_DumpHashtable dumper(output(), VM_DumpHashtable::DumpStrings,
+ _verbose.value());
+ VMThread::execute(&dumper);
+}
+
+int StringtableDCmd::num_arguments() {
+ ResourceMark rm;
+ StringtableDCmd* dcmd = new StringtableDCmd(NULL, false);
+ if (dcmd != NULL) {
+ DCmdMark mark(dcmd);
+ return dcmd->_dcmdparser.num_arguments();
+ } else {
+ return 0;
+ }
+}
--- a/hotspot/src/share/vm/classfile/stringTable.hpp Tue Dec 23 16:42:48 2014 -0800
+++ b/hotspot/src/share/vm/classfile/stringTable.hpp Tue Dec 23 19:04:23 2014 -0800
@@ -118,7 +118,7 @@
// Debugging
static void verify();
- static void dump(outputStream* st);
+ static void dump(outputStream* st, bool verbose=false);
enum VerifyMesgModes {
_verify_quietly = 0,
--- a/hotspot/src/share/vm/classfile/symbolTable.cpp Tue Dec 23 16:42:48 2014 -0800
+++ b/hotspot/src/share/vm/classfile/symbolTable.cpp Tue Dec 23 19:04:23 2014 -0800
@@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "classfile/altHashing.hpp"
+#include "classfile/compactHashtable.hpp"
#include "classfile/javaClasses.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
@@ -47,6 +48,9 @@
// Static arena for symbols that are not deallocated
Arena* SymbolTable::_arena = NULL;
bool SymbolTable::_needs_rehashing = false;
+bool SymbolTable::_lookup_shared_first = false;
+
+CompactHashtable<Symbol*, char> SymbolTable::_shared_table;
Symbol* SymbolTable::allocate_symbol(const u1* name, int len, bool c_heap, TRAPS) {
assert (len <= Symbol::max_length(), "should be checked by caller");
@@ -186,8 +190,8 @@
// Lookup a symbol in a bucket.
-Symbol* SymbolTable::lookup(int index, const char* name,
- int len, unsigned int hash) {
+Symbol* SymbolTable::lookup_dynamic(int index, const char* name,
+ int len, unsigned int hash) {
int count = 0;
for (HashtableEntry<Symbol*, mtSymbol>* e = bucket(index); e != NULL; e = e->next()) {
count++; // count all entries in this bucket, not just ones with same hash
@@ -207,6 +211,34 @@
return NULL;
}
+Symbol* SymbolTable::lookup_shared(const char* name,
+ int len, unsigned int hash) {
+ return _shared_table.lookup(name, hash, len);
+}
+
+Symbol* SymbolTable::lookup(int index, const char* name,
+ int len, unsigned int hash) {
+ Symbol* sym;
+ if (_lookup_shared_first) {
+ sym = lookup_shared(name, len, hash);
+ if (sym != NULL) {
+ return sym;
+ }
+ _lookup_shared_first = false;
+ return lookup_dynamic(index, name, len, hash);
+ } else {
+ sym = lookup_dynamic(index, name, len, hash);
+ if (sym != NULL) {
+ return sym;
+ }
+ sym = lookup_shared(name, len, hash);
+ if (sym != NULL) {
+ _lookup_shared_first = true;
+ }
+ return sym;
+ }
+}
+
// Pick hashing algorithm.
unsigned int SymbolTable::hash_symbol(const char* s, int len) {
return use_alternate_hashcode() ?
@@ -483,10 +515,56 @@
}
}
-void SymbolTable::dump(outputStream* st) {
- the_table()->dump_table(st, "SymbolTable");
+void SymbolTable::dump(outputStream* st, bool verbose) {
+ if (!verbose) {
+ the_table()->dump_table(st, "SymbolTable");
+ } else {
+ st->print_cr("VERSION: 1.0");
+ for (int i = 0; i < the_table()->table_size(); ++i) {
+ HashtableEntry<Symbol*, mtSymbol>* p = the_table()->bucket(i);
+ for ( ; p != NULL; p = p->next()) {
+ Symbol* s = (Symbol*)(p->literal());
+ const char* utf8_string = (const char*)s->bytes();
+ int utf8_length = s->utf8_length();
+ st->print("%d %d: ", utf8_length, s->refcount());
+ HashtableTextDump::put_utf8(st, utf8_string, utf8_length);
+ st->cr();
+ }
+ }
+ }
}
+bool SymbolTable::copy_compact_table(char** top, char*end) {
+#if INCLUDE_CDS
+ CompactHashtableWriter ch_table("symbol", the_table()->number_of_entries(),
+ &MetaspaceShared::stats()->symbol);
+ if (*top + ch_table.get_required_bytes() > end) {
+ // not enough space left
+ return false;
+ }
+
+ for (int i = 0; i < the_table()->table_size(); ++i) {
+ HashtableEntry<Symbol*, mtSymbol>* p = the_table()->bucket(i);
+ for ( ; p != NULL; p = p->next()) {
+ Symbol* s = (Symbol*)(p->literal());
+ unsigned int fixed_hash = hash_symbol((char*)s->bytes(), s->utf8_length());
+ assert(fixed_hash == p->hash(), "must not rehash during dumping");
+ ch_table.add(fixed_hash, s);
+ }
+ }
+
+ char* old_top = *top;
+ ch_table.dump(top, end);
+
+ *top = (char*)align_pointer_up(*top, sizeof(void*));
+#endif
+ return true;
+}
+
+const char* SymbolTable::init_shared_table(const char* buffer) {
+ const char* end = _shared_table.init(buffer);
+ return (const char*)align_pointer_up(end, sizeof(void*));
+}
//---------------------------------------------------------------------------
// Non-product code
@@ -574,3 +652,29 @@
}
}
#endif // PRODUCT
+
+
+// Utility for dumping symbols
+SymboltableDCmd::SymboltableDCmd(outputStream* output, bool heap) :
+ DCmdWithParser(output, heap),
+ _verbose("-verbose", "Dump the content of each symbol in the table",
+ "BOOLEAN", false, "false") {
+ _dcmdparser.add_dcmd_option(&_verbose);
+}
+
+void SymboltableDCmd::execute(DCmdSource source, TRAPS) {
+ VM_DumpHashtable dumper(output(), VM_DumpHashtable::DumpSymbols,
+ _verbose.value());
+ VMThread::execute(&dumper);
+}
+
+int SymboltableDCmd::num_arguments() {
+ ResourceMark rm;
+ SymboltableDCmd* dcmd = new SymboltableDCmd(NULL, false);
+ if (dcmd != NULL) {
+ DCmdMark mark(dcmd);
+ return dcmd->_dcmdparser.num_arguments();
+ } else {
+ return 0;
+ }
+}
--- a/hotspot/src/share/vm/classfile/symbolTable.hpp Tue Dec 23 16:42:48 2014 -0800
+++ b/hotspot/src/share/vm/classfile/symbolTable.hpp Tue Dec 23 19:04:23 2014 -0800
@@ -73,6 +73,8 @@
operator Symbol*() { return _temp; }
};
+template <class T, class N> class CompactHashtable;
+
class SymbolTable : public RehashableHashtable<Symbol*, mtSymbol> {
friend class VMStructs;
friend class ClassFileParser;
@@ -83,11 +85,15 @@
// Set if one bucket is out of balance due to hash algorithm deficiency
static bool _needs_rehashing;
+ static bool _lookup_shared_first;
// For statistics
static int _symbols_removed;
static int _symbols_counted;
+ // shared symbol table.
+ static CompactHashtable<Symbol*, char> _shared_table;
+
Symbol* allocate_symbol(const u1* name, int len, bool c_heap, TRAPS); // Assumes no characters larger than 0x7F
// Adding elements
@@ -106,6 +112,8 @@
add(loader_data, cp, names_count, name, lengths, cp_indices, hashValues, THREAD);
}
+ static Symbol* lookup_shared(const char* name, int len, unsigned int hash);
+ Symbol* lookup_dynamic(int index, const char* name, int len, unsigned int hash);
Symbol* lookup(int index, const char* name, int len, unsigned int hash);
SymbolTable()
@@ -144,20 +152,6 @@
initialize_symbols(symbol_alloc_arena_size);
}
- static void create_table(HashtableBucket<mtSymbol>* t, int length,
- int number_of_entries) {
- assert(_the_table == NULL, "One symbol table allowed.");
-
- // If CDS archive used a different symbol table size, use that size instead
- // which is better than giving an error.
- SymbolTableSize = length/bucket_size();
-
- _the_table = new SymbolTable(t, number_of_entries);
- // if CDS give symbol table a default arena size since most symbols
- // are already allocated in the shared misc section.
- initialize_symbols();
- }
-
static unsigned int hash_symbol(const char* s, int len);
static Symbol* lookup(const char* name, int len, TRAPS);
@@ -230,18 +224,12 @@
// Debugging
static void verify();
- static void dump(outputStream* st);
+ static void dump(outputStream* st, bool verbose=false);
+ static void read(const char* filename, TRAPS);
// Sharing
- static void copy_buckets(char** top, char*end) {
- the_table()->Hashtable<Symbol*, mtSymbol>::copy_buckets(top, end);
- }
- static void copy_table(char** top, char*end) {
- the_table()->Hashtable<Symbol*, mtSymbol>::copy_table(top, end);
- }
- static void reverse(void* boundary = NULL) {
- the_table()->Hashtable<Symbol*, mtSymbol>::reverse(boundary);
- }
+ static bool copy_compact_table(char** top, char* end);
+ static const char* init_shared_table(const char* buffer);
// Rehash the symbol table if it gets out of balance
static void rehash_table();
--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp Tue Dec 23 16:42:48 2014 -0800
+++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp Tue Dec 23 19:04:23 2014 -0800
@@ -48,6 +48,8 @@
ReservedSpace* MetaspaceShared::_shared_rs = NULL;
+MetaspaceSharedStats MetaspaceShared::_stats;
+
bool MetaspaceShared::_link_classes_made_progress;
bool MetaspaceShared::_check_classes_made_progress;
bool MetaspaceShared::_has_error_classes;
@@ -259,7 +261,7 @@
#define SHAREDSPACE_OBJ_TYPES_DO(f) \
METASPACE_OBJ_TYPES_DO(f) \
f(SymbolHashentry) \
- f(SymbolBuckets) \
+ f(SymbolBucket) \
f(Other)
#define SHAREDSPACE_OBJ_TYPE_DECLARE(name) name ## Type,
@@ -315,18 +317,16 @@
int other_bytes = md_all + mc_all;
// Calculate size of data that was not allocated by Metaspace::allocate()
- int symbol_count = _counts[RO][MetaspaceObj::SymbolType];
- int symhash_bytes = symbol_count * sizeof (HashtableEntry<Symbol*, mtSymbol>);
- int symbuck_count = SymbolTable::the_table()->table_size();
- int symbuck_bytes = symbuck_count * sizeof(HashtableBucket<mtSymbol>);
+ MetaspaceSharedStats *stats = MetaspaceShared::stats();
- _counts[RW][SymbolHashentryType] = symbol_count;
- _bytes [RW][SymbolHashentryType] = symhash_bytes;
- other_bytes -= symhash_bytes;
+ // symbols
+ _counts[RW][SymbolHashentryType] = stats->symbol.hashentry_count;
+ _bytes [RW][SymbolHashentryType] = stats->symbol.hashentry_bytes;
+ other_bytes -= stats->symbol.hashentry_bytes;
- _counts[RW][SymbolBucketsType] = symbuck_count;
- _bytes [RW][SymbolBucketsType] = symbuck_bytes;
- other_bytes -= symbuck_bytes;
+ _counts[RW][SymbolBucketType] = stats->symbol.bucket_count;
+ _bytes [RW][SymbolBucketType] = stats->symbol.bucket_bytes;
+ other_bytes -= stats->symbol.bucket_bytes;
// TODO: count things like dictionary, vtable, etc
_bytes[RW][OtherType] = other_bytes;
@@ -424,6 +424,13 @@
VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; }
void doit(); // outline because gdb sucks
+
+private:
+ void handle_misc_data_space_failure(bool success) {
+ if (!success) {
+ report_out_of_shared_space(SharedMiscData);
+ }
+ }
}; // class VM_PopulateDumpSharedSpace
@@ -517,9 +524,8 @@
// buckets first [read-write], then copy the linked lists of entries
// [read-only].
- SymbolTable::reverse(md_top);
NOT_PRODUCT(SymbolTable::verify());
- SymbolTable::copy_buckets(&md_top, md_end);
+ handle_misc_data_space_failure(SymbolTable::copy_compact_table(&md_top, md_end));
SystemDictionary::reverse();
SystemDictionary::copy_buckets(&md_top, md_end);
@@ -528,7 +534,6 @@
ClassLoader::copy_package_info_buckets(&md_top, md_end);
ClassLoader::verify();
- SymbolTable::copy_table(&md_top, md_end);
SystemDictionary::copy_table(&md_top, md_end);
ClassLoader::verify();
ClassLoader::copy_package_info_table(&md_top, md_end);
@@ -1000,17 +1005,12 @@
buffer += sizeof(intptr_t);
buffer += vtable_size;
- // Create the symbol table using the bucket array at this spot in the
- // misc data space. Since the symbol table is often modified, this
- // region (of mapped pages) will be copy-on-write.
+ // Create the shared symbol table using the bucket array at this spot in the
+ // misc data space. (Todo: move this to read-only space. Currently
+ // this is mapped copy-on-write but will never be written into).
- int symbolTableLen = *(intptr_t*)buffer;
- buffer += sizeof(intptr_t);
- int number_of_entries = *(intptr_t*)buffer;
- buffer += sizeof(intptr_t);
- SymbolTable::create_table((HashtableBucket<mtSymbol>*)buffer, symbolTableLen,
- number_of_entries);
- buffer += symbolTableLen;
+ buffer = (char*)SymbolTable::init_shared_table(buffer);
+ SymbolTable::create_table();
// Create the shared dictionary using the bucket array at this spot in
// the misc data space. Since the shared dictionary table is never
@@ -1019,7 +1019,7 @@
int sharedDictionaryLen = *(intptr_t*)buffer;
buffer += sizeof(intptr_t);
- number_of_entries = *(intptr_t*)buffer;
+ int number_of_entries = *(intptr_t*)buffer;
buffer += sizeof(intptr_t);
SystemDictionary::set_shared_dictionary((HashtableBucket<mtClass>*)buffer,
sharedDictionaryLen,
@@ -1041,18 +1041,10 @@
ClassLoader::verify();
// The following data in the shared misc data region are the linked
- // list elements (HashtableEntry objects) for the symbol table, string
- // table, and shared dictionary. The heap objects referred to by the
- // symbol table, string table, and shared dictionary are permanent and
- // unmovable. Since new entries added to the string and symbol tables
- // are always added at the beginning of the linked lists, THESE LINKED
- // LIST ELEMENTS ARE READ-ONLY.
+ // list elements (HashtableEntry objects) for the shared dictionary
+ // and package info table.
- int len = *(intptr_t*)buffer; // skip over symbol table entries
- buffer += sizeof(intptr_t);
- buffer += len;
-
- len = *(intptr_t*)buffer; // skip over shared dictionary entries
+ int len = *(intptr_t*)buffer; // skip over shared dictionary entries
buffer += sizeof(intptr_t);
buffer += len;
--- a/hotspot/src/share/vm/memory/metaspaceShared.hpp Tue Dec 23 16:42:48 2014 -0800
+++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp Tue Dec 23 19:04:23 2014 -0800
@@ -24,6 +24,7 @@
#ifndef SHARE_VM_MEMORY_METASPACE_SHARED_HPP
#define SHARE_VM_MEMORY_METASPACE_SHARED_HPP
+#include "classfile/compactHashtable.hpp"
#include "memory/allocation.hpp"
#include "memory/memRegion.hpp"
#include "runtime/virtualspace.hpp"
@@ -45,12 +46,21 @@
class FileMapInfo;
+class MetaspaceSharedStats VALUE_OBJ_CLASS_SPEC {
+public:
+ MetaspaceSharedStats() {
+ memset(this, 0, sizeof(*this));
+ }
+ CompactHashtableStats symbol;
+};
+
// Class Data Sharing Support
class MetaspaceShared : AllStatic {
// CDS support
static ReservedSpace* _shared_rs;
static int _max_alignment;
+ static MetaspaceSharedStats _stats;
static bool _link_classes_made_progress;
static bool _check_classes_made_progress;
static bool _has_error_classes;
@@ -123,6 +133,10 @@
char** mc_top, char* mc_end);
static void serialize(SerializeClosure* sc);
+ static MetaspaceSharedStats* stats() {
+ return &_stats;
+ }
+
// JVM/TI RedefineClasses() support:
// Remap the shared readonly space to shared readwrite, private if
// sharing is enabled. Simply returns true if sharing is not enabled
--- a/hotspot/src/share/vm/memory/universe.cpp Tue Dec 23 16:42:48 2014 -0800
+++ b/hotspot/src/share/vm/memory/universe.cpp Tue Dec 23 19:04:23 2014 -0800
@@ -1176,9 +1176,7 @@
MemoryService::set_universe_heap(Universe::_collectedHeap);
#if INCLUDE_CDS
- if (UseSharedSpaces) {
- SharedClassUtil::initialize(CHECK_false);
- }
+ SharedClassUtil::initialize(CHECK_false);
#endif
return true;
}
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp Tue Dec 23 16:42:48 2014 -0800
+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Tue Dec 23 19:04:23 2014 -0800
@@ -3543,11 +3543,12 @@
("purge: %s(%s): prev method @%d in version @%d is alive",
method->name()->as_C_string(),
method->signature()->as_C_string(), j, version));
+#ifdef ASSERT
if (method->method_data() != NULL) {
- // Clean out any weak method links for running methods
- // (also should include not EMCP methods)
- method->method_data()->clean_weak_method_links();
+ // Verify MethodData for running methods don't refer to old methods.
+ method->method_data()->verify_clean_weak_method_links();
}
+#endif // ASSERT
}
}
}
@@ -3561,15 +3562,17 @@
deleted_count));
}
- // Clean MethodData of this class's methods so they don't refer to
+#ifdef ASSERT
+ // Verify clean MethodData for this class's methods, e.g. they don't refer to
// old methods that are no longer running.
Array<Method*>* methods = ik->methods();
int num_methods = methods->length();
- for (int index2 = 0; index2 < num_methods; ++index2) {
- if (methods->at(index2)->method_data() != NULL) {
- methods->at(index2)->method_data()->clean_weak_method_links();
+ for (int index = 0; index < num_methods; ++index) {
+ if (methods->at(index)->method_data() != NULL) {
+ methods->at(index)->method_data()->verify_clean_weak_method_links();
}
}
+#endif // ASSERT
}
void InstanceKlass::mark_newly_obsolete_methods(Array<Method*>* old_methods,
--- a/hotspot/src/share/vm/oops/methodData.cpp Tue Dec 23 16:42:48 2014 -0800
+++ b/hotspot/src/share/vm/oops/methodData.cpp Tue Dec 23 19:04:23 2014 -0800
@@ -1283,6 +1283,11 @@
DataLayout::compute_size_in_bytes(SpeculativeTrapData::static_cell_count()),
"code needs to be adjusted");
+ // Do not create one of these if method has been redefined.
+ if (m != NULL && m->is_old()) {
+ return NULL;
+ }
+
DataLayout* dp = extra_data_base();
DataLayout* end = args_data_limit();
@@ -1554,9 +1559,7 @@
class CleanExtraDataMethodClosure : public CleanExtraDataClosure {
public:
CleanExtraDataMethodClosure() {}
- bool is_live(Method* m) {
- return !m->is_old() || m->on_stack();
- }
+ bool is_live(Method* m) { return !m->is_old(); }
};
@@ -1658,3 +1661,16 @@
clean_extra_data(&cl);
verify_extra_data_clean(&cl);
}
+
+#ifdef ASSERT
+void MethodData::verify_clean_weak_method_links() {
+ for (ProfileData* data = first_data();
+ is_valid(data);
+ data = next_data(data)) {
+ data->verify_clean_weak_method_links();
+ }
+
+ CleanExtraDataMethodClosure cl;
+ verify_extra_data_clean(&cl);
+}
+#endif // ASSERT
--- a/hotspot/src/share/vm/oops/methodData.hpp Tue Dec 23 16:42:48 2014 -0800
+++ b/hotspot/src/share/vm/oops/methodData.hpp Tue Dec 23 19:04:23 2014 -0800
@@ -254,6 +254,7 @@
// Redefinition support
void clean_weak_method_links();
+ DEBUG_ONLY(void verify_clean_weak_method_links();)
};
@@ -511,6 +512,7 @@
// Redefinition support
virtual void clean_weak_method_links() {}
+ DEBUG_ONLY(virtual void verify_clean_weak_method_links() {})
// CI translation: ProfileData can represent both MethodDataOop data
// as well as CIMethodData data. This function is provided for translating
@@ -1971,6 +1973,7 @@
}
void set_method(Method* m) {
+ assert(!m->is_old(), "cannot add old methods");
set_intptr_at(speculative_trap_method, (intptr_t)m);
}
@@ -2480,6 +2483,7 @@
void clean_method_data(BoolObjectClosure* is_alive);
void clean_weak_method_links();
+ DEBUG_ONLY(void verify_clean_weak_method_links();)
Mutex* extra_data_lock() { return &_extra_data_lock; }
};
--- a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp Tue Dec 23 16:42:48 2014 -0800
+++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.cpp Tue Dec 23 19:04:23 2014 -0800
@@ -148,6 +148,10 @@
_scratch_classes[i] = NULL;
}
+ // Clean out MethodData pointing to old Method*
+ MethodDataCleaner clean_weak_method_links;
+ ClassLoaderDataGraph::classes_do(&clean_weak_method_links);
+
// Disable any dependent concurrent compilations
SystemDictionary::notice_modification();
@@ -155,8 +159,8 @@
// See jvmtiExport.hpp for detailed explanation.
JvmtiExport::set_has_redefined_a_class();
-// check_class() is optionally called for product bits, but is
-// always called for non-product bits.
+ // check_class() is optionally called for product bits, but is
+ // always called for non-product bits.
#ifdef PRODUCT
if (RC_TRACE_ENABLED(0x00004000)) {
#endif
@@ -3445,6 +3449,22 @@
}
}
+// Clean method data for this class
+void VM_RedefineClasses::MethodDataCleaner::do_klass(Klass* k) {
+ if (k->oop_is_instance()) {
+ InstanceKlass *ik = InstanceKlass::cast(k);
+ // Clean MethodData of this class's methods so they don't refer to
+ // old methods that are no longer running.
+ Array<Method*>* methods = ik->methods();
+ int num_methods = methods->length();
+ for (int index = 0; index < num_methods; ++index) {
+ if (methods->at(index)->method_data() != NULL) {
+ methods->at(index)->method_data()->clean_weak_method_links();
+ }
+ }
+ }
+}
+
void VM_RedefineClasses::update_jmethod_ids() {
for (int j = 0; j < _matching_methods_length; ++j) {
Method* old_method = _matching_old_methods[j];
--- a/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp Tue Dec 23 16:42:48 2014 -0800
+++ b/hotspot/src/share/vm/prims/jvmtiRedefineClasses.hpp Tue Dec 23 19:04:23 2014 -0800
@@ -511,6 +511,12 @@
void do_klass(Klass* k);
};
+ // Clean MethodData out
+ class MethodDataCleaner : public KlassClosure {
+ public:
+ MethodDataCleaner() {}
+ void do_klass(Klass* k);
+ };
public:
VM_RedefineClasses(jint class_count,
const jvmtiClassDefinition *class_defs,
--- a/hotspot/src/share/vm/runtime/globals.hpp Tue Dec 23 16:42:48 2014 -0800
+++ b/hotspot/src/share/vm/runtime/globals.hpp Tue Dec 23 19:04:23 2014 -0800
@@ -3779,6 +3779,9 @@
NOT_LP64(LINUX_ONLY(2*G) NOT_LINUX(0)), \
"Address to allocate shared memory region for class data") \
\
+ product(uintx, SharedSymbolTableBucketSize, 4, \
+ "Average number of symbols per bucket in shared table") \
+ \
diagnostic(bool, IgnoreUnverifiableClassesDuringDump, false, \
"Do not quit -Xshare:dump even if we encounter unverifiable " \
"classes. Just exclude them from the shared dictionary.") \
--- a/hotspot/src/share/vm/runtime/vm_operations.hpp Tue Dec 23 16:42:48 2014 -0800
+++ b/hotspot/src/share/vm/runtime/vm_operations.hpp Tue Dec 23 19:04:23 2014 -0800
@@ -100,6 +100,7 @@
template(RotateGCLog) \
template(WhiteBoxOperation) \
template(ClassLoaderStatsOperation) \
+ template(DumpHashtable) \
template(MarkActiveNMethods) \
template(PrintCompileQueue) \
template(PrintCodeList) \
--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp Tue Dec 23 16:42:48 2014 -0800
+++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp Tue Dec 23 19:04:23 2014 -0800
@@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "classfile/classLoaderStats.hpp"
+#include "classfile/compactHashtable.hpp"
#include "gc_implementation/shared/vmGCOperations.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/os.hpp"
@@ -56,6 +57,8 @@
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<HeapDumpDCmd>(DCmd_Source_Internal | DCmd_Source_AttachAPI, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassHistogramDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassStatsDCmd>(full_export, true, false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<SymboltableDCmd>(full_export, true, false));
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<StringtableDCmd>(full_export, true, false));
#endif // INCLUDE_SERVICES
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ThreadDumpDCmd>(full_export, true, false));
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<RotateGCLogDCmd>(full_export, true, false));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/SharedArchiveFile/DumpSymbolAndStringTable.java Tue Dec 23 19:04:23 2014 -0800
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+/*
+ * @test
+ * @bug 8059510
+ * @summary Test jcmd VM.symboltable and VM.stringtable options
+ * @library /testlibrary
+ * @run main/othervm -XX:+UnlockDiagnosticVMOptions DumpSymbolAndStringTable
+ */
+
+import com.oracle.java.testlibrary.*;
+
+public class DumpSymbolAndStringTable {
+ public static void main(String[] args) throws Exception {
+ // Grab my own PID
+ String pid = Integer.toString(ProcessTools.getProcessId());
+
+ ProcessBuilder pb = new ProcessBuilder();
+ pb.command(new String[] {JDKToolFinder.getJDKTool("jcmd"), pid, "VM.symboltable", "-verbose"});
+ OutputAnalyzer output = new OutputAnalyzer(pb.start());
+ try {
+ output.shouldContain("24 2: DumpSymbolAndStringTable\n");
+ } catch (RuntimeException e) {
+ output.shouldContain("Unknown diagnostic command");
+ }
+
+ pb.command(new String[] {JDKToolFinder.getJDKTool("jcmd"), pid, "VM.stringtable", "-verbose"});
+ output = new OutputAnalyzer(pb.start());
+ try {
+ output.shouldContain("16: java.lang.String\n");
+ } catch (RuntimeException e) {
+ output.shouldContain("Unknown diagnostic command");
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/runtime/SharedArchiveFile/SharedSymbolTableBucketSize.java Tue Dec 23 19:04:23 2014 -0800
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2014, 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.
+ */
+
+/*
+ * @test
+ * @bug 8059510
+ * @summary Test SharedSymbolTableBucketSize option
+ * @library /testlibrary
+ */
+
+import com.oracle.java.testlibrary.*;
+
+public class SharedSymbolTableBucketSize {
+ public static void main(String[] args) throws Exception {
+ int bucket_size = 8;
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+ "-Xshare:dump", "-XX:+PrintSharedSpaces",
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:SharedArchiveFile=./sample.jsa",
+ "-XX:SharedSymbolTableBucketSize=" + Integer.valueOf(bucket_size));
+ OutputAnalyzer output = new OutputAnalyzer(pb.start());
+ output.shouldContain("Loading classes to share");
+ output.shouldHaveExitValue(0);
+
+ String s = output.firstMatch("Average bucket size : .*");
+ Float f = Float.parseFloat(s.substring(25));
+ int size = Math.round(f);
+ if (size != bucket_size) {
+ throw new Exception("FAILED: incorrect bucket size " + size +
+ ", expect " + bucket_size);
+ }
+
+
+ // Invalid SharedSymbolTableBucketSize input
+ String input[] = {"-XX:SharedSymbolTableBucketSize=-1",
+ "-XX:SharedSymbolTableBucketSize=2.5"};
+ for (int i = 0; i < input.length; i++) {
+ pb = ProcessTools.createJavaProcessBuilder(
+ "-Xshare:dump", "-XX:+PrintSharedSpaces",
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:SharedArchiveFile=./sample.jsa",
+ input[i]);
+ output = new OutputAnalyzer(pb.start());
+ output.shouldContain("Improperly specified VM option");
+ }
+ }
+}