8185525: Add JFR event for DictionarySizes

Added TableStatistics event

Reviewed-by: egahlin, coleenp
This commit is contained in:
Gerard Ziemski 2019-05-08 11:11:50 -05:00
parent c5fc45ee08
commit d988e67b89
22 changed files with 594 additions and 75 deletions

View File

@ -443,7 +443,7 @@ void ClassLoaderDataGraph::print_dictionary(outputStream* st) {
} }
} }
void ClassLoaderDataGraph::print_dictionary_statistics(outputStream* st) { void ClassLoaderDataGraph::print_table_statistics(outputStream* st) {
FOR_ALL_DICTIONARY(cld) { FOR_ALL_DICTIONARY(cld) {
ResourceMark rm; ResourceMark rm;
stringStream tempst; stringStream tempst;

View File

@ -112,7 +112,7 @@ class ClassLoaderDataGraph : public AllStatic {
static void verify_dictionary(); static void verify_dictionary();
static void print_dictionary(outputStream* st); static void print_dictionary(outputStream* st);
static void print_dictionary_statistics(outputStream* st); static void print_table_statistics(outputStream* st);
// CMS support. // CMS support.
static void remember_new_clds(bool remember) { _saved_head = (remember ? _head : NULL); } static void remember_new_clds(bool remember) { _saved_head = (remember ? _head : NULL); }

View File

@ -579,6 +579,13 @@ struct SizeFunc : StackObj {
}; };
}; };
TableStatistics StringTable::get_table_statistics() {
static TableStatistics ts;
SizeFunc sz;
ts = _local_table->statistics_get(Thread::current(), sz, ts);
return ts;
}
void StringTable::print_table_statistics(outputStream* st, void StringTable::print_table_statistics(outputStream* st,
const char* table_name) { const char* table_name) {
SizeFunc sz; SizeFunc sz;

View File

@ -99,6 +99,7 @@ private:
// The string table // The string table
static StringTable* the_table() { return _the_table; } static StringTable* the_table() { return _the_table; }
size_t table_size(); size_t table_size();
TableStatistics get_table_statistics();
static OopStorage* weak_storage() { return the_table()->_weak_handles; } static OopStorage* weak_storage() { return the_table()->_weak_handles; }

View File

@ -503,6 +503,13 @@ struct SizeFunc : StackObj {
}; };
}; };
TableStatistics SymbolTable::get_table_statistics() {
static TableStatistics ts;
SizeFunc sz;
ts = _local_table->statistics_get(Thread::current(), sz, ts);
return ts;
}
void SymbolTable::print_table_statistics(outputStream* st, void SymbolTable::print_table_statistics(outputStream* st,
const char* table_name) { const char* table_name) {
SizeFunc sz; SizeFunc sz;

View File

@ -171,6 +171,7 @@ public:
// The symbol table // The symbol table
static SymbolTable* the_table() { return _the_table; } static SymbolTable* the_table() { return _the_table; }
size_t table_size(); size_t table_size();
TableStatistics get_table_statistics();
enum { enum {
symbol_alloc_batch_size = 8, symbol_alloc_batch_size = 8,

View File

@ -2849,13 +2849,25 @@ void SystemDictionary::dump(outputStream *st, bool verbose) {
print_on(st); print_on(st);
} else { } else {
CDS_ONLY(SystemDictionaryShared::print_table_statistics(st)); CDS_ONLY(SystemDictionaryShared::print_table_statistics(st));
ClassLoaderDataGraph::print_dictionary_statistics(st); ClassLoaderDataGraph::print_table_statistics(st);
placeholders()->print_table_statistics(st, "Placeholder Table"); placeholders()->print_table_statistics(st, "Placeholder Table");
constraints()->print_table_statistics(st, "LoaderConstraints Table"); constraints()->print_table_statistics(st, "LoaderConstraints Table");
_pd_cache_table->print_table_statistics(st, "ProtectionDomainCache Table"); pd_cache_table()->print_table_statistics(st, "ProtectionDomainCache Table");
} }
} }
TableStatistics SystemDictionary::placeholders_statistics() {
return placeholders()->statistics_calculate();
}
TableStatistics SystemDictionary::loader_constraints_statistics() {
return constraints()->statistics_calculate();
}
TableStatistics SystemDictionary::protection_domain_cache_statistics() {
return pd_cache_table()->statistics_calculate();
}
// Utility for dumping dictionaries. // Utility for dumping dictionaries.
SystemDictionaryDCmd::SystemDictionaryDCmd(outputStream* output, bool heap) : SystemDictionaryDCmd::SystemDictionaryDCmd(outputStream* output, bool heap) :
DCmdWithParser(output, heap), DCmdWithParser(output, heap),

View File

@ -681,6 +681,11 @@ private:
static oop _java_platform_loader; static oop _java_platform_loader;
static bool _has_checkPackageAccess; static bool _has_checkPackageAccess;
public:
static TableStatistics placeholders_statistics();
static TableStatistics loader_constraints_statistics();
static TableStatistics protection_domain_cache_statistics();
}; };
#endif // SHARE_CLASSFILE_SYSTEMDICTIONARY_HPP #endif // SHARE_CLASSFILE_SYSTEMDICTIONARY_HPP

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
Copyright (c) 2012, 2018, Oracle and/or its affiliates. All rights reserved. Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved.
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
This code is free software; you can redistribute it and/or modify it This code is free software; you can redistribute it and/or modify it
@ -692,6 +692,66 @@
description="Total size of all allocated metaspace blocks for unsafe anonymous classes (each chunk has several blocks)" /> description="Total size of all allocated metaspace blocks for unsafe anonymous classes (each chunk has several blocks)" />
</Event> </Event>
<Event name="SymbolTableStatistics" category="Java Virtual Machine, Runtime, Tables" label="Symbol Table Statistics" period="everyChunk">
<Field type="ulong" name="bucketCount" label="Bucket Count" description="Number of buckets" />
<Field type="ulong" name="entryCount" label="Entry Count" description="Number of all entries" />
<Field type="ulong" contentType="bytes" name="totalFootprint" label="Total Footprint" description="Total memory footprint (the table itself plus all of the entries)" />
<Field type="ulong" name="bucketCountMaximum" label="Maximum Bucket Count" description="The maximum bucket length (entries in a single bucket)" />
<Field type="float" name="bucketCountAverage" label="Average Bucket Count" description="The average bucket length" />
<Field type="float" name="bucketCountVariance" label="Bucket Count Variance" description="How far bucket lengths are spread out from their average value" />
<Field type="float" name="bucketCountStandardDeviation" label="Bucket Count Standard Deviation" description="How far bucket lengths are spread out from their mean (expected) value" />
<Field type="float" name="insertionRate" label="Insertion Rate" description="How many items were added since last event (per second)" />
<Field type="float" name="removalRate" label="Removal Rate" description="How many items were removed since last event (per second)" />
</Event>
<Event name="StringTableStatistics" category="Java Virtual Machine, Runtime, Tables" label="String Table Statistics" period="everyChunk">
<Field type="ulong" name="bucketCount" label="Bucket Count" description="Number of buckets" />
<Field type="ulong" name="entryCount" label="Entry Count" description="Number of all entries" />
<Field type="ulong" contentType="bytes" name="totalFootprint" label="Total Footprint" description="Total memory footprint (the table itself plus all of the entries)" />
<Field type="ulong" name="bucketCountMaximum" label="Maximum Bucket Count" description="The maximum bucket length (entries in a single bucket)" />
<Field type="float" name="bucketCountAverage" label="Average Bucket Count" description="The average bucket length" />
<Field type="float" name="bucketCountVariance" label="Bucket Count Variance" description="How far bucket lengths are spread out from their average value" />
<Field type="float" name="bucketCountStandardDeviation" label="Bucket Count Standard Deviation" description="How far bucket lengths are spread out from their mean (expected) value" />
<Field type="float" name="insertionRate" label="Insertion Rate" description="How many items were added since last event (per second)" />
<Field type="float" name="removalRate" label="Removal Rate" description="How many items were removed since last event (per second)" />
</Event>
<Event name="PlaceholderTableStatistics" category="Java Virtual Machine, Runtime, Tables" label="Placeholder Table Statistics" period="everyChunk">
<Field type="ulong" name="bucketCount" label="Bucket Count" description="Number of buckets" />
<Field type="ulong" name="entryCount" label="Entry Count" description="Number of all entries" />
<Field type="ulong" contentType="bytes" name="totalFootprint" label="Total Footprint" description="Total memory footprint (the table itself plus all of the entries)" />
<Field type="ulong" name="bucketCountMaximum" label="Maximum Bucket Count" description="The maximum bucket length (entries in a single bucket)" />
<Field type="float" name="bucketCountAverage" label="Average Bucket Count" description="The average bucket length" />
<Field type="float" name="bucketCountVariance" label="Bucket Count Variance" description="How far bucket lengths are spread out from their average value" />
<Field type="float" name="bucketCountStandardDeviation" label="Bucket Count Standard Deviation" description="How far bucket lengths are spread out from their mean (expected) value" />
<Field type="float" name="insertionRate" label="Insertion Rate" description="How many items were added since last event (per second)" />
<Field type="float" name="removalRate" label="Removal Rate" description="How many items were removed since last event (per second)" />
</Event>
<Event name="LoaderConstraintsTableStatistics" category="Java Virtual Machine, Runtime, Tables" label="Loader Constraints Table Statistics" period="everyChunk">
<Field type="ulong" name="bucketCount" label="Bucket Count" />
<Field type="ulong" name="entryCount" label="Entry Count" description="Number of all entries" />
<Field type="ulong" contentType="bytes" name="totalFootprint" label="Total Footprint" description="Total memory footprint (the table itself plus all of the entries)" />
<Field type="ulong" name="bucketCountMaximum" label="Maximum Bucket Count" description="The maximum bucket length (entries in a single bucket)" />
<Field type="float" name="bucketCountAverage" label="Average Bucket Count" description="The average bucket length" />
<Field type="float" name="bucketCountVariance" label="Bucket Count Variance" description="How far bucket lengths are spread out from their average value" />
<Field type="float" name="bucketCountStandardDeviation" label="Bucket Count Standard Deviation" description="How far bucket lengths are spread out from their mean (expected) value" />
<Field type="float" name="insertionRate" label="Insertion Rate" description="How many items were added since last event (per second)" />
<Field type="float" name="removalRate" label="Removal Rate" description="How many items were removed since last event (per second)" />
</Event>
<Event name="ProtectionDomainCacheTableStatistics" category="Java Virtual Machine, Runtime, Tables" label="Protection Domain Cache Table Statistics" period="everyChunk">
<Field type="ulong" name="bucketCount" label="Bucket Count" description="Number of buckets" />
<Field type="ulong" name="entryCount" label="Entry Count" description="Number of all entries" />
<Field type="ulong" contentType="bytes" name="totalFootprint" label="Total Footprint" description="Total memory footprint (the table itself plus all of the entries)" />
<Field type="ulong" name="bucketCountMaximum" label="Maximum Bucket Count" description="The maximum bucket length (entries in a single bucket)" />
<Field type="float" name="bucketCountAverage" label="Average Bucket Count" description="The average bucket length" />
<Field type="float" name="bucketCountVariance" label="Bucket Count Variance" description="How far bucket lengths are spread out from their average value" />
<Field type="float" name="bucketCountStandardDeviation" label="Bucket Count Standard Deviation" description="How far bucket lengths are spread out from their mean (expected) value" />
<Field type="float" name="insertionRate" label="Insertion Rate" description="How many items were added since last event (per second)" />
<Field type="float" name="removalRate" label="Removal Rate" description="How many items were removed since last event (per second)" />
</Event>
<Event name="ThreadAllocationStatistics" category="Java Application, Statistics" label="Thread Allocation Statistics" period="everyChunk"> <Event name="ThreadAllocationStatistics" category="Java Application, Statistics" label="Thread Allocation Statistics" period="everyChunk">
<Field type="ulong" contentType="bytes" name="allocated" label="Allocated" description="Approximate number of bytes allocated since thread start" /> <Field type="ulong" contentType="bytes" name="allocated" label="Allocated" description="Approximate number of bytes allocated since thread start" />
<Field type="Thread" name="thread" label="Thread" /> <Field type="Thread" name="thread" label="Thread" />

