8150607: Clean up CompactHashtable

Refactored code, and added test cases for serviceability agent

Reviewed-by: jiangli, ccheung
This commit is contained in:
Ioi Lam 2016-04-17 19:15:52 -07:00
parent 16c430d2b6
commit 6526d15d6e
18 changed files with 808 additions and 494 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2016, 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
@ -25,6 +25,7 @@
#include "precompiled.hpp"
#include "classfile/compactHashtable.inline.hpp"
#include "classfile/javaClasses.hpp"
#include "memory/metadataFactory.hpp"
#include "memory/metaspaceShared.hpp"
#include "prims/jvm.h"
#include "utilities/numberSeq.hpp"
@ -34,270 +35,259 @@
//
// The compact hash table writer implementations
//
CompactHashtableWriter::CompactHashtableWriter(int table_type,
int num_entries,
CompactHashtableWriter::CompactHashtableWriter(int num_buckets,
CompactHashtableStats* stats) {
assert(DumpSharedSpaces, "dump-time only");
_type = table_type;
_num_entries = num_entries;
_num_buckets = number_of_buckets(_num_entries);
_buckets = NEW_C_HEAP_ARRAY(Entry*, _num_buckets, mtSymbol);
memset(_buckets, 0, sizeof(Entry*) * _num_buckets);
_num_buckets = num_buckets;
_num_entries = 0;
_buckets = NEW_C_HEAP_ARRAY(GrowableArray<Entry>*, _num_buckets, mtSymbol);
for (int i=0; i<_num_buckets; i++) {
_buckets[i] = new (ResourceObj::C_HEAP, mtSymbol) GrowableArray<Entry>(0, true, mtSymbol);
}
/* bucket sizes table */
_bucket_sizes = NEW_C_HEAP_ARRAY(juint, _num_buckets, mtSymbol);
memset(_bucket_sizes, 0, sizeof(juint) * _num_buckets);
stats->hashentry_count = _num_entries;
// Compact buckets' entries will have only the 4-byte offset, but
// we don't know how many there will be at this point. So use a
// conservative estimate here. The size is adjusted later when we
// write out the buckets.
stats->hashentry_bytes = _num_entries * 8;
stats->bucket_count = _num_buckets;
stats->bucket_bytes = (_num_buckets + 1) * (sizeof(juint));
stats->bucket_count = _num_buckets;
stats->bucket_bytes = (_num_buckets + 1) * (sizeof(u4));
_stats = stats;
// See compactHashtable.hpp for table layout
_required_bytes = sizeof(juint) * 2; // _base_address, written as 2 juints
_required_bytes+= sizeof(juint) + // num_entries
sizeof(juint) + // num_buckets
stats->hashentry_bytes +
stats->bucket_bytes;
_compact_buckets = NULL;
_compact_entries = NULL;
_num_empty_buckets = 0;
_num_value_only_buckets = 0;
_num_other_buckets = 0;
}
CompactHashtableWriter::~CompactHashtableWriter() {
for (int index = 0; index < _num_buckets; index++) {
Entry* next = NULL;
for (Entry* tent = _buckets[index]; tent; tent = next) {
next = tent->next();
delete tent;
}
GrowableArray<Entry>* bucket = _buckets[index];
delete bucket;
}
FREE_C_HEAP_ARRAY(juint, _bucket_sizes);
FREE_C_HEAP_ARRAY(Entry*, _buckets);
}
// Calculate the number of buckets in the temporary hash table
int CompactHashtableWriter::number_of_buckets(int num_entries) {
const int buksize = (int)SharedSymbolTableBucketSize;
int num_buckets = (num_entries + buksize - 1) / buksize;
num_buckets = (num_buckets + 1) & (~0x01);
return num_buckets;
FREE_C_HEAP_ARRAY(GrowableArray<Entry>*, _buckets);
}
// Add a symbol entry to the temporary hash table
void CompactHashtableWriter::add(unsigned int hash, Entry* entry) {
void CompactHashtableWriter::add(unsigned int hash, u4 value) {
int index = hash % _num_buckets;
entry->set_next(_buckets[index]);
_buckets[index] = entry;
_bucket_sizes[index] ++;
_buckets[index]->append_if_missing(Entry(hash, value));
_num_entries++;
}
// Write the compact table's bucket infos
juint* CompactHashtableWriter::dump_table(juint* p, juint** first_bucket,
NumberSeq* summary) {
int index;
juint* compact_table = p;
// 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;
void CompactHashtableWriter::allocate_table() {
int entries_space = 0;
for (int index = 0; index < _num_buckets; index++) {
GrowableArray<Entry>* bucket = _buckets[index];
int bucket_size = bucket->length();
if (bucket_size == 1) {
entries_space++;
} else {
entries_space += 2 * bucket_size;
}
}
for (index = 0; index < _num_buckets; index++) {
int bucket_size = _bucket_sizes[index];
if (entries_space & ~BUCKET_OFFSET_MASK) {
vm_exit_during_initialization("CompactHashtableWriter::allocate_table: Overflow! "
"Too many entries.");
}
Thread* THREAD = VMThread::vm_thread();
ClassLoaderData* loader_data = ClassLoaderData::the_null_class_loader_data();
_compact_buckets = MetadataFactory::new_array<u4>(loader_data, _num_buckets + 1, THREAD);
_compact_entries = MetadataFactory::new_array<u4>(loader_data, entries_space, THREAD);
_stats->hashentry_count = _num_entries;
_stats->hashentry_bytes = entries_space * sizeof(u4);
}
// Write the compact table's buckets
void CompactHashtableWriter::dump_table(NumberSeq* summary) {
u4 offset = 0;
for (int index = 0; index < _num_buckets; index++) {
GrowableArray<Entry>* bucket = _buckets[index];
int bucket_size = bucket->length();
if (bucket_size == 1) {
// bucket with one entry is compacted and only has the symbol offset
compact_table[index] = BUCKET_INFO(offset, COMPACT_BUCKET_TYPE);
offset += bucket_size; // each entry contains symbol offset only
_compact_buckets->at_put(index, BUCKET_INFO(offset, VALUE_ONLY_BUCKET_TYPE));
Entry ent = bucket->at(0);
_compact_entries->at_put(offset++, ent.value());
_num_value_only_buckets++;
} else {
// regular bucket, each entry is a symbol (hash, offset) pair
compact_table[index] = BUCKET_INFO(offset, REGULAR_BUCKET_TYPE);
offset += bucket_size * 2; // each hash entry is 2 juints
}
if (offset & ~BUCKET_OFFSET_MASK) {
vm_exit_during_initialization("CompactHashtableWriter::dump_table: Overflow! "
"Too many symbols.");
_compact_buckets->at_put(index, BUCKET_INFO(offset, REGULAR_BUCKET_TYPE));
for (int i=0; i<bucket_size; i++) {
Entry ent = bucket->at(i);
_compact_entries->at_put(offset++, u4(ent.hash())); // write entry hash
_compact_entries->at_put(offset++, ent.value());
}
if (bucket_size == 0) {
_num_empty_buckets++;
} else {
_num_other_buckets++;
}
}
summary->add(bucket_size);
}
// Mark the end of the table
compact_table[_num_buckets] = BUCKET_INFO(offset, TABLEEND_BUCKET_TYPE);
return compact_table;
// Mark the end of the buckets
_compact_buckets->at_put(_num_buckets, BUCKET_INFO(offset, TABLEEND_BUCKET_TYPE));
assert(offset == (u4)_compact_entries->length(), "sanity");
}
// Write the compact table's entries
juint* CompactHashtableWriter::dump_buckets(juint* compact_table, juint* p,
NumberSeq* summary) {
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 <= MAX_SHARED_DELTA, "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++) {
juint count = 0;
int bucket_size = _bucket_sizes[index];
int bucket_type = BUCKET_TYPE(compact_table[index]);
if (bucket_size == 1) {
assert(bucket_type == COMPACT_BUCKET_TYPE, "Bad bucket type");
num_compact_buckets ++;
}
for (Entry* tent = _buckets[index]; tent;
tent = tent->next()) {
if (bucket_type == REGULAR_BUCKET_TYPE) {
*p++ = juint(tent->hash()); // write 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());
}
count ++;
}
assert(count == _bucket_sizes[index], "sanity");
}
// Adjust the hashentry_bytes in CompactHashtableStats. Each compact
// bucket saves 4-byte.
_stats->hashentry_bytes -= num_compact_buckets * 4;
return p;
}
// Write the compact table
void CompactHashtableWriter::dump(char** top, char* end) {
void CompactHashtableWriter::dump(SimpleCompactHashtable *cht, const char* table_name) {
NumberSeq summary;
char* old_top = *top;
juint* p = (juint*)(*top);
allocate_table();
dump_table(&summary);
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
*p++ = _num_buckets; // number of buckets in the table
juint* first_bucket = NULL;
juint* compact_table = dump_table(p, &first_bucket, &summary);
juint* bucket_end = dump_buckets(compact_table, first_bucket, &summary);
assert(bucket_end <= (juint*)end, "cannot write past end");
*top = (char*)bucket_end;
int table_bytes = _stats->bucket_bytes + _stats->hashentry_bytes;
address base_address = address(MetaspaceShared::shared_rs()->base());
cht->init(base_address, _num_entries, _num_buckets,
_compact_buckets->data(), _compact_entries->data());
if (PrintSharedSpaces) {
double avg_cost = 0.0;
if (_num_entries > 0) {
avg_cost = double(_required_bytes)/double(_num_entries);
avg_cost = double(table_bytes)/double(_num_entries);
}
tty->print_cr("Shared %s table stats -------- base: " PTR_FORMAT,
table_name(), (intptr_t)base_address);
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("Total bytes used : %9d", table_bytes);
tty->print_cr("Average bytes per entry : %9.3f", avg_cost);
tty->print_cr("Average bucket size : %9.3f", summary.avg());
tty->print_cr("Variance of bucket size : %9.3f", summary.variance());
tty->print_cr("Std. dev. of bucket size: %9.3f", summary.sd());
tty->print_cr("Maximum bucket size : %9d", (int)summary.maximum());
tty->print_cr("Empty buckets : %9d", _num_empty_buckets);
tty->print_cr("Value_Only buckets : %9d", _num_value_only_buckets);
tty->print_cr("Other buckets : %9d", _num_other_buckets);
}
}
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";
/////////////////////////////////////////////////////////////
//
// Customization for dumping Symbol and String tables
void CompactSymbolTableWriter::add(unsigned int hash, Symbol *symbol) {
address base_address = address(MetaspaceShared::shared_rs()->base());
uintx max_delta = uintx(MetaspaceShared::shared_rs()->size());
assert(max_delta <= MAX_SHARED_DELTA, "range check");
uintx deltax = address(symbol) - base_address;
assert(deltax < max_delta, "range check");
u4 delta = u4(deltax);
CompactHashtableWriter::add(hash, delta);
}
void CompactStringTableWriter::add(unsigned int hash, oop string) {
CompactHashtableWriter::add(hash, oopDesc::encode_heap_oop(string));
}
void CompactSymbolTableWriter::dump(CompactHashtable<Symbol*, char> *cht) {
CompactHashtableWriter::dump(cht, "symbol");
}
void CompactStringTableWriter::dump(CompactHashtable<oop, char> *cht) {
CompactHashtableWriter::dump(cht, "string");
}
/////////////////////////////////////////////////////////////
//
// The CompactHashtable implementation
//
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++;
_base_address = uintx(jlong_from(upper, lower));
_entry_count = *p++;
_bucket_count = *p++;
_buckets = p;
_table_end_offset = BUCKET_OFFSET(p[_bucket_count]); // located at the end of the bucket_info table
juint *end = _buckets + _table_end_offset;
return (const char*)end;
void SimpleCompactHashtable::serialize(SerializeClosure* soc) {
soc->do_ptr((void**)&_base_address);
soc->do_u4(&_entry_count);
soc->do_u4(&_bucket_count);
soc->do_ptr((void**)&_buckets);
soc->do_ptr((void**)&_entries);
}
bool SimpleCompactHashtable::exists(u4 value) {
assert(!DumpSharedSpaces, "run-time only");
if (_entry_count == 0) {
return false;
}
unsigned int hash = (unsigned int)value;
int index = hash % _bucket_count;
u4 bucket_info = _buckets[index];
u4 bucket_offset = BUCKET_OFFSET(bucket_info);
int bucket_type = BUCKET_TYPE(bucket_info);
u4* entry = _entries + bucket_offset;
if (bucket_type == VALUE_ONLY_BUCKET_TYPE) {
return (entry[0] == value);
} else {
u4*entry_max = _entries + BUCKET_OFFSET(_buckets[index + 1]);
while (entry <entry_max) {
if (entry[1] == value) {
return true;
}
entry += 2;
}
return false;
}
}
template <class I>
inline void SimpleCompactHashtable::iterate(const I& iterator) {
assert(!DumpSharedSpaces, "run-time only");
for (u4 i = 0; i < _bucket_count; i++) {
u4 bucket_info = _buckets[i];
u4 bucket_offset = BUCKET_OFFSET(bucket_info);
int bucket_type = BUCKET_TYPE(bucket_info);
u4* entry = _entries + bucket_offset;
if (bucket_type == VALUE_ONLY_BUCKET_TYPE) {
iterator.do_value(_base_address, entry[0]);
} else {
u4*entry_max = _entries + BUCKET_OFFSET(_buckets[i + 1]);
while (entry < entry_max) {
iterator.do_value(_base_address, entry[0]);
entry += 2;
}
}
}
}
template <class T, class N> void CompactHashtable<T, N>::serialize(SerializeClosure* soc) {
SimpleCompactHashtable::serialize(soc);
soc->do_u4(&_type);
}
class CompactHashtable_SymbolIterator {
SymbolClosure* const _closure;
public:
CompactHashtable_SymbolIterator(SymbolClosure *cl) : _closure(cl) {}
inline void do_value(address base_address, u4 offset) const {
Symbol* sym = (Symbol*)((void*)(base_address + offset));
_closure->do_symbol(&sym);
}
};
template <class T, class N> void CompactHashtable<T, N>::symbols_do(SymbolClosure *cl) {
assert(!DumpSharedSpaces, "run-time only");
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;
Symbol* sym;
if (bucket_type == COMPACT_BUCKET_TYPE) {
sym = (Symbol*)((void*)(_base_address + bucket[0]));
cl->do_symbol(&sym);
} else {
bucket_end += BUCKET_OFFSET(_buckets[i + 1]);
while (bucket < bucket_end) {
sym = (Symbol*)((void*)(_base_address + bucket[1]));
cl->do_symbol(&sym);
bucket += 2;
}
}
}
CompactHashtable_SymbolIterator iterator(cl);
iterate(iterator);
}
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;
}
}
class CompactHashtable_OopIterator {
OopClosure* const _closure;
public:
CompactHashtable_OopIterator(OopClosure *cl) : _closure(cl) {}
inline void do_value(address base_address, u4 offset) const {
narrowOop o = (narrowOop)offset;
_closure->do_oop(&o);
}
};
template <class T, class N> void CompactHashtable<T, N>::oops_do(OopClosure* cl) {
assert(_type == _string_table || _bucket_count == 0, "sanity");
CompactHashtable_OopIterator iterator(cl);
iterate(iterator);
}
// Explicitly instantiate these types
@ -360,7 +350,7 @@ bool HashtableTextDump::skip_newline() {
} else {
corrupted(_p, "Unexpected character");
}
_line_no ++;
_line_no++;
return true;
}
@ -390,7 +380,7 @@ void HashtableTextDump::check_version(const char* ver) {
}
void HashtableTextDump::scan_prefix_type() {
_p ++;
_p++;
if (strncmp(_p, "SECTION: String", 15) == 0) {
_p += 15;
_prefix_type = StringPrefix;