8015086: add interned strings to the shared archive

Support saving interned strings in shared CDS archive.

Reviewed-by: coleenp, iklam, pliden
This commit is contained in:
Jiangli Zhou 2015-06-12 17:29:14 -04:00
parent c3f3f0f1bd
commit edcd4cb94b
17 changed files with 724 additions and 116 deletions

View File

@ -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 @@ 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
// 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 @@ juint* CompactHashtableWriter::dump_table(juint* p, juint** first_bucket,
// 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 @@ juint* CompactHashtableWriter::dump_buckets(juint* compact_table, juint* p,
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
}
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());
}
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");
@ -174,6 +185,10 @@ void CompactHashtableWriter::dump(char** top, char* end) {
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 @@ void CompactHashtableWriter::dump(char** top, char* end) {
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 @@ void CompactHashtableWriter::dump(char** top, char* end) {
}
}
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>::symbols_do(SymbolClosur
}
}
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 @@ HashtableTextDump::HashtableTextDump(const char* filename) : _fd(-1) {
_p = _base;
_end = _base + st.st_size;
_filename = filename;
_prefix_type = Unknown;
_line_no = 1;
}
HashtableTextDump::~HashtableTextDump() {
@ -286,9 +342,9 @@ void HashtableTextDump::quit(const char* err, const char* msg) {
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 @@ bool HashtableTextDump::skip_newline() {
} else if (_p[0] == '\n') {
_p += 1;
} else {
corrupted(_p);
corrupted(_p, "Unexpected character");
}
_line_no ++;
return true;
}
@ -328,26 +385,60 @@ void HashtableTextDump::check_version(const char* ver) {
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 @@ void HashtableTextDump::get_utf8(char* utf8_buffer, int utf8_length) {
case 'r': *to++ = '\r'; break;
case '\\': *to++ = '\\'; break;
default:
ShouldNotReachHere();
corrupted(_p, "Unsupported character");
}
}
}

View File

@ -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 @@ public:
// 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 @@ public:
// 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:
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 @@ public:
Symbol *symbol() {
return (Symbol*)_literal;
}
oop string() {
return (oop)_literal;
}
unsigned int hash() {
return _hash;
}
@ -95,7 +100,7 @@ public:
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 @@ private:
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 @@ public:
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 @@ private:
public:
void dump(char** top, char* end);
const char* table_name();
};
#define REGULAR_BUCKET_TYPE 0
@ -136,23 +146,23 @@ public:
/////////////////////////////////////////////////////////////////////////////
//
// 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 @@ public:
// 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 @@ public:
//
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 @@ public:
_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 @@ public:
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 @@ public:
// 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 @@ class HashtableTextDump VALUE_OBJ_CLASS_SPEC {
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 @@ public:
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 @@ public:
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);

View File

@ -118,6 +118,10 @@ class java_lang_String : AllStatic {
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 @@ class java_lang_String : AllStatic {
// Debugging
static void print(oop java_string, outputStream* st);
friend class JavaClasses;
friend class StringTable;
};

View File

@ -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 @@ class StableMemoryChecker : public StackObj {
// --------------------------------------------------------------------------
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 @@ oop StringTable::basic_add(int index_arg, Handle string, jchar* name,
// 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 @@ static void ensure_string_alive(oop string) {
}
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::lookup(jchar* name, int len) {
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 @@ int StringtableDCmd::num_arguments() {
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;
}

View File

@ -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 @@ private:
// 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 @@ private:
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 @@ public:
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();
}

View File

@ -539,7 +539,8 @@ void SymbolTable::dump(outputStream* st, bool verbose) {
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 @@ bool SymbolTable::copy_compact_table(char** top, char*end) {
}
}
char* old_top = *top;
ch_table.dump(top, end);
*top = (char*)align_pointer_up(*top, sizeof(void*));
@ -565,7 +565,8 @@ bool SymbolTable::copy_compact_table(char** top, char*end) {
}
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*));
}

View File

@ -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 @@ G1StringDedupThread* G1StringDedupThread::thread() {
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 (;;) {

View File

@ -52,6 +52,8 @@ public:
static G1StringDedupThread* thread();
virtual void run();
void deduplicate_shared_strings(G1StringDedupStat& stat);
};
#endif // SHARE_VM_GC_G1_G1STRINGDEDUPTHREAD_HPP

View File

@ -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 @@ void FileMapInfo::FileMapHeader::populate(FileMapInfo* mapinfo, size_t alignment
_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 @@ void FileMapInfo::write_region(int region, char* base, size_t size,
} 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 @@ void FileMapInfo::write_region(int region, char* base, size_t size,
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 @@ void FileMapInfo::close() {
// 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 @@ bool FileMapInfo::remap_shared_readonly_as_readwrite() {
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 @@ bool FileMapInfo::remap_shared_readonly_as_readwrite() {
// 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 @@ ReservedSpace FileMapInfo::reserve_shared_memory() {
}
// 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* FileMapInfo::map_region(int i) {
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 @@ char* FileMapInfo::map_region(int i) {
// 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 @@ bool FileMapInfo::verify_region_checksum(int i) {
// 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 @@ bool FileMapInfo::initialize() {
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 @@ bool FileMapInfo::validate_header() {
// 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 @@ void FileMapInfo::print_shared_spaces() {
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 @@ void FileMapInfo::stop_sharing_and_unmap(const char* msg) {
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);
}

View File

@ -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 @@ public:
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 @@ public:
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 @@ public:
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 @@ public:
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; }

View File

@ -3307,7 +3307,7 @@ void Metaspace::global_initialize() {
// 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.");

View File

@ -422,6 +422,8 @@ private:
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 @@ void VM_PopulateDumpSharedSpace::doit() {
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 @@ void VM_PopulateDumpSharedSpace::doit() {
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 @@ void VM_PopulateDumpSharedSpace::doit() {
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 @@ void VM_PopulateDumpSharedSpace::doit() {
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 @@ void VM_PopulateDumpSharedSpace::doit() {
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 @@ void VM_PopulateDumpSharedSpace::doit() {
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 @@ void VM_PopulateDumpSharedSpace::doit() {
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 @@ bool MetaspaceShared::is_in_shared_space(const void* p) {
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 @@ bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) {
// 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 @@ bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) {
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 @@ bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) {
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 @@ void MetaspaceShared::initialize_shared_spaces() {
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::initialize_shared_spaces() {
}
}
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");

View File

@ -53,6 +53,7 @@ public:
memset(this, 0, sizeof(*this));
}
CompactHashtableStats symbol;
CompactHashtableStats string;
};
// Class Data Sharing Support
@ -90,7 +91,10 @@ class MetaspaceShared : AllStatic {
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 @@ class MetaspaceShared : AllStatic {
}
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,

View File

@ -311,6 +311,7 @@ void Universe::genesis(TRAPS) {
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());

View File

@ -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 @@ WB_ENTRY(jobject, WB_GetMethodStringOption(JNIEnv* env, jobject wb, jobject meth
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 @@ static JNINativeMethod methods[] = {
{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

View File

@ -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 @@ intptr_t ObjectSynchronizer::FastHashCode(Thread * Self, oop obj) {
// 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;

View File

@ -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 @@ bool VirtualMemoryTracker::add_reserved_region(address base_addr, size_t size,
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;
}