View File

@ -27,6 +27,9 @@
#include "classfile/classLoaderDataGraph.hpp" #include "classfile/classLoaderDataGraph.hpp"
#include "classfile/classLoaderStats.hpp" #include "classfile/classLoaderStats.hpp"
#include "classfile/javaClasses.hpp" #include "classfile/javaClasses.hpp"
#include "classfile/stringTable.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/systemDictionary.hpp"
#include "code/codeCache.hpp" #include "code/codeCache.hpp"
#include "compiler/compileBroker.hpp" #include "compiler/compileBroker.hpp"
#include "gc/g1/g1HeapRegionEventSender.hpp" #include "gc/g1/g1HeapRegionEventSender.hpp"
@ -506,6 +509,46 @@ TRACE_REQUEST_FUNC(ClassLoaderStatistics) {
VMThread::execute(&op); VMThread::execute(&op);
} }
template<typename EVENT>
static void emit_table_statistics(TableStatistics statistics) {
EVENT event;
event.set_bucketCount(statistics._number_of_buckets);
event.set_entryCount(statistics._number_of_entries);
event.set_totalFootprint(statistics._total_footprint);
event.set_bucketCountMaximum(statistics._maximum_bucket_size);
event.set_bucketCountAverage(statistics._average_bucket_size);
event.set_bucketCountVariance(statistics._variance_of_bucket_size);
event.set_bucketCountStandardDeviation(statistics._stddev_of_bucket_size);
event.set_insertionRate(statistics._add_rate);
event.set_removalRate(statistics._remove_rate);
event.commit();
}
TRACE_REQUEST_FUNC(SymbolTableStatistics) {
TableStatistics statistics = SymbolTable::the_table()->get_table_statistics();
emit_table_statistics<EventSymbolTableStatistics>(statistics);
}
TRACE_REQUEST_FUNC(StringTableStatistics) {
TableStatistics statistics = StringTable::the_table()->get_table_statistics();
emit_table_statistics<EventStringTableStatistics>(statistics);
}
TRACE_REQUEST_FUNC(PlaceholderTableStatistics) {
TableStatistics statistics = SystemDictionary::placeholders_statistics();
emit_table_statistics<EventPlaceholderTableStatistics>(statistics);
}
TRACE_REQUEST_FUNC(LoaderConstraintsTableStatistics) {
TableStatistics statistics = SystemDictionary::loader_constraints_statistics();
emit_table_statistics<EventLoaderConstraintsTableStatistics>(statistics);
}
TRACE_REQUEST_FUNC(ProtectionDomainCacheTableStatistics) {
TableStatistics statistics = SystemDictionary::protection_domain_cache_statistics();
emit_table_statistics<EventProtectionDomainCacheTableStatistics>(statistics);
}
TRACE_REQUEST_FUNC(CompilerStatistics) { TRACE_REQUEST_FUNC(CompilerStatistics) {
EventCompilerStatistics event; EventCompilerStatistics event;
event.set_compileCount(CompileBroker::get_total_compile_count()); event.set_compileCount(CompileBroker::get_total_compile_count());

View File

@ -130,6 +130,7 @@ class AllocatedObj {
f(mtTest, "Test") /* Test type for verifying NMT */ \ f(mtTest, "Test") /* Test type for verifying NMT */ \
f(mtTracing, "Tracing") \ f(mtTracing, "Tracing") \
f(mtLogging, "Logging") \ f(mtLogging, "Logging") \
f(mtStatistics, "Statistics") \
f(mtArguments, "Arguments") \ f(mtArguments, "Arguments") \
f(mtModule, "Module") \ f(mtModule, "Module") \
f(mtSafepoint, "Safepoint") \ f(mtSafepoint, "Safepoint") \

View File

@ -28,6 +28,7 @@
#include "memory/allocation.hpp" #include "memory/allocation.hpp"
#include "utilities/globalCounter.hpp" #include "utilities/globalCounter.hpp"
#include "utilities/globalDefinitions.hpp" #include "utilities/globalDefinitions.hpp"
#include "utilities/tableStatistics.hpp"
// A mostly concurrent-hash-table where the read-side is wait-free, inserts are // A mostly concurrent-hash-table where the read-side is wait-free, inserts are
// CAS and deletes mutual exclude each other on per bucket-basis. VALUE is the // CAS and deletes mutual exclude each other on per bucket-basis. VALUE is the
@ -380,6 +381,8 @@ class ConcurrentHashTable : public CHeapObj<F> {
~ConcurrentHashTable(); ~ConcurrentHashTable();
TableRateStatistics _stats_rate;
size_t get_size_log2(Thread* thread); size_t get_size_log2(Thread* thread);
size_t get_node_size() const { return sizeof(Node); } size_t get_node_size() const { return sizeof(Node); }
bool is_max_size_reached() { return _size_limit_reached; } bool is_max_size_reached() { return _size_limit_reached; }
@ -454,6 +457,15 @@ class ConcurrentHashTable : public CHeapObj<F> {
template <typename EVALUATE_FUNC, typename DELETE_FUNC> template <typename EVALUATE_FUNC, typename DELETE_FUNC>
void bulk_delete(Thread* thread, EVALUATE_FUNC& eval_f, DELETE_FUNC& del_f); void bulk_delete(Thread* thread, EVALUATE_FUNC& eval_f, DELETE_FUNC& del_f);
// Calcuate statistics. Item sizes are calculated with VALUE_SIZE_FUNC.
template <typename VALUE_SIZE_FUNC>
TableStatistics statistics_calculate(Thread* thread, VALUE_SIZE_FUNC& vs_f);
// Gets statistics if available, if not return old one. Item sizes are calculated with
// VALUE_SIZE_FUNC.
template <typename VALUE_SIZE_FUNC>
TableStatistics statistics_get(Thread* thread, VALUE_SIZE_FUNC& vs_f, TableStatistics old);
// Writes statistics to the outputStream. Item sizes are calculated with // Writes statistics to the outputStream. Item sizes are calculated with
// VALUE_SIZE_FUNC. // VALUE_SIZE_FUNC.
template <typename VALUE_SIZE_FUNC> template <typename VALUE_SIZE_FUNC>

View File

@ -485,6 +485,7 @@ inline bool ConcurrentHashTable<VALUE, CONFIG, F>::
GlobalCounter::write_synchronize(); GlobalCounter::write_synchronize();
delete_f(rem_n->value()); delete_f(rem_n->value());
Node::destroy_node(rem_n); Node::destroy_node(rem_n);
JFR_ONLY(_stats_rate.remove();)
return true; return true;
} }
@ -533,6 +534,7 @@ inline void ConcurrentHashTable<VALUE, CONFIG, F>::
for (size_t node_it = 0; node_it < nd; node_it++) { for (size_t node_it = 0; node_it < nd; node_it++) {
del_f(ndel[node_it]->value()); del_f(ndel[node_it]->value());
Node::destroy_node(ndel[node_it]); Node::destroy_node(ndel[node_it]);
JFR_ONLY(_stats_rate.remove();)
DEBUG_ONLY(ndel[node_it] = (Node*)POISON_PTR;) DEBUG_ONLY(ndel[node_it] = (Node*)POISON_PTR;)
} }
cs_context = GlobalCounter::critical_section_begin(thread); cs_context = GlobalCounter::critical_section_begin(thread);
@ -571,6 +573,7 @@ inline void ConcurrentHashTable<VALUE, CONFIG, F>::
GlobalCounter::write_synchronize(); GlobalCounter::write_synchronize();
for (size_t node_it = 0; node_it < dels; node_it++) { for (size_t node_it = 0; node_it < dels; node_it++) {
Node::destroy_node(ndel[node_it]); Node::destroy_node(ndel[node_it]);
JFR_ONLY(_stats_rate.remove();)
DEBUG_ONLY(ndel[node_it] = (Node*)POISON_PTR;) DEBUG_ONLY(ndel[node_it] = (Node*)POISON_PTR;)
} }
} }
@ -900,6 +903,7 @@ inline bool ConcurrentHashTable<VALUE, CONFIG, F>::
if (old == NULL) { if (old == NULL) {
new_node->set_next(first_at_start); new_node->set_next(first_at_start);
if (bucket->cas_first(new_node, first_at_start)) { if (bucket->cas_first(new_node, first_at_start)) {
JFR_ONLY(_stats_rate.add();)
new_node = NULL; new_node = NULL;
ret = true; ret = true;
break; /* leave critical section */ break; /* leave critical section */
@ -1008,6 +1012,7 @@ inline ConcurrentHashTable<VALUE, CONFIG, F>::
_size_limit_reached(false), _resize_lock_owner(NULL), _size_limit_reached(false), _resize_lock_owner(NULL),
_invisible_epoch(0) _invisible_epoch(0)
{ {
_stats_rate = TableRateStatistics();
_resize_lock = _resize_lock =
new Mutex(Mutex::leaf, "ConcurrentHashTable", false, new Mutex(Mutex::leaf, "ConcurrentHashTable", false,
Monitor::_safepoint_check_never); Monitor::_safepoint_check_never);
@ -1081,6 +1086,7 @@ inline bool ConcurrentHashTable<VALUE, CONFIG, F>::
if (!bucket->cas_first(new_node, bucket->first())) { if (!bucket->cas_first(new_node, bucket->first())) {
assert(false, "bad"); assert(false, "bad");
} }
JFR_ONLY(_stats_rate.add();)
return true; return true;
} }
@ -1182,24 +1188,18 @@ inline void ConcurrentHashTable<VALUE, CONFIG, F>::
template <typename VALUE, typename CONFIG, MEMFLAGS F> template <typename VALUE, typename CONFIG, MEMFLAGS F>
template <typename VALUE_SIZE_FUNC> template <typename VALUE_SIZE_FUNC>
inline void ConcurrentHashTable<VALUE, CONFIG, F>:: inline TableStatistics ConcurrentHashTable<VALUE, CONFIG, F>::
statistics_to(Thread* thread, VALUE_SIZE_FUNC& vs_f, statistics_calculate(Thread* thread, VALUE_SIZE_FUNC& vs_f)
outputStream* st, const char* table_name)
{ {
NumberSeq summary; NumberSeq summary;
size_t literal_bytes = 0; size_t literal_bytes = 0;
if (!try_resize_lock(thread)) {
st->print_cr("statistics unavailable at this moment");
return;
}
InternalTable* table = get_table(); InternalTable* table = get_table();
for (size_t bucket_it = 0; bucket_it < table->_size; bucket_it++) { for (size_t bucket_it = 0; bucket_it < table->_size; bucket_it++) {
ScopedCS cs(thread, this); ScopedCS cs(thread, this);
size_t count = 0; size_t count = 0;
Bucket* bucket = table->get_bucket(bucket_it); Bucket* bucket = table->get_bucket(bucket_it);
if (bucket->have_redirect() || bucket->is_locked()) { if (bucket->have_redirect() || bucket->is_locked()) {
continue; continue;
} }
Node* current_node = bucket->first(); Node* current_node = bucket->first();
while (current_node != NULL) { while (current_node != NULL) {
@ -1210,37 +1210,39 @@ inline void ConcurrentHashTable<VALUE, CONFIG, F>::
summary.add((double)count); summary.add((double)count);
} }
double num_buckets = summary.num(); return TableStatistics(_stats_rate, summary, literal_bytes, sizeof(Bucket), sizeof(Node));
double num_entries = summary.sum(); }
size_t bucket_bytes = num_buckets * sizeof(Bucket); template <typename VALUE, typename CONFIG, MEMFLAGS F>
size_t entry_bytes = num_entries * sizeof(Node); template <typename VALUE_SIZE_FUNC>
size_t total_bytes = literal_bytes + bucket_bytes + entry_bytes; inline TableStatistics ConcurrentHashTable<VALUE, CONFIG, F>::
statistics_get(Thread* thread, VALUE_SIZE_FUNC& vs_f, TableStatistics old)
size_t bucket_size = (num_buckets <= 0) ? 0 : (bucket_bytes / num_buckets); {
size_t entry_size = (num_entries <= 0) ? 0 : (entry_bytes / num_entries); if (!try_resize_lock(thread)) {
return old;
st->print_cr("%s statistics:", table_name);
st->print_cr("Number of buckets : %9" PRIuPTR " = %9" PRIuPTR
" bytes, each " SIZE_FORMAT,
(size_t)num_buckets, bucket_bytes, bucket_size);
st->print_cr("Number of entries : %9" PRIuPTR " = %9" PRIuPTR
" bytes, each " SIZE_FORMAT,
(size_t)num_entries, entry_bytes, entry_size);
if (literal_bytes != 0) {
double literal_avg = (num_entries <= 0) ? 0 : (literal_bytes / num_entries);
st->print_cr("Number of literals : %9" PRIuPTR " = %9" PRIuPTR
" bytes, avg %7.3f",
(size_t)num_entries, literal_bytes, literal_avg);
} }
st->print_cr("Total footprsize_t : %9s = %9" PRIuPTR " bytes", ""
, total_bytes); TableStatistics ts = statistics_calculate(thread, vs_f);
st->print_cr("Average bucket size : %9.3f", summary.avg());
st->print_cr("Variance of bucket size : %9.3f", summary.variance());
st->print_cr("Std. dev. of bucket size: %9.3f", summary.sd());
st->print_cr("Maximum bucket size : %9" PRIuPTR,
(size_t)summary.maximum());
unlock_resize_lock(thread); unlock_resize_lock(thread);
return ts;
}
template <typename VALUE, typename CONFIG, MEMFLAGS F>
template <typename VALUE_SIZE_FUNC>
inline void ConcurrentHashTable<VALUE, CONFIG, F>::
statistics_to(Thread* thread, VALUE_SIZE_FUNC& vs_f,
outputStream* st, const char* table_name)
{
if (!try_resize_lock(thread)) {
st->print_cr("statistics unavailable at this moment");
return;
}
TableStatistics ts = statistics_calculate(thread, vs_f);
unlock_resize_lock(thread);
ts.print(st, table_name);
} }
template <typename VALUE, typename CONFIG, MEMFLAGS F> template <typename VALUE, typename CONFIG, MEMFLAGS F>

View File

@ -191,18 +191,7 @@ template <MEMFLAGS F> bool BasicHashtable<F>::maybe_grow(int max_size, int load_
} }
} }
// Dump footprint and bucket length statistics template <class T, MEMFLAGS F> TableStatistics Hashtable<T, F>::statistics_calculate(T (*literal_load_barrier)(HashtableEntry<T, F>*)) {
//
// Note: if you create a new subclass of Hashtable<MyNewType, F>, you will need to
// add a new function static int literal_size(MyNewType lit)
// because I can't get template <class T> int literal_size(T) to pick the specializations for Symbol and oop.
//
// The StringTable and SymbolTable dumping print how much footprint is used by the String and Symbol
// literals.
template <class T, MEMFLAGS F> void Hashtable<T, F>::print_table_statistics(outputStream* st,
const char *table_name,
T (*literal_load_barrier)(HashtableEntry<T, F>*)) {
NumberSeq summary; NumberSeq summary;
int literal_bytes = 0; int literal_bytes = 0;
for (int i = 0; i < this->table_size(); ++i) { for (int i = 0; i < this->table_size(); ++i) {
@ -215,28 +204,19 @@ template <class T, MEMFLAGS F> void Hashtable<T, F>::print_table_statistics(outp
} }
summary.add((double)count); summary.add((double)count);
} }
double num_buckets = summary.num(); return TableStatistics(this->_stats_rate, summary, literal_bytes, sizeof(HashtableBucket<F>), sizeof(HashtableEntry<T, F>));
double num_entries = summary.sum(); }
int bucket_bytes = (int)num_buckets * sizeof(HashtableBucket<F>); // Dump footprint and bucket length statistics
int entry_bytes = (int)num_entries * sizeof(HashtableEntry<T, F>); //
int total_bytes = literal_bytes + bucket_bytes + entry_bytes; // Note: if you create a new subclass of Hashtable<MyNewType, F>, you will need to
// add a new function static int literal_size(MyNewType lit)
int bucket_size = (num_buckets <= 0) ? 0 : (bucket_bytes / num_buckets); // because I can't get template <class T> int literal_size(T) to pick the specializations for Symbol and oop.
int entry_size = (num_entries <= 0) ? 0 : (entry_bytes / num_entries); template <class T, MEMFLAGS F> void Hashtable<T, F>::print_table_statistics(outputStream* st,
const char *table_name,
st->print_cr("%s statistics:", table_name); T (*literal_load_barrier)(HashtableEntry<T, F>*)) {
st->print_cr("Number of buckets : %9d = %9d bytes, each %d", (int)num_buckets, bucket_bytes, bucket_size); TableStatistics ts = statistics_calculate(literal_load_barrier);
st->print_cr("Number of entries : %9d = %9d bytes, each %d", (int)num_entries, entry_bytes, entry_size); ts.print(st, table_name);
if (literal_bytes != 0) {
double literal_avg = (num_entries <= 0) ? 0 : (literal_bytes / num_entries);
st->print_cr("Number of literals : %9d = %9d bytes, avg %7.3f", (int)num_entries, literal_bytes, literal_avg);
}
st->print_cr("Total footprint : %9s = %9d bytes", "", total_bytes);
st->print_cr("Average bucket size : %9.3f", summary.avg());
st->print_cr("Variance of bucket size : %9.3f", summary.variance());
st->print_cr("Std. dev. of bucket size: %9.3f", summary.sd());
st->print_cr("Maximum bucket size : %9d", (int)summary.maximum());
} }
#ifndef PRODUCT #ifndef PRODUCT

View File

@ -30,6 +30,7 @@
#include "oops/symbol.hpp" #include "oops/symbol.hpp"
#include "runtime/handles.hpp" #include "runtime/handles.hpp"
#include "utilities/growableArray.hpp" #include "utilities/growableArray.hpp"
#include "utilities/tableStatistics.hpp"
// This is a generic hashtable, designed to be used for the symbol // This is a generic hashtable, designed to be used for the symbol
// and string tables. // and string tables.
@ -168,6 +169,8 @@ private:
protected: protected:
TableRateStatistics _stats_rate;
void initialize(int table_size, int entry_size, int number_of_entries); void initialize(int table_size, int entry_size, int number_of_entries);
// Accessor // Accessor
@ -245,6 +248,7 @@ public:
return this->hash_to_index(compute_hash(name)); return this->hash_to_index(compute_hash(name));
} }
TableStatistics statistics_calculate(T (*literal_load_barrier)(HashtableEntry<T, F>*) = NULL);
void print_table_statistics(outputStream* st, const char *table_name, T (*literal_load_barrier)(HashtableEntry<T, F>*) = NULL); void print_table_statistics(outputStream* st, const char *table_name, T (*literal_load_barrier)(HashtableEntry<T, F>*) = NULL);
protected: protected:

