8015086: add interned strings to the shared archive.
Summary: Support saving interned strings in shared CDS archive.
Reviewed-by: coleenp, iklam, pliden
--- a/hotspot/src/share/vm/classfile/compactHashtable.cpp Fri Jun 12 12:55:32 2015 +0200
+++ b/hotspot/src/share/vm/classfile/compactHashtable.cpp Fri Jun 12 17:29:14 2015 -0400
@@ -32,11 +32,11 @@
//
// The compact hash table writer implementations
//
-CompactHashtableWriter::CompactHashtableWriter(const char* table_name,
+CompactHashtableWriter::CompactHashtableWriter(int table_type,
int num_entries,
CompactHashtableStats* stats) {
assert(DumpSharedSpaces, "dump-time only");
- _table_name = table_name;
+ _type = table_type;
_num_entries = num_entries;
_num_buckets = number_of_buckets(_num_entries);
_buckets = NEW_C_HEAP_ARRAY(Entry*, _num_buckets, mtSymbol);
@@ -99,7 +99,7 @@
NumberSeq* summary) {
int index;
juint* compact_table = p;
- // Find the start of the buckets, skip the compact_bucket_infos table
+ // Compute the start of the buckets, include the compact_bucket_infos table
// and the table end offset.
juint offset = _num_buckets + 1;
*first_bucket = compact_table + offset;
@@ -130,10 +130,17 @@
// 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");
+ uintx base_address = 0;
+ uintx max_delta = 0;
int num_compact_buckets = 0;
+ if (_type == CompactHashtable<Symbol*, char>::_symbol_table) {
+ base_address = uintx(MetaspaceShared::shared_rs()->base());
+ max_delta = uintx(MetaspaceShared::shared_rs()->size());
+ assert(max_delta <= 0x7fffffff, "range check");
+ } else {
+ assert((_type == CompactHashtable<oop, char>::_string_table), "unknown table");
+ assert(UseCompressedOops, "UseCompressedOops is required");
+ }
assert(p != NULL, "sanity");
for (int index = 0; index < _num_buckets; index++) {
@@ -148,12 +155,16 @@
for (Entry* tent = _buckets[index]; tent;
tent = tent->next()) {
if (bucket_type == REGULAR_BUCKET_TYPE) {
- *p++ = juint(tent->hash()); // write symbol hash
+ *p++ = juint(tent->hash()); // write entry hash
}
- uintx deltax = uintx(tent->value()) - base_address;
- assert(deltax < max_delta, "range check");
- juint delta = juint(deltax);
- *p++ = delta; // write symbol offset
+ if (_type == CompactHashtable<Symbol*, char>::_symbol_table) {
+ uintx deltax = uintx(tent->value()) - base_address;
+ assert(deltax < max_delta, "range check");
+ juint delta = juint(deltax);
+ *p++ = delta; // write entry offset
+ } else {
+ *p++ = oopDesc::encode_heap_oop(tent->string());
+ }
count ++;
}
assert(count == _bucket_sizes[index], "sanity");
@@ -174,6 +185,10 @@
uintx base_address = uintx(MetaspaceShared::shared_rs()->base());
+ // Now write the following at the beginning of the table:
+ // base_address (uintx)
+ // num_entries (juint)
+ // num_buckets (juint)
*p++ = high(base_address);
*p++ = low (base_address); // base address
*p++ = _num_entries; // number of entries in the table
@@ -191,7 +206,8 @@
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("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);
@@ -202,12 +218,24 @@
}
}
+const char* CompactHashtableWriter::table_name() {
+ switch (_type) {
+ case CompactHashtable<Symbol*, char>::_symbol_table: return "symbol";
+ case CompactHashtable<oop, char>::_string_table: return "string";
+ default:
+ ;
+ }
+ return "unknown";
+}
+
/////////////////////////////////////////////////////////////
//
// The CompactHashtable implementation
//
-template <class T, class N> const char* CompactHashtable<T, N>::init(const char* buffer) {
+template <class T, class N> const char* CompactHashtable<T, N>::init(
+ CompactHashtableType type, const char* buffer) {
assert(!DumpSharedSpaces, "run-time only");
+ _type = type;
juint*p = (juint*)buffer;
juint upper = *p++;
juint lower = *p++;
@@ -245,8 +273,34 @@
}
}
+template <class T, class N> void CompactHashtable<T, N>::oops_do(OopClosure* f) {
+ assert(!DumpSharedSpaces, "run-time only");
+ assert(_type == _string_table || _bucket_count == 0, "sanity");
+ for (juint i = 0; i < _bucket_count; i ++) {
+ juint bucket_info = _buckets[i];
+ juint bucket_offset = BUCKET_OFFSET(bucket_info);
+ int bucket_type = BUCKET_TYPE(bucket_info);
+ juint* bucket = _buckets + bucket_offset;
+ juint* bucket_end = _buckets;
+
+ narrowOop o;
+ if (bucket_type == COMPACT_BUCKET_TYPE) {
+ o = (narrowOop)bucket[0];
+ f->do_oop(&o);
+ } else {
+ bucket_end += BUCKET_OFFSET(_buckets[i + 1]);
+ while (bucket < bucket_end) {
+ o = (narrowOop)bucket[1];
+ f->do_oop(&o);
+ bucket += 2;
+ }
+ }
+ }
+}
+
// Explicitly instantiate these types
template class CompactHashtable<Symbol*, char>;
+template class CompactHashtable<oop, char>;
#ifndef O_BINARY // if defined (Win32) use binary files.
#define O_BINARY 0 // otherwise do nothing.
@@ -273,6 +327,8 @@
_p = _base;
_end = _base + st.st_size;
_filename = filename;
+ _prefix_type = Unknown;
+ _line_no = 1;
}
HashtableTextDump::~HashtableTextDump() {
@@ -286,9 +342,9 @@
vm_exit_during_initialization(err, msg);
}
-void HashtableTextDump::corrupted(const char *p) {
+void HashtableTextDump::corrupted(const char *p, const char* msg) {
char info[60];
- sprintf(info, "corrupted at pos %d", (int)(p - _base));
+ sprintf(info, "%s. Corrupted at line %d (file pos %d)", msg, _line_no, (int)(p - _base));
quit(info, _filename);
}
@@ -298,8 +354,9 @@
} else if (_p[0] == '\n') {
_p += 1;
} else {
- corrupted(_p);
+ corrupted(_p, "Unexpected character");
}
+ _line_no ++;
return true;
}
@@ -328,26 +385,60 @@
skip_newline();
}
+void HashtableTextDump::scan_prefix_type() {
+ _p ++;
+ if (strncmp(_p, "SECTION: String", 15) == 0) {
+ _p += 15;
+ _prefix_type = StringPrefix;
+ } else if (strncmp(_p, "SECTION: Symbol", 15) == 0) {
+ _p += 15;
+ _prefix_type = SymbolPrefix;
+ } else {
+ _prefix_type = Unknown;
+ }
+ skip_newline();
+}
-int HashtableTextDump::scan_prefix() {
+int HashtableTextDump::scan_prefix(int* utf8_length) {
+ if (*_p == '@') {
+ scan_prefix_type();
+ }
+
+ switch (_prefix_type) {
+ case SymbolPrefix:
+ *utf8_length = scan_symbol_prefix(); break;
+ case StringPrefix:
+ *utf8_length = scan_string_prefix(); break;
+ default:
+ tty->print_cr("Shared input data type: Unknown.");
+ corrupted(_p, "Unknown data type");
+ }
+
+ return _prefix_type;
+}
+
+int HashtableTextDump::scan_string_prefix() {
// Expect /[0-9]+: /
- int utf8_length = get_num(':');
+ int utf8_length;
+ get_num(':', &utf8_length);
if (*_p != ' ') {
- corrupted(_p);
+ corrupted(_p, "Wrong prefix format for string");
}
_p++;
return utf8_length;
}
-int HashtableTextDump::scan_prefix2() {
+int HashtableTextDump::scan_symbol_prefix() {
// Expect /[0-9]+ (-|)[0-9]+: /
- int utf8_length = get_num(' ');
- if (*_p == '-') {
- _p++;
+ int utf8_length;
+ get_num(' ', &utf8_length);
+ if (*_p == '-') {
+ _p++;
}
- (void)get_num(':');
+ int ref_num;
+ (void)get_num(':', &ref_num);
if (*_p != ' ') {
- corrupted(_p);
+ corrupted(_p, "Wrong prefix format for symbol");
}
_p++;
return utf8_length;
@@ -408,7 +499,7 @@
case 'r': *to++ = '\r'; break;
case '\\': *to++ = '\\'; break;
default:
- ShouldNotReachHere();
+ corrupted(_p, "Unsupported character");
}
}
}
--- a/hotspot/src/share/vm/classfile/compactHashtable.hpp Fri Jun 12 12:55:32 2015 +0200
+++ b/hotspot/src/share/vm/classfile/compactHashtable.hpp Fri Jun 12 17:29:14 2015 -0400
@@ -28,6 +28,7 @@
#include "classfile/stringTable.hpp"
#include "classfile/symbolTable.hpp"
#include "memory/allocation.inline.hpp"
+#include "oops/oop.inline.hpp"
#include "oops/symbol.hpp"
#include "services/diagnosticCommand.hpp"
#include "utilities/hashtable.hpp"
@@ -49,7 +50,7 @@
// 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
+// symbol/string 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.
@@ -57,14 +58,14 @@
// 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.
+// We use a simple hash function (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
+// than one entry, both hash and entry offset are written to the
+// table. For buckets with only one entry, only the entry 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.
@@ -78,6 +79,7 @@
public:
Entry(unsigned int hash, Symbol *symbol) : _next(NULL), _hash(hash), _literal(symbol) {}
+ Entry(unsigned int hash, oop string) : _next(NULL), _hash(hash), _literal(string) {}
void *value() {
return _literal;
@@ -85,6 +87,9 @@
Symbol *symbol() {
return (Symbol*)_literal;
}
+ oop string() {
+ return (oop)_literal;
+ }
unsigned int hash() {
return _hash;
}
@@ -95,7 +100,7 @@
private:
static int number_of_buckets(int num_entries);
- const char* _table_name;
+ int _type;
int _num_entries;
int _num_buckets;
juint* _bucket_sizes;
@@ -105,7 +110,7 @@
public:
// This is called at dump-time only
- CompactHashtableWriter(const char* table_name, int num_entries, CompactHashtableStats* stats);
+ CompactHashtableWriter(int table_type, int num_entries, CompactHashtableStats* stats);
~CompactHashtableWriter();
int get_required_bytes() {
@@ -116,6 +121,10 @@
add(hash, new Entry(hash, symbol));
}
+ void add(unsigned int hash, oop string) {
+ add(hash, new Entry(hash, string));
+ }
+
private:
void add(unsigned int hash, Entry* entry);
juint* dump_table(juint* p, juint** first_bucket, NumberSeq* summary);
@@ -123,6 +132,7 @@
public:
void dump(char** top, char* end);
+ const char* table_name();
};
#define REGULAR_BUCKET_TYPE 0
@@ -136,23 +146,23 @@
/////////////////////////////////////////////////////////////////////////////
//
-// CompactHashtable is used to stored the CDS archive's symbol table. Used
+// CompactHashtable is used to stored the CDS archive's symbol/string 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:
+// Layout of compact table in the shared archive:
//
// uintx base_address;
-// juint num_symbols;
+// juint num_entries;
// juint num_buckets;
// juint bucket_infos[num_buckets+1]; // bit[31,30]: type; bit[29-0]: offset
// juint table[]
//
// -----------------------------------
-// | base_address | num_symbols |
+// | base_address | num_entries |
// |---------------------------------|
// | num_buckets | bucket_info0 |
// |---------------------------------|
@@ -177,9 +187,13 @@
// 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 normal buckets, each entry is 8 bytes in the table[]:
+// juint hash; /* symbol/string hash */
+// union {
+// juint offset; /* Symbol* sym = (Symbol*)(base_address + offset) */
+// narrowOop str; /* String narrowOop encoding */
+// }
+//
//
// For compact buckets, each entry has only the 4-byte 'offset' in the table[].
//
@@ -189,19 +203,41 @@
//
template <class T, class N> class CompactHashtable VALUE_OBJ_CLASS_SPEC {
friend class VMStructs;
+
+ public:
+ enum CompactHashtableType {
+ _symbol_table = 0,
+ _string_table = 1
+ };
+
+private:
+ CompactHashtableType _type;
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;
+ inline Symbol* lookup_entry(CompactHashtable<Symbol*, char>* const t,
+ juint* addr, const char* name, int len) {
+ Symbol* sym = (Symbol*)((void*)(_base_address + *addr));
+ if (sym->equals(name, len)) {
+ assert(sym->refcount() == -1, "must be shared");
+ return sym;
}
+
+ return NULL;
+ }
+
+ inline oop lookup_entry(CompactHashtable<oop, char>* const t,
+ juint* addr, const char* name, int len) {
+ narrowOop obj = (narrowOop)(*addr);
+ oop string = oopDesc::decode_heap_oop(obj);
+ if (java_lang_String::equals(string, (jchar*)name, len)) {
+ return string;
+ }
+
+ return NULL;
}
public:
@@ -211,7 +247,14 @@
_table_end_offset = 0;
_buckets = 0;
}
- const char* init(const char *buffer);
+ const char* init(CompactHashtableType type, const char *buffer);
+
+ void reset() {
+ _entry_count = 0;
+ _bucket_count = 0;
+ _table_end_offset = 0;
+ _buckets = 0;
+ }
// Lookup an entry from the compact table
inline T lookup(const N* name, unsigned int hash, int len) {
@@ -225,23 +268,22 @@
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;
+ // the compact bucket has one entry with entry offset only
+ T res = lookup_entry(this, &bucket[0], name, len);
+ if (res != NULL) {
+ return res;
}
} else {
// This is a regular bucket, which has more than one
- // entries. Each entry is a pair of symbol (hash, offset).
+ // entries. Each entry is a pair of entry (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;
+ T res = lookup_entry(this, &bucket[1], name, len);
+ if (res != NULL) {
+ return res;
}
}
bucket += 2;
@@ -253,12 +295,15 @@
// iterate over symbols
void symbols_do(SymbolClosure *cl);
+
+ // iterate over strings
+ void oops_do(OopClosure* f);
};
////////////////////////////////////////////////////////////////////////
//
// Read/Write the contents of a hashtable textual dump (created by
-// SymbolTable::dump).
+// SymbolTable::dump and StringTable::dump).
// Because the dump file may be big (hundred of MB in extreme cases),
// we use mmap for fast access when reading it.
//
@@ -269,21 +314,29 @@
const char* _end;
const char* _filename;
size_t _size;
+ int _prefix_type;
+ int _line_no;
public:
HashtableTextDump(const char* filename);
~HashtableTextDump();
+ enum {
+ SymbolPrefix = 1 << 0,
+ StringPrefix = 1 << 1,
+ Unknown = 1 << 2
+ };
+
void quit(const char* err, const char* msg);
inline int remain() {
return (int)(_end - _p);
}
- void corrupted(const char *p);
+ void corrupted(const char *p, const char *msg);
inline void corrupted_if(bool cond) {
if (cond) {
- corrupted(_p);
+ corrupted(_p, NULL);
}
}
@@ -292,7 +345,7 @@
void skip_past(char c);
void check_version(const char* ver);
- inline int get_num(char delim) {
+ inline bool get_num(char delim, int *utf8_length) {
const char* p = _p;
const char* end = _end;
int num = 0;
@@ -303,18 +356,22 @@
num = num * 10 + (c - '0');
} else if (c == delim) {
_p = p;
- return num;
+ *utf8_length = num;
+ return true;
} else {
- corrupted(p-1);
+ // Not [0-9], not 'delim'
+ return false;
}
}
- corrupted(_end);
+ corrupted(_end, "Incorrect format");
ShouldNotReachHere();
- return 0;
+ return false;
}
- int scan_prefix();
- int scan_prefix2();
+ void scan_prefix_type();
+ int scan_prefix(int* utf8_length);
+ int scan_string_prefix();
+ int scan_symbol_prefix();
jchar unescape(const char* from, const char* end, int count);
void get_utf8(char* utf8_buffer, int utf8_length);
--- a/hotspot/src/share/vm/classfile/javaClasses.hpp Fri Jun 12 12:55:32 2015 +0200
+++ b/hotspot/src/share/vm/classfile/javaClasses.hpp Fri Jun 12 17:29:14 2015 -0400
@@ -118,6 +118,10 @@
return hash_offset;
}
+ static void set_value_raw(oop string, typeArrayOop buffer) {
+ assert(initialized, "Must be initialized");
+ string->obj_field_put_raw(value_offset, buffer);
+ }
static void set_value(oop string, typeArrayOop buffer) {
assert(initialized && (value_offset > 0), "Must be initialized");
string->obj_field_put(value_offset, (oop)buffer);
@@ -210,6 +214,7 @@
// Debugging
static void print(oop java_string, outputStream* st);
friend class JavaClasses;
+ friend class StringTable;
};
--- a/hotspot/src/share/vm/classfile/stringTable.cpp Fri Jun 12 12:55:32 2015 +0200
+++ b/hotspot/src/share/vm/classfile/stringTable.cpp Fri Jun 12 17:29:14 2015 -0400
@@ -38,6 +38,7 @@
#include "utilities/hashtable.inline.hpp"
#include "utilities/macros.hpp"
#if INCLUDE_ALL_GCS
+#include "gc/g1/g1CollectedHeap.hpp"
#include "gc/g1/g1SATBCardTableModRefBS.hpp"
#include "gc/g1/g1StringDedup.hpp"
#endif
@@ -87,19 +88,28 @@
// --------------------------------------------------------------------------
StringTable* StringTable::_the_table = NULL;
-
+bool StringTable::_ignore_shared_strings = false;
bool StringTable::_needs_rehashing = false;
volatile int StringTable::_parallel_claimed_idx = 0;
+CompactHashtable<oop, char> StringTable::_shared_table;
+
// Pick hashing algorithm
unsigned int StringTable::hash_string(const jchar* s, int len) {
return use_alternate_hashcode() ? AltHashing::murmur3_32(seed(), s, len) :
java_lang_String::hash_code(s, len);
}
-oop StringTable::lookup(int index, jchar* name,
- int len, unsigned int hash) {
+oop StringTable::lookup_shared(jchar* name, int len) {
+ // java_lang_String::hash_code() was used to compute hash values in the shared table. Don't
+ // use the hash value from StringTable::hash_string() as it might use alternate hashcode.
+ return _shared_table.lookup((const char*)name,
+ java_lang_String::hash_code(name, len), len);
+}
+
+oop StringTable::lookup_in_main_table(int index, jchar* name,
+ int len, unsigned int hash) {
int count = 0;
for (HashtableEntry<oop, mtSymbol>* l = bucket(index); l != NULL; l = l->next()) {
count++;
@@ -140,7 +150,8 @@
// Since look-up was done lock-free, we need to check if another
// thread beat us in the race to insert the symbol.
- oop test = lookup(index, name, len, hashValue); // calls lookup(u1*, int)
+ // No need to lookup the shared table from here since the caller (intern()) already did
+ oop test = lookup_in_main_table(index, name, len, hashValue); // calls lookup(u1*, int)
if (test != NULL) {
// Entry already added
return test;
@@ -172,9 +183,14 @@
}
oop StringTable::lookup(jchar* name, int len) {
+ oop string = lookup_shared(name, len);
+ if (string != NULL) {
+ return string;
+ }
+
unsigned int hash = hash_string(name, len);
int index = the_table()->hash_to_index(hash);
- oop string = the_table()->lookup(index, name, len, hash);
+ string = the_table()->lookup_in_main_table(index, name, len, hash);
ensure_string_alive(string);
@@ -184,9 +200,14 @@
oop StringTable::intern(Handle string_or_null, jchar* name,
int len, TRAPS) {
+ oop found_string = lookup_shared(name, len);
+ if (found_string != NULL) {
+ return found_string;
+ }
+
unsigned int hashValue = hash_string(name, len);
int index = the_table()->hash_to_index(hashValue);
- oop found_string = the_table()->lookup(index, name, len, hashValue);
+ found_string = the_table()->lookup_in_main_table(index, name, len, hashValue);
// Found
if (found_string != NULL) {
@@ -611,3 +632,131 @@
return 0;
}
}
+
+// Sharing
+bool StringTable::copy_shared_string(GrowableArray<MemRegion> *string_space,
+ CompactHashtableWriter* ch_table) {
+#if INCLUDE_CDS && INCLUDE_ALL_GCS && defined(_LP64) && !defined(_WINDOWS)
+ assert(UseG1GC, "Only support G1 GC");
+ assert(UseCompressedOops && UseCompressedClassPointers,
+ "Only support UseCompressedOops and UseCompressedClassPointers enabled");
+
+ Thread* THREAD = Thread::current();
+ G1CollectedHeap::heap()->begin_archive_alloc_range();
+ for (int i = 0; i < the_table()->table_size(); ++i) {
+ HashtableEntry<oop, mtSymbol>* bucket = the_table()->bucket(i);
+ for ( ; bucket != NULL; bucket = bucket->next()) {
+ oop s = bucket->literal();
+ unsigned int hash = java_lang_String::hash_code(s);
+ if (hash == 0) {
+ continue;
+ }
+
+ // allocate the new 'value' array first
+ typeArrayOop v = java_lang_String::value(s);
+ int v_len = v->size();
+ typeArrayOop new_v;
+ if (G1CollectedHeap::heap()->is_archive_alloc_too_large(v_len)) {
+ continue; // skip the current String. The 'value' array is too large to handle
+ } else {
+ new_v = (typeArrayOop)G1CollectedHeap::heap()->archive_mem_allocate(v_len);
+ if (new_v == NULL) {
+ return false; // allocation failed
+ }
+ }
+ // now allocate the new String object
+ int s_len = s->size();
+ oop new_s = (oop)G1CollectedHeap::heap()->archive_mem_allocate(s_len);
+ if (new_s == NULL) {
+ return false;
+ }
+
+ s->identity_hash();
+ v->identity_hash();
+
+ // copy the objects' data
+ Copy::aligned_disjoint_words((HeapWord*)s, (HeapWord*)new_s, s_len);
+ Copy::aligned_disjoint_words((HeapWord*)v, (HeapWord*)new_v, v_len);
+
+ // adjust the pointer to the 'value' field in the new String oop. Also pre-compute and set the
+ // 'hash' field. That avoids "write" to the shared strings at runtime by the deduplication process.
+ java_lang_String::set_value_raw(new_s, new_v);
+ if (java_lang_String::hash(new_s) == 0) {
+ java_lang_String::set_hash(new_s, hash);
+ }
+
+ // add to the compact table
+ ch_table->add(hash, new_s);
+ }
+ }
+
+ G1CollectedHeap::heap()->end_archive_alloc_range(string_space, os::vm_allocation_granularity());
+ assert(string_space->length() <= 2, "sanity");
+#endif
+ return true;
+}
+
+bool StringTable::copy_compact_table(char** top, char *end, GrowableArray<MemRegion> *string_space,
+ size_t* space_size) {
+#if INCLUDE_CDS && defined(_LP64) && !defined(_WINDOWS)
+ if (!(UseG1GC && UseCompressedOops && UseCompressedClassPointers)) {
+ if (PrintSharedSpaces) {
+ tty->print_cr("Shared strings are excluded from the archive as UseG1GC, "
+ "UseCompressedOops and UseCompressedClassPointers are required.");
+ }
+ return true;
+ }
+
+ CompactHashtableWriter ch_table(CompactHashtable<oop, char>::_string_table,
+ the_table()->number_of_entries(),
+ &MetaspaceShared::stats()->string);
+
+ // Copy the interned strings into the "string space" within the java heap
+ if (!copy_shared_string(string_space, &ch_table)) {
+ return false;
+ }
+
+ for (int i = 0; i < string_space->length(); i++) {
+ *space_size += string_space->at(i).byte_size();
+ }
+
+ // Now dump the compact table
+ if (*top + ch_table.get_required_bytes() > end) {
+ // not enough space left
+ return false;
+ }
+ ch_table.dump(top, end);
+ *top = (char*)align_pointer_up(*top, sizeof(void*));
+
+#endif
+ return true;
+}
+
+void StringTable::shared_oops_do(OopClosure* f) {
+#if INCLUDE_CDS && defined(_LP64) && !defined(_WINDOWS)
+ _shared_table.oops_do(f);
+#endif
+}
+
+const char* StringTable::init_shared_table(FileMapInfo *mapinfo, char *buffer) {
+#if INCLUDE_CDS && defined(_LP64) && !defined(_WINDOWS)
+ if (mapinfo->space_capacity(MetaspaceShared::first_string) == 0) {
+ // no shared string data
+ return buffer;
+ }
+
+ // initialize the shared table
+ juint *p = (juint*)buffer;
+ const char* end = _shared_table.init(
+ CompactHashtable<oop, char>::_string_table, (char*)p);
+ const char* aligned_end = (const char*)align_pointer_up(end, sizeof(void*));
+
+ if (_ignore_shared_strings) {
+ _shared_table.reset();
+ }
+
+ return aligned_end;
+#endif
+
+ return buffer;
+}
--- a/hotspot/src/share/vm/classfile/stringTable.hpp Fri Jun 12 12:55:32 2015 +0200
+++ b/hotspot/src/share/vm/classfile/stringTable.hpp Fri Jun 12 17:29:14 2015 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2015, 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
@@ -28,6 +28,10 @@
#include "memory/allocation.inline.hpp"
#include "utilities/hashtable.hpp"
+template <class T, class N> class CompactHashtable;
+class CompactHashtableWriter;
+class FileMapInfo;
+
class StringTable : public RehashableHashtable<oop, mtSymbol> {
friend class VMStructs;
friend class Symbol;
@@ -36,6 +40,10 @@
// The string table
static StringTable* _the_table;
+ // Shared string table
+ static CompactHashtable<oop, char> _shared_table;
+ static bool _ignore_shared_strings;
+
// Set if one bucket is out of balance due to hash algorithm deficiency
static bool _needs_rehashing;
@@ -46,7 +54,8 @@
oop basic_add(int index, Handle string_or_null, jchar* name, int len,
unsigned int hashValue, TRAPS);
- oop lookup(int index, jchar* chars, int length, unsigned int hashValue);
+ oop lookup_in_main_table(int index, jchar* chars, int length, unsigned int hashValue);
+ static oop lookup_shared(jchar* name, int len);
// Apply the give oop closure to the entries to the buckets
// in the range [start_idx, end_idx).
@@ -141,12 +150,14 @@
static int verify_and_compare_entries();
// Sharing
- static void copy_buckets(char** top, char*end) {
- the_table()->Hashtable<oop, mtSymbol>::copy_buckets(top, end);
- }
- static void copy_table(char** top, char*end) {
- the_table()->Hashtable<oop, mtSymbol>::copy_table(top, end);
- }
+ static void ignore_shared_strings(bool v) { _ignore_shared_strings = v; }
+ static bool shared_string_ignored() { return _ignore_shared_strings; }
+ static void shared_oops_do(OopClosure* f);
+ static bool copy_shared_string(GrowableArray<MemRegion> *string_space,
+ CompactHashtableWriter* ch_table);
+ static bool copy_compact_table(char** top, char* end, GrowableArray<MemRegion> *string_space,
+ size_t* space_size);
+ static const char* init_shared_table(FileMapInfo *mapinfo, char* buffer);
static void reverse() {
the_table()->Hashtable<oop, mtSymbol>::reverse();
}
--- a/hotspot/src/share/vm/classfile/symbolTable.cpp Fri Jun 12 12:55:32 2015 +0200
+++ b/hotspot/src/share/vm/classfile/symbolTable.cpp Fri Jun 12 17:29:14 2015 -0400
@@ -539,7 +539,8 @@
bool SymbolTable::copy_compact_table(char** top, char*end) {
#if INCLUDE_CDS
- CompactHashtableWriter ch_table("symbol", the_table()->number_of_entries(),
+ CompactHashtableWriter ch_table(CompactHashtable<Symbol*, char>::_symbol_table,
+ the_table()->number_of_entries(),
&MetaspaceShared::stats()->symbol);
if (*top + ch_table.get_required_bytes() > end) {
// not enough space left
@@ -556,7 +557,6 @@
}
}
- char* old_top = *top;
ch_table.dump(top, end);
*top = (char*)align_pointer_up(*top, sizeof(void*));
@@ -565,7 +565,8 @@
}
const char* SymbolTable::init_shared_table(const char* buffer) {
- const char* end = _shared_table.init(buffer);
+ const char* end = _shared_table.init(
+ CompactHashtable<Symbol*, char>::_symbol_table, buffer);
return (const char*)align_pointer_up(end, sizeof(void*));
}
--- a/hotspot/src/share/vm/gc/g1/g1StringDedupThread.cpp Fri Jun 12 12:55:32 2015 +0200
+++ b/hotspot/src/share/vm/gc/g1/g1StringDedupThread.cpp Fri Jun 12 17:29:14 2015 -0400
@@ -23,12 +23,14 @@
*/
#include "precompiled.hpp"
+#include "classfile/stringTable.hpp"
#include "gc/g1/g1Log.hpp"
#include "gc/g1/g1StringDedup.hpp"
#include "gc/g1/g1StringDedupQueue.hpp"
#include "gc/g1/g1StringDedupTable.hpp"
#include "gc/g1/g1StringDedupThread.hpp"
#include "gc/g1/suspendibleThreadSet.hpp"
+#include "oops/oop.inline.hpp"
#include "runtime/atomic.inline.hpp"
G1StringDedupThread* G1StringDedupThread::_thread = NULL;
@@ -55,11 +57,36 @@
return _thread;
}
+class G1StringDedupSharedClosure: public OopClosure {
+ private:
+ G1StringDedupStat& _stat;
+
+ public:
+ G1StringDedupSharedClosure(G1StringDedupStat& stat) : _stat(stat) {}
+
+ virtual void do_oop(oop* p) { ShouldNotReachHere(); }
+ virtual void do_oop(narrowOop* p) {
+ oop java_string = oopDesc::load_decode_heap_oop(p);
+ G1StringDedupTable::deduplicate(java_string, _stat);
+ }
+};
+
+// The CDS archive does not include the string dedupication table. Only the string
+// table is saved in the archive. The shared strings from CDS archive need to be
+// added to the string dedupication table before deduplication occurs. That is
+// done in the begining of the G1StringDedupThread (see G1StringDedupThread::run()
+// below).
+void G1StringDedupThread::deduplicate_shared_strings(G1StringDedupStat& stat) {
+ G1StringDedupSharedClosure sharedStringDedup(stat);
+ StringTable::shared_oops_do(&sharedStringDedup);
+}
+
void G1StringDedupThread::run() {
G1StringDedupStat total_stat;
initialize_in_thread();
wait_for_universe_init();
+ deduplicate_shared_strings(total_stat);
// Main loop
for (;;) {
--- a/hotspot/src/share/vm/gc/g1/g1StringDedupThread.hpp Fri Jun 12 12:55:32 2015 +0200
+++ b/hotspot/src/share/vm/gc/g1/g1StringDedupThread.hpp Fri Jun 12 17:29:14 2015 -0400
@@ -52,6 +52,8 @@
static G1StringDedupThread* thread();
virtual void run();
+
+ void deduplicate_shared_strings(G1StringDedupStat& stat);
};
#endif // SHARE_VM_GC_G1_G1STRINGDEDUPTHREAD_HPP
--- a/hotspot/src/share/vm/memory/filemap.cpp Fri Jun 12 12:55:32 2015 +0200
+++ b/hotspot/src/share/vm/memory/filemap.cpp Fri Jun 12 17:29:14 2015 -0400
@@ -28,6 +28,9 @@
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionaryShared.hpp"
#include "classfile/altHashing.hpp"
+#if INCLUDE_ALL_GCS
+#include "gc/g1/g1CollectedHeap.hpp"
+#endif
#include "memory/filemap.hpp"
#include "memory/metadataFactory.hpp"
#include "memory/oopFactory.hpp"
@@ -166,6 +169,9 @@
_version = _current_version;
_alignment = alignment;
_obj_alignment = ObjectAlignmentInBytes;
+ _narrow_oop_mode = Universe::narrow_oop_mode();
+ _narrow_oop_shift = Universe::narrow_oop_shift();
+ _max_heap_size = MaxHeapSize;
_classpath_entry_table_size = mapinfo->_classpath_entry_table_size;
_classpath_entry_table = mapinfo->_classpath_entry_table;
_classpath_entry_size = mapinfo->_classpath_entry_size;
@@ -441,7 +447,16 @@
} else {
si->_file_offset = _file_offset;
}
- si->_base = base;
+ if (MetaspaceShared::is_string_region(region)) {
+ assert((base - (char*)Universe::narrow_oop_base()) % HeapWordSize == 0, "Sanity");
+ if (base != NULL) {
+ si->_addr._offset = (intx)oopDesc::encode_heap_oop_not_null((oop)base);
+ } else {
+ si->_addr._offset = 0;
+ }
+ } else {
+ si->_addr._base = base;
+ }
si->_used = size;
si->_capacity = capacity;
si->_read_only = read_only;
@@ -450,6 +465,38 @@
write_bytes_aligned(base, (int)size);
}
+// Write the string space. The string space contains one or multiple GC(G1) regions.
+// When the total string space size is smaller than one GC region of the dump time,
+// only one string region is used for shared strings.
+//
+// If the total string space size is bigger than one GC region, there would be more
+// than one GC regions allocated for shared strings. The first/bottom GC region might
+// be a partial GC region with the empty portion at the higher address within that region.
+// The non-empty portion of the first region is written into the archive as one string
+// region. The rest are consecutive full GC regions if they exist, which can be written
+// out in one chunk as another string region.
+void FileMapInfo::write_string_regions(GrowableArray<MemRegion> *regions) {
+ for (int i = MetaspaceShared::first_string;
+ i < MetaspaceShared::first_string + MetaspaceShared::max_strings; i++) {
+ char* start = NULL;
+ size_t size = 0;
+ if (regions->is_nonempty()) {
+ if (i == MetaspaceShared::first_string) {
+ MemRegion first = regions->first();
+ start = (char*)first.start();
+ size = first.byte_size();
+ } else {
+ int len = regions->length();
+ if (len > 1) {
+ start = (char*)regions->at(1).start();
+ size = (char*)regions->at(len - 1).end() - start;
+ }
+ }
+ }
+ write_region(i, start, size, size, false, false);
+ }
+}
+
// Dump bytes to file -- at the current file position.
@@ -514,7 +561,8 @@
// JVM/TI RedefineClasses() support:
// Remap the shared readonly space to shared readwrite, private.
bool FileMapInfo::remap_shared_readonly_as_readwrite() {
- struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[0];
+ int idx = 0;
+ struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[idx];
if (!si->_read_only) {
// the space is already readwrite so we are done
return true;
@@ -524,15 +572,16 @@
if (!open_for_read()) {
return false;
}
+ char *addr = _header->region_addr(idx);
char *base = os::remap_memory(_fd, _full_path, si->_file_offset,
- si->_base, size, false /* !read_only */,
+ addr, size, false /* !read_only */,
si->_allow_exec);
close();
if (base == NULL) {
fail_continue("Unable to remap shared readonly space (errno=%d).", errno);
return false;
}
- if (base != si->_base) {
+ if (base != addr) {
fail_continue("Unable to remap shared readonly space at required address.");
return false;
}
@@ -543,7 +592,7 @@
// Map the whole region at once, assumed to be allocated contiguously.
ReservedSpace FileMapInfo::reserve_shared_memory() {
struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[0];
- char* requested_addr = si->_base;
+ char* requested_addr = _header->region_addr(0);
size_t size = FileMapInfo::shared_spaces_size();
@@ -561,14 +610,16 @@
}
// Memory map a region in the address space.
-static const char* shared_region_name[] = { "ReadOnly", "ReadWrite", "MiscData", "MiscCode"};
+static const char* shared_region_name[] = { "ReadOnly", "ReadWrite", "MiscData", "MiscCode",
+ "String1", "String2" };
char* FileMapInfo::map_region(int i) {
+ assert(!MetaspaceShared::is_string_region(i), "sanity");
struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i];
size_t used = si->_used;
size_t alignment = os::vm_allocation_granularity();
size_t size = align_size_up(used, alignment);
- char *requested_addr = si->_base;
+ char *requested_addr = _header->region_addr(i);
bool read_only;
// If a tool agent is in use (debugging enabled), we must map the address space RW
@@ -583,7 +634,7 @@
char *base = os::map_memory(_fd, _full_path, si->_file_offset,
requested_addr, size, read_only,
si->_allow_exec);
- if (base == NULL || base != si->_base) {
+ if (base == NULL || base != requested_addr) {
fail_continue("Unable to map %s shared space at required address.", shared_region_name[i]);
return NULL;
}
@@ -592,15 +643,119 @@
// in method FileMapInfo::reserve_shared_memory(), which is not called on Windows.
MemTracker::record_virtual_memory_type((address)base, mtClassShared);
#endif
+
return base;
}
+MemRegion *string_ranges = NULL;
+int num_ranges = 0;
+bool FileMapInfo::map_string_regions() {
+#if INCLUDE_ALL_GCS
+ if (UseG1GC && UseCompressedOops && UseCompressedClassPointers) {
+ if (narrow_oop_mode() == Universe::narrow_oop_mode() &&
+ narrow_oop_shift() == Universe::narrow_oop_shift()) {
+ string_ranges = new MemRegion[MetaspaceShared::max_strings];
+ struct FileMapInfo::FileMapHeader::space_info* si;
+
+ for (int i = MetaspaceShared::first_string;
+ i < MetaspaceShared::first_string + MetaspaceShared::max_strings; i++) {
+ si = &_header->_space[i];
+ size_t used = si->_used;
+ if (used > 0) {
+ size_t size = used;
+ char* requested_addr = (char*)((void*)oopDesc::decode_heap_oop_not_null(
+ (narrowOop)si->_addr._offset));
+ string_ranges[num_ranges] = MemRegion((HeapWord*)requested_addr, size / HeapWordSize);
+ num_ranges ++;
+ }
+ }
+
+ if (num_ranges == 0) {
+ return true; // no shared string data
+ }
+
+ // Check that ranges are within the java heap
+ if (!G1CollectedHeap::heap()->check_archive_addresses(string_ranges, num_ranges)) {
+ fail_continue("Unable to allocate shared string space: range is not "
+ "within java heap.");
+ return false;
+ }
+
+ // allocate from java heap
+ if (!G1CollectedHeap::heap()->alloc_archive_regions(string_ranges, num_ranges)) {
+ fail_continue("Unable to allocate shared string space: range is "
+ "already in use.");
+ return false;
+ }
+
+ // Map the string data. No need to call MemTracker::record_virtual_memory_type()
+ // for mapped string regions as they are part of the reserved java heap, which
+ // is already recorded.
+ for (int i = 0; i < num_ranges; i++) {
+ si = &_header->_space[MetaspaceShared::first_string + i];
+ char* addr = (char*)string_ranges[i].start();
+ char* base = os::map_memory(_fd, _full_path, si->_file_offset,
+ addr, string_ranges[i].byte_size(), si->_read_only,
+ si->_allow_exec);
+ if (base == NULL || base != addr) {
+ fail_continue("Unable to map shared string space at required address.");
+ return false;
+ }
+ }
+ return true; // the shared string data is mapped successfuly
+ } else {
+ // narrow oop encoding differ, the shared string data are not used
+ if (PrintSharedSpaces && _header->_space[MetaspaceShared::first_string]._used > 0) {
+ tty->print_cr("Shared string data from the CDS archive is being ignored. "
+ "The current CompressedOops encoding differs from that archived "
+ "due to heap size change. The archive was dumped using max heap "
+ "size %dM.", max_heap_size() >> 20);
+ }
+ }
+ } else {
+ if (PrintSharedSpaces && _header->_space[MetaspaceShared::first_string]._used > 0) {
+ tty->print_cr("Shared string data from the CDS archive is being ignored. UseG1GC, "
+ "UseCompressedOops and UseCompressedClassPointers are required.");
+ }
+ }
+
+ // if we get here, the shared string data is not mapped
+ assert(string_ranges == NULL && num_ranges == 0, "sanity");
+ StringTable::ignore_shared_strings(true);
+#endif
+ return true;
+}
+
+bool FileMapInfo::verify_string_regions() {
+ for (int i = MetaspaceShared::first_string;
+ i < MetaspaceShared::first_string + MetaspaceShared::max_strings; i++) {
+ if (!verify_region_checksum(i)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void FileMapInfo::fixup_string_regions() {
+ if (string_ranges != NULL) {
+ G1CollectedHeap::heap()->fill_archive_regions(string_ranges, num_ranges);
+ }
+}
+
bool FileMapInfo::verify_region_checksum(int i) {
if (!VerifySharedSpaces) {
return true;
}
- const char* buf = _header->_space[i]._base;
+
size_t sz = _header->_space[i]._used;
+
+ if (sz == 0) {
+ return true; // no data
+ }
+ if (MetaspaceShared::is_string_region(i) && StringTable::shared_string_ignored()) {
+ return true; // shared string data are not mapped
+ }
+ const char* buf = _header->region_addr(i);
int crc = ClassLoader::crc32(0, buf, (jint)sz);
if (crc != _header->_space[i]._crc) {
fail_continue("Checksum verification failed.");
@@ -612,14 +767,36 @@
// Unmap a memory region in the address space.
void FileMapInfo::unmap_region(int i) {
+ assert(!MetaspaceShared::is_string_region(i), "sanity");
struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i];
size_t used = si->_used;
size_t size = align_size_up(used, os::vm_allocation_granularity());
- if (!os::unmap_memory(si->_base, size)) {
+
+ if (used == 0) {
+ return;
+ }
+
+ char* addr = _header->region_addr(i);
+ if (!os::unmap_memory(addr, size)) {
fail_stop("Unable to unmap shared space.");
}
}
+void FileMapInfo::unmap_string_regions() {
+ for (int i = MetaspaceShared::first_string;
+ i < MetaspaceShared::first_string + MetaspaceShared::max_strings; i++) {
+ struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i];
+ size_t used = si->_used;
+ if (used > 0) {
+ size_t size = align_size_up(used, os::vm_allocation_granularity());
+ char* addr = (char*)((void*)oopDesc::decode_heap_oop_not_null(
+ (narrowOop)si->_addr._offset));
+ if (!os::unmap_memory(addr, size)) {
+ fail_stop("Unable to unmap shared space.");
+ }
+ }
+ }
+}
void FileMapInfo::assert_mark(bool check) {
if (!check) {
@@ -663,6 +840,15 @@
return true;
}
+char* FileMapInfo::FileMapHeader::region_addr(int idx) {
+ if (MetaspaceShared::is_string_region(idx)) {
+ return (char*)((void*)oopDesc::decode_heap_oop_not_null(
+ (narrowOop)_space[idx]._addr._offset));
+ } else {
+ return _space[idx]._addr._base;
+ }
+}
+
int FileMapInfo::FileMapHeader::compute_crc() {
char* header = data();
// start computing from the field after _crc
@@ -734,8 +920,12 @@
// True if the p is within the mapped shared space, otherwise, false.
bool FileMapInfo::is_in_shared_space(const void* p) {
for (int i = 0; i < MetaspaceShared::n_regions; i++) {
- if (p >= _header->_space[i]._base &&
- p < _header->_space[i]._base + _header->_space[i]._used) {
+ char *base;
+ if (MetaspaceShared::is_string_region(i) && _header->_space[i]._used == 0) {
+ continue;
+ }
+ base = _header->region_addr(i);
+ if (p >= base && p < base + _header->_space[i]._used) {
return true;
}
}
@@ -747,9 +937,10 @@
gclog_or_tty->print_cr("Shared Spaces:");
for (int i = 0; i < MetaspaceShared::n_regions; i++) {
struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i];
+ char *base = _header->region_addr(i);
gclog_or_tty->print(" %s " INTPTR_FORMAT "-" INTPTR_FORMAT,
shared_region_name[i],
- si->_base, si->_base + si->_used);
+ base, base + si->_used);
}
}
@@ -758,12 +949,14 @@
FileMapInfo *map_info = FileMapInfo::current_info();
if (map_info) {
map_info->fail_continue("%s", msg);
- for (int i = 0; i < MetaspaceShared::n_regions; i++) {
- if (map_info->_header->_space[i]._base != NULL) {
+ for (int i = 0; i < MetaspaceShared::num_non_strings; i++) {
+ char *addr = map_info->_header->region_addr(i);
+ if (addr != NULL && !MetaspaceShared::is_string_region(i)) {
map_info->unmap_region(i);
- map_info->_header->_space[i]._base = NULL;
+ map_info->_header->_space[i]._addr._base = NULL;
}
}
+ map_info->unmap_string_regions();
} else if (DumpSharedSpaces) {
fail_stop("%s", msg);
}
--- a/hotspot/src/share/vm/memory/filemap.hpp Fri Jun 12 12:55:32 2015 +0200
+++ b/hotspot/src/share/vm/memory/filemap.hpp Fri Jun 12 17:29:14 2015 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2015, 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
@@ -94,11 +94,18 @@
int _version; // (from enum, above.)
size_t _alignment; // how shared archive should be aligned
int _obj_alignment; // value of ObjectAlignmentInBytes
+ int _narrow_oop_shift; // compressed oop encoding shift
+ uintx _max_heap_size; // java max heap size during dumping
+ Universe::NARROW_OOP_MODE _narrow_oop_mode; // compressed oop encoding mode
struct space_info {
int _crc; // crc checksum of the current space
size_t _file_offset; // sizeof(this) rounded to vm page size
- char* _base; // copy-on-write base address
+ union {
+ char* _base; // copy-on-write base address
+ intx _offset; // offset from the compressed oop encoding base, only used
+ // by string space
+ } _addr;
size_t _capacity; // for validity checking
size_t _used; // for setting space top on read
bool _read_only; // read only space?
@@ -138,6 +145,8 @@
size_t _classpath_entry_size;
SharedClassPathEntry* _classpath_entry_table;
+ char* region_addr(int idx);
+
virtual bool validate();
virtual void populate(FileMapInfo* info, size_t alignment);
int compute_crc();
@@ -166,8 +175,10 @@
void invalidate();
int version() { return _header->_version; }
size_t alignment() { return _header->_alignment; }
+ Universe::NARROW_OOP_MODE narrow_oop_mode() { return _header->_narrow_oop_mode; }
+ int narrow_oop_shift() { return _header->_narrow_oop_shift; }
+ uintx max_heap_size() { return _header->_max_heap_size; }
size_t space_capacity(int i) { return _header->_space[i]._capacity; }
- char* region_base(int i) { return _header->_space[i]._base; }
struct FileMapHeader* header() { return _header; }
static FileMapInfo* current_info() {
@@ -185,10 +196,15 @@
void write_space(int i, Metaspace* space, bool read_only);
void write_region(int region, char* base, size_t size,
size_t capacity, bool read_only, bool allow_exec);
+ void write_string_regions(GrowableArray<MemRegion> *regions);
void write_bytes(const void* buffer, int count);
void write_bytes_aligned(const void* buffer, int count);
char* map_region(int i);
+ bool map_string_regions();
+ bool verify_string_regions();
+ void fixup_string_regions();
void unmap_region(int i);
+ void unmap_string_regions();
bool verify_region_checksum(int i);
void close();
bool is_open() { return _file_open; }
--- a/hotspot/src/share/vm/memory/metaspace.cpp Fri Jun 12 12:55:32 2015 +0200
+++ b/hotspot/src/share/vm/memory/metaspace.cpp Fri Jun 12 17:29:14 2015 -0400
@@ -3307,7 +3307,7 @@
// Map in spaces now also
if (mapinfo->initialize() && MetaspaceShared::map_shared_spaces(mapinfo)) {
cds_total = FileMapInfo::shared_spaces_size();
- cds_address = (address)mapinfo->region_base(0);
+ cds_address = (address)mapinfo->header()->region_addr(0);
} else {
assert(!mapinfo->is_open() && !UseSharedSpaces,
"archive file not closed or shared spaces not disabled.");
--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp Fri Jun 12 12:55:32 2015 +0200
+++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp Fri Jun 12 17:29:14 2015 -0400
@@ -422,6 +422,8 @@
GrowableArray<Klass*> *_class_promote_order;
VirtualSpace _md_vs;
VirtualSpace _mc_vs;
+ CompactHashtableWriter* _string_cht;
+ GrowableArray<MemRegion> *_string_regions;
public:
VM_PopulateDumpSharedSpace(ClassLoaderData* loader_data,
@@ -540,7 +542,7 @@
NOT_PRODUCT(SystemDictionary::verify();)
- // Copy the the symbol table, and the system dictionary to the shared
+ // Copy the the symbol table, string table, and the system dictionary to the shared
// space in usable form. Copy the hashtable
// buckets first [read-write], then copy the linked lists of entries
// [read-only].
@@ -548,6 +550,15 @@
NOT_PRODUCT(SymbolTable::verify());
handle_misc_data_space_failure(SymbolTable::copy_compact_table(&md_top, md_end));
+ size_t ss_bytes = 0;
+ char* ss_low;
+ // The string space has maximum two regions. See FileMapInfo::write_string_regions() for details.
+ _string_regions = new GrowableArray<MemRegion>(2);
+ NOT_PRODUCT(StringTable::verify());
+ handle_misc_data_space_failure(StringTable::copy_compact_table(&md_top, md_end, _string_regions,
+ &ss_bytes));
+ ss_low = _string_regions->is_empty() ? NULL : (char*)_string_regions->first().start();
+
SystemDictionary::reverse();
SystemDictionary::copy_buckets(&md_top, md_end);
@@ -576,7 +587,8 @@
const size_t rw_alloced = rw_space->capacity_bytes_slow(Metaspace::NonClassType);
const size_t md_alloced = md_end-md_low;
const size_t mc_alloced = mc_end-mc_low;
- const size_t total_alloced = ro_alloced + rw_alloced + md_alloced + mc_alloced;
+ const size_t total_alloced = ro_alloced + rw_alloced + md_alloced + mc_alloced
+ + ss_bytes;
// Occupied size of each space.
const size_t ro_bytes = ro_space->used_bytes_slow(Metaspace::NonClassType);
@@ -585,11 +597,12 @@
const size_t mc_bytes = size_t(mc_top - mc_low);
// Percent of total size
- const size_t total_bytes = ro_bytes + rw_bytes + md_bytes + mc_bytes;
+ const size_t total_bytes = ro_bytes + rw_bytes + md_bytes + mc_bytes + ss_bytes;
const double ro_t_perc = ro_bytes / double(total_bytes) * 100.0;
const double rw_t_perc = rw_bytes / double(total_bytes) * 100.0;
const double md_t_perc = md_bytes / double(total_bytes) * 100.0;
const double mc_t_perc = mc_bytes / double(total_bytes) * 100.0;
+ const double ss_t_perc = ss_bytes / double(total_bytes) * 100.0;
// Percent of fullness of each space
const double ro_u_perc = ro_bytes / double(ro_alloced) * 100.0;
@@ -602,6 +615,7 @@
tty->print_cr(fmt_space, "rw", rw_bytes, rw_t_perc, rw_alloced, rw_u_perc, rw_space->bottom());
tty->print_cr(fmt_space, "md", md_bytes, md_t_perc, md_alloced, md_u_perc, md_low);
tty->print_cr(fmt_space, "mc", mc_bytes, mc_t_perc, mc_alloced, mc_u_perc, mc_low);
+ tty->print_cr(fmt_space, "st", ss_bytes, ss_t_perc, ss_bytes, 100.0, ss_low);
tty->print_cr("total : %9d [100.0%% of total] out of %9d bytes [%4.1f%% used]",
total_bytes, total_alloced, total_u_perc);
@@ -631,6 +645,7 @@
pointer_delta(mc_top, _mc_vs.low(), sizeof(char)),
SharedMiscCodeSize,
true, true);
+ mapinfo->write_string_regions(_string_regions);
// Pass 2 - write data.
mapinfo->open_for_write();
@@ -646,6 +661,8 @@
pointer_delta(mc_top, _mc_vs.low(), sizeof(char)),
SharedMiscCodeSize,
true, true);
+ mapinfo->write_string_regions(_string_regions);
+
mapinfo->close();
memmove(vtbl_list, saved_vtbl, vtbl_list_size * sizeof(void*));
@@ -942,6 +959,11 @@
return UseSharedSpaces && FileMapInfo::current_info()->is_in_shared_space(p);
}
+bool MetaspaceShared::is_string_region(int idx) {
+ return (idx >= MetaspaceShared::first_string &&
+ idx < MetaspaceShared::first_string + MetaspaceShared::max_strings);
+}
+
void MetaspaceShared::print_shared_spaces() {
if (UseSharedSpaces) {
FileMapInfo::current_info()->print_shared_spaces();
@@ -972,13 +994,15 @@
// Map each shared region
if ((_ro_base = mapinfo->map_region(ro)) != NULL &&
- mapinfo->verify_region_checksum(ro) &&
+ mapinfo->verify_region_checksum(ro) &&
(_rw_base = mapinfo->map_region(rw)) != NULL &&
- mapinfo->verify_region_checksum(rw) &&
+ mapinfo->verify_region_checksum(rw) &&
(_md_base = mapinfo->map_region(md)) != NULL &&
- mapinfo->verify_region_checksum(md) &&
+ mapinfo->verify_region_checksum(md) &&
(_mc_base = mapinfo->map_region(mc)) != NULL &&
- mapinfo->verify_region_checksum(mc) &&
+ mapinfo->verify_region_checksum(mc) &&
+ mapinfo->map_string_regions() &&
+ mapinfo->verify_string_regions() &&
(image_alignment == (size_t)max_alignment()) &&
mapinfo->validate_classpath_entry_table()) {
// Success (no need to do anything)
@@ -990,6 +1014,7 @@
if (_rw_base != NULL) mapinfo->unmap_region(rw);
if (_md_base != NULL) mapinfo->unmap_region(md);
if (_mc_base != NULL) mapinfo->unmap_region(mc);
+ mapinfo->unmap_string_regions();
#ifndef _WINDOWS
// Release the entire mapped region
shared_rs.release();
@@ -1011,7 +1036,7 @@
void MetaspaceShared::initialize_shared_spaces() {
FileMapInfo *mapinfo = FileMapInfo::current_info();
- char* buffer = mapinfo->region_base(md);
+ char* buffer = mapinfo->header()->region_addr(md);
// Skip over (reserve space for) a list of addresses of C++ vtables
// for Klass objects. They get filled in later.
@@ -1027,13 +1052,16 @@
buffer += sizeof(intptr_t);
buffer += vtable_size;
- // Create the shared symbol table using the bucket array at this spot in the
+ // Create the shared symbol table using the compact table 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).
buffer = (char*)SymbolTable::init_shared_table(buffer);
SymbolTable::create_table();
+ // Create the shared string table using the compact table
+ buffer = (char*)StringTable::init_shared_table(mapinfo, buffer);
+
// Create the shared dictionary using the bucket array at this spot in
// the misc data space. Since the shared dictionary table is never
// modified, this region (of mapped pages) will be (effectively, if
@@ -1100,6 +1128,11 @@
}
}
+void MetaspaceShared::fixup_shared_string_regions() {
+ FileMapInfo *mapinfo = FileMapInfo::current_info();
+ mapinfo->fixup_string_regions();
+}
+
// JVM/TI RedefineClasses() support:
bool MetaspaceShared::remap_shared_readonly_as_readwrite() {
assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
--- a/hotspot/src/share/vm/memory/metaspaceShared.hpp Fri Jun 12 12:55:32 2015 +0200
+++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp Fri Jun 12 17:29:14 2015 -0400
@@ -53,6 +53,7 @@
memset(this, 0, sizeof(*this));
}
CompactHashtableStats symbol;
+ CompactHashtableStats string;
};
// Class Data Sharing Support
@@ -90,7 +91,10 @@
rw = 1, // read-write shared space in the heap
md = 2, // miscellaneous data for initializing tables, etc.
mc = 3, // miscellaneous code - vtable replacement.
- n_regions = 4
+ max_strings = 2, // max number of string regions in string space
+ num_non_strings = 4, // number of non-string regions
+ first_string = num_non_strings, // index of first string region
+ n_regions = max_strings + num_non_strings // total number of regions
};
// Accessor functions to save shared space created for metadata, which has
@@ -124,10 +128,13 @@
}
static bool map_shared_spaces(FileMapInfo* mapinfo) NOT_CDS_RETURN_(false);
static void initialize_shared_spaces() NOT_CDS_RETURN;
+ static void fixup_shared_string_regions() NOT_CDS_RETURN;
// Return true if given address is in the mapped shared space.
static bool is_in_shared_space(const void* p) NOT_CDS_RETURN_(false);
+ static bool is_string_region(int idx) NOT_CDS_RETURN_(false);
+
static void generate_vtable_methods(void** vtbl_list,
void** vtable,
char** md_top, char* md_end,
--- a/hotspot/src/share/vm/memory/universe.cpp Fri Jun 12 12:55:32 2015 +0200
+++ b/hotspot/src/share/vm/memory/universe.cpp Fri Jun 12 17:29:14 2015 -0400
@@ -311,6 +311,7 @@
SystemDictionary::Cloneable_klass(), "u3");
assert(_the_array_interfaces_array->at(1) ==
SystemDictionary::Serializable_klass(), "u3");
+ MetaspaceShared::fixup_shared_string_regions();
} else {
// Set up shared interfaces array. (Do this before supers are set up.)
_the_array_interfaces_array->at_put(0, SystemDictionary::Cloneable_klass());
--- a/hotspot/src/share/vm/prims/whitebox.cpp Fri Jun 12 12:55:32 2015 +0200
+++ b/hotspot/src/share/vm/prims/whitebox.cpp Fri Jun 12 17:29:14 2015 -0400
@@ -31,6 +31,7 @@
#include "code/codeCache.hpp"
#include "jvmtifiles/jvmtiEnv.hpp"
#include "memory/metadataFactory.hpp"
+#include "memory/metaspaceShared.hpp"
#include "memory/universe.hpp"
#include "oops/oop.inline.hpp"
#include "prims/wbtestmethods/parserTests.hpp"
@@ -1207,6 +1208,11 @@
return NULL;
WB_END
+WB_ENTRY(jboolean, WB_IsShared(JNIEnv* env, jobject wb, jobject obj))
+ oop obj_oop = JNIHandles::resolve(obj);
+ return MetaspaceShared::is_in_shared_space((void*)obj_oop);
+WB_END
+
//Some convenience methods to deal with objects from java
int WhiteBox::offset_for_field(const char* field_name, oop object,
Symbol* signature_symbol) {
@@ -1431,6 +1437,7 @@
{CC"getMethodStringOption",
CC"(Ljava/lang/reflect/Executable;Ljava/lang/String;)Ljava/lang/String;",
(void*)&WB_GetMethodStringOption},
+ {CC"isShared", CC"(Ljava/lang/Object;)Z", (void*)&WB_IsShared },
};
#undef CC
--- a/hotspot/src/share/vm/runtime/synchronizer.cpp Fri Jun 12 12:55:32 2015 +0200
+++ b/hotspot/src/share/vm/runtime/synchronizer.cpp Fri Jun 12 17:29:14 2015 -0400
@@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "classfile/vmSymbols.hpp"
+#include "memory/metaspaceShared.hpp"
#include "memory/padded.hpp"
#include "memory/resourceArea.hpp"
#include "oops/markOop.hpp"
@@ -638,11 +639,11 @@
// hashCode() is a heap mutator ...
// Relaxing assertion for bug 6320749.
- assert(Universe::verify_in_progress() ||
+ assert(Universe::verify_in_progress() || DumpSharedSpaces ||
!SafepointSynchronize::is_at_safepoint(), "invariant");
- assert(Universe::verify_in_progress() ||
+ assert(Universe::verify_in_progress() || DumpSharedSpaces ||
Self->is_Java_thread() , "invariant");
- assert(Universe::verify_in_progress() ||
+ assert(Universe::verify_in_progress() || DumpSharedSpaces ||
((JavaThread *)Self)->thread_state() != _thread_blocked, "invariant");
ObjectMonitor* monitor = NULL;
--- a/hotspot/src/share/vm/services/virtualMemoryTracker.cpp Fri Jun 12 12:55:32 2015 +0200
+++ b/hotspot/src/share/vm/services/virtualMemoryTracker.cpp Fri Jun 12 17:29:14 2015 -0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2015, 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
@@ -347,6 +347,13 @@
return true;
}
+ // Mapped CDS string region.
+ // The string region(s) is part of the java heap.
+ if (reserved_rgn->flag() == mtJavaHeap) {
+ assert(reserved_rgn->contain_region(base_addr, size), "Reserved heap region should contain this mapping region");
+ return true;
+ }
+
ShouldNotReachHere();
return false;
}