View File

@ -43,6 +43,7 @@ template <MEMFLAGS F> inline BasicHashtable<F>::BasicHashtable(int table_size, i
for (int index = 0; index < _table_size; index++) { for (int index = 0; index < _table_size; index++) {
_buckets[index].clear(); _buckets[index].clear();
} }
_stats_rate = TableRateStatistics();
} }
@ -52,6 +53,7 @@ template <MEMFLAGS F> inline BasicHashtable<F>::BasicHashtable(int table_size, i
// Called on startup, no locking needed // Called on startup, no locking needed
initialize(table_size, entry_size, number_of_entries); initialize(table_size, entry_size, number_of_entries);
_buckets = buckets; _buckets = buckets;
_stats_rate = TableRateStatistics();
} }
template <MEMFLAGS F> inline BasicHashtable<F>::~BasicHashtable() { template <MEMFLAGS F> inline BasicHashtable<F>::~BasicHashtable() {
@ -101,6 +103,11 @@ template <MEMFLAGS F> inline BasicHashtableEntry<F>* HashtableBucket<F>::get_ent
template <MEMFLAGS F> inline void BasicHashtable<F>::set_entry(int index, BasicHashtableEntry<F>* entry) { template <MEMFLAGS F> inline void BasicHashtable<F>::set_entry(int index, BasicHashtableEntry<F>* entry) {
_buckets[index].set_entry(entry); _buckets[index].set_entry(entry);
if (entry != NULL) {
JFR_ONLY(_stats_rate.add();)
} else {
JFR_ONLY(_stats_rate.remove();)
}
} }
@ -108,12 +115,14 @@ template <MEMFLAGS F> inline void BasicHashtable<F>::add_entry(int index, BasicH
entry->set_next(bucket(index)); entry->set_next(bucket(index));
_buckets[index].set_entry(entry); _buckets[index].set_entry(entry);
++_number_of_entries; ++_number_of_entries;
JFR_ONLY(_stats_rate.add();)
} }
template <MEMFLAGS F> inline void BasicHashtable<F>::free_entry(BasicHashtableEntry<F>* entry) { template <MEMFLAGS F> inline void BasicHashtable<F>::free_entry(BasicHashtableEntry<F>* entry) {
entry->set_next(_free_list); entry->set_next(_free_list);
_free_list = entry; _free_list = entry;
--_number_of_entries; --_number_of_entries;
JFR_ONLY(_stats_rate.remove();)
} }
#endif // SHARE_UTILITIES_HASHTABLE_INLINE_HPP #endif // SHARE_UTILITIES_HASHTABLE_INLINE_HPP

View File

@ -0,0 +1,140 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "precompiled.hpp"
#include "jfr/jfr.hpp"
#include "runtime/atomic.hpp"
#include "runtime/os.hpp"
#include "utilities/debug.hpp"
#include "utilities/tableStatistics.hpp"
TableRateStatistics::TableRateStatistics() :
_added_items(0), _removed_items(0),
_time_stamp(0), _seconds_stamp(0),
_added_items_stamp(0), _added_items_stamp_prev(0),
_removed_items_stamp(0), _removed_items_stamp_prev(0) {}
TableRateStatistics::~TableRateStatistics() { };
void TableRateStatistics::add() {
if (Jfr::is_recording()) {
Atomic::inc(&_added_items);
}
}
void TableRateStatistics::remove() {
if (Jfr::is_recording()) {
Atomic::inc(&_removed_items);
}
}
void TableRateStatistics::stamp() {
jlong now = os::javaTimeNanos();
_added_items_stamp_prev = _added_items_stamp;
_removed_items_stamp_prev = _removed_items_stamp;
_added_items_stamp = _added_items;
_removed_items_stamp = _removed_items;
if (_time_stamp == 0) {
_time_stamp = now - 1000000000;
}
jlong diff = (now - _time_stamp);
_seconds_stamp = (float)diff / 1000000000.0;
_time_stamp = now;
}
float TableRateStatistics::get_add_rate() {
return (float)((_added_items_stamp - _added_items_stamp_prev) / _seconds_stamp);
}
float TableRateStatistics::get_remove_rate() {
return (float)((_removed_items_stamp - _removed_items_stamp_prev) / _seconds_stamp);
}
TableStatistics::TableStatistics() :
_literal_bytes(0),
_number_of_buckets(0), _number_of_entries(0),
_maximum_bucket_size(0), _average_bucket_size(0),
_variance_of_bucket_size(0), _stddev_of_bucket_size(0),
_bucket_bytes(0), _entry_bytes(0), _total_footprint(0),
_bucket_size(0), _entry_size(0),
_add_rate(0), _remove_rate(0) {
}
TableStatistics::TableStatistics(TableRateStatistics& rate_stats, NumberSeq summary, size_t literal_bytes, size_t bucket_bytes, size_t node_bytes) :
_literal_bytes(literal_bytes),
_number_of_buckets(0), _number_of_entries(0),
_maximum_bucket_size(0), _average_bucket_size(0),
_variance_of_bucket_size(0), _stddev_of_bucket_size(0),
_bucket_bytes(0), _entry_bytes(0), _total_footprint(0),
_bucket_size(0), _entry_size(0),
_add_rate(0), _remove_rate(0) {
_number_of_buckets = summary.num();
_number_of_entries = summary.sum();
_maximum_bucket_size = summary.maximum();
_average_bucket_size = summary.avg();
_variance_of_bucket_size = summary.variance();
_stddev_of_bucket_size = summary.sd();
_bucket_bytes = _number_of_buckets * bucket_bytes;
_entry_bytes = _number_of_entries * node_bytes;
_total_footprint = _literal_bytes + _bucket_bytes + _entry_bytes;
_bucket_size = (_number_of_buckets <= 0) ? 0 : (_bucket_bytes / _number_of_buckets);
_entry_size = (_number_of_entries <= 0) ? 0 : (_entry_bytes / _number_of_entries);
if (Jfr::is_recording()) {
rate_stats.stamp();
_add_rate = rate_stats.get_add_rate();
_remove_rate = rate_stats.get_remove_rate();
}
}
TableStatistics::~TableStatistics() { }
void TableStatistics::print(outputStream* st, const char *table_name) {
st->print_cr("%s statistics:", table_name);
st->print_cr("Number of buckets : %9" PRIuPTR " = %9" PRIuPTR
" bytes, each " SIZE_FORMAT,
_number_of_buckets, _bucket_bytes, _bucket_size);
st->print_cr("Number of entries : %9" PRIuPTR " = %9" PRIuPTR
" bytes, each " SIZE_FORMAT,
_number_of_entries, _entry_bytes, _entry_size);
if (_literal_bytes != 0) {
float literal_avg = (_number_of_entries <= 0) ? 0 : (_literal_bytes / _number_of_entries);
st->print_cr("Number of literals : %9" PRIuPTR " = %9" PRIuPTR
" bytes, avg %7.3f",
_number_of_entries, _literal_bytes, literal_avg);
}
st->print_cr("Total footprint : %9s = %9" PRIuPTR " bytes", "", _total_footprint);
st->print_cr("Average bucket size : %9.3f", _average_bucket_size);
st->print_cr("Variance of bucket size : %9.3f", _variance_of_bucket_size);
st->print_cr("Std. dev. of bucket size: %9.3f", _stddev_of_bucket_size);
st->print_cr("Maximum bucket size : %9" PRIuPTR, _maximum_bucket_size);
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_UTILITIES_TABLE_STATISTICS_HPP
#define SHARE_UTILITIES_TABLE_STATISTICS_HPP
#include "memory/allocation.hpp"
#include "utilities/debug.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/numberSeq.hpp"
class TableRateStatistics : CHeapObj<mtStatistics> {
friend class TableStatistics;
private:
volatile size_t _added_items;
volatile size_t _removed_items;
jlong _time_stamp;
double _seconds_stamp;
size_t _added_items_stamp;
size_t _added_items_stamp_prev;
size_t _removed_items_stamp;
size_t _removed_items_stamp_prev;
public:
TableRateStatistics();
~TableRateStatistics();
void add();
void remove();
protected:
void stamp();
float get_add_rate();
float get_remove_rate();
};
class TableStatistics : CHeapObj<mtStatistics> {
public:
size_t _literal_bytes;
size_t _number_of_buckets;
size_t _number_of_entries;
size_t _maximum_bucket_size;
float _average_bucket_size;
float _variance_of_bucket_size;
float _stddev_of_bucket_size;
size_t _bucket_bytes;
size_t _entry_bytes;
size_t _total_footprint;
size_t _bucket_size;
size_t _entry_size;
float _add_rate;
float _remove_rate;
TableStatistics();
TableStatistics(TableRateStatistics& rate_stats, NumberSeq summary, size_t literal_bytes, size_t bucket_bytes, size_t node_bytes);
~TableStatistics();
void print(outputStream* st, const char *table_name);
};
#endif // SHARE_UTILITIES_TABLE_STATISTICS_HPP

View File

@ -27,6 +27,31 @@
<setting name="period">1000 ms</setting> <setting name="period">1000 ms</setting>
</event> </event>
<event name="jdk.SymbolTableStatistics">
<setting name="enabled">true</setting>
<setting name="period">10 s</setting>
</event>
<event name="jdk.StringTableStatistics">
<setting name="enabled">true</setting>
<setting name="period">10 s</setting>
</event>
<event name="jdk.PlaceholderTableStatistics">
<setting name="enabled">true</setting>
<setting name="period">10 s</setting>
</event>
<event name="jdk.LoaderConstraintsTableStatistics">
<setting name="enabled">true</setting>
<setting name="period">10 s</setting>
</event>
<event name="jdk.ProtectionDomainCacheTableStatistics">
<setting name="enabled">true</setting>
<setting name="period">10 s</setting>
</event>
<event name="jdk.ThreadStart"> <event name="jdk.ThreadStart">
<setting name="enabled">true</setting> <setting name="enabled">true</setting>
</event> </event>

View File

@ -27,6 +27,31 @@
<setting name="period">1000 ms</setting> <setting name="period">1000 ms</setting>
</event> </event>
<event name="jdk.SymbolTableStatistics">
<setting name="enabled">true</setting>
<setting name="period">10 s</setting>
</event>
<event name="jdk.StringTableStatistics">
<setting name="enabled">true</setting>
<setting name="period">10 s</setting>
</event>
<event name="jdk.PlaceholderTableStatistics">
<setting name="enabled">true</setting>
<setting name="period">10 s</setting>
</event>
<event name="jdk.LoaderConstraintsTableStatistics">
<setting name="enabled">true</setting>
<setting name="period">10 s</setting>
</event>
<event name="jdk.ProtectionDomainCacheTableStatistics">
<setting name="enabled">true</setting>
<setting name="period">10 s</setting>
</event>
<event name="jdk.ThreadStart"> <event name="jdk.ThreadStart">
<setting name="enabled">true</setting> <setting name="enabled">true</setting>
</event> </event>

View File

@ -0,0 +1,89 @@
/*
* Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.jfr.event.runtime;
import java.util.List;
import java.util.stream.Collectors;
import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordedClass;
import jdk.jfr.consumer.RecordedClassLoader;
import jdk.jfr.consumer.RecordedEvent;
import jdk.test.lib.Asserts;
import jdk.test.lib.jfr.EventNames;
import jdk.test.lib.jfr.Events;
/**
* @test
* @key jfr
* @requires vm.hasJFR
* @library /test/lib /test/jdk
* @build jdk.jfr.event.runtime.TestClasses
* @run main/othervm jdk.jfr.event.runtime.TestTableStatisticsEvent
* @bug 8185525
*/
public final class TestTableStatisticsEvent {
public static void main(String[] args) throws Throwable {
try (Recording recording = new Recording()) {
recording.enable(EventNames.SymbolTableStatistics);
recording.enable(EventNames.StringTableStatistics);
recording.enable(EventNames.PlaceholderTableStatistics);
recording.enable(EventNames.LoaderConstraintsTableStatistics);
recording.enable(EventNames.ProtectionDomainCacheTableStatistics);
recording.start();
recording.stop();
List<RecordedEvent> events = Events.fromRecording(recording);
verifyTable(events, EventNames.SymbolTableStatistics);
verifyTable(events, EventNames.StringTableStatistics);
verifyTable(events, EventNames.PlaceholderTableStatistics);
verifyTable(events, EventNames.LoaderConstraintsTableStatistics);
verifyTable(events, EventNames.ProtectionDomainCacheTableStatistics);
}
}
private static void verifyTable(List<RecordedEvent> allEvents, String eventName) throws Exception {
List<RecordedEvent> eventsForTable = allEvents.stream().filter(e -> e.getEventType().getName().equals(eventName)).collect(Collectors.toList());
if (eventsForTable.isEmpty()) {
throw new Exception("No events for " + eventName);
}
for (RecordedEvent event : eventsForTable) {
Events.assertField(event, "bucketCount").atLeast(0L);
long entryCount = Events.assertField(event, "entryCount").atLeast(0L).getValue();
Events.assertField(event, "totalFootprint").atLeast(0L);
float averageBucketCount = Events.assertField(event, "bucketCountAverage").atLeast(0.0f).getValue();
Events.assertField(event, "bucketCountMaximum").atLeast((long)averageBucketCount);
Events.assertField(event, "bucketCountVariance").atLeast(0.0f);
Events.assertField(event, "bucketCountStandardDeviation").atLeast(0.0f);
float insertionRate = Events.assertField(event, "insertionRate").atLeast(0.0f).getValue();
float removalRate = Events.assertField(event, "removalRate").atLeast(0.0f).getValue();
if ((insertionRate > 0.0f) && (insertionRate > removalRate)) {
Asserts.assertGreaterThan(entryCount, 0L, "Entries marked as added, but no entries found for " + eventName);
}
}
}
}

View File

@ -82,6 +82,11 @@ public class EventNames {
public final static String BiasedLockRevocation = PREFIX + "BiasedLockRevocation"; public final static String BiasedLockRevocation = PREFIX + "BiasedLockRevocation";
public final static String BiasedLockSelfRevocation = PREFIX + "BiasedLockSelfRevocation"; public final static String BiasedLockSelfRevocation = PREFIX + "BiasedLockSelfRevocation";
public final static String BiasedLockClassRevocation = PREFIX + "BiasedLockClassRevocation"; public final static String BiasedLockClassRevocation = PREFIX + "BiasedLockClassRevocation";
public final static String SymbolTableStatistics = PREFIX + "SymbolTableStatistics";
public final static String StringTableStatistics = PREFIX + "StringTableStatistics";
public final static String PlaceholderTableStatistics = PREFIX + "PlaceholderTableStatistics";
public final static String LoaderConstraintsTableStatistics = PREFIX + "LoaderConstraintsTableStatistics";
public final static String ProtectionDomainCacheTableStatistics = PREFIX + "ProtectionDomainCacheTableStatistics";
// This event is hard to test // This event is hard to test
public final static String ReservedStackActivation = PREFIX + "ReservedStackActivation"; public final static String ReservedStackActivation = PREFIX + "ReservedStackActivation";