8185525: Add JFR event for DictionarySizes
Summary: Added TableStatistics event
Reviewed-by: egahlin, coleenp
--- a/src/hotspot/share/classfile/classLoaderDataGraph.cpp Wed May 08 20:57:12 2019 +0800
+++ b/src/hotspot/share/classfile/classLoaderDataGraph.cpp Wed May 08 11:11:50 2019 -0500
@@ -443,7 +443,7 @@
}
}
-void ClassLoaderDataGraph::print_dictionary_statistics(outputStream* st) {
+void ClassLoaderDataGraph::print_table_statistics(outputStream* st) {
FOR_ALL_DICTIONARY(cld) {
ResourceMark rm;
stringStream tempst;
--- a/src/hotspot/share/classfile/classLoaderDataGraph.hpp Wed May 08 20:57:12 2019 +0800
+++ b/src/hotspot/share/classfile/classLoaderDataGraph.hpp Wed May 08 11:11:50 2019 -0500
@@ -112,7 +112,7 @@
static void verify_dictionary();
static void print_dictionary(outputStream* st);
- static void print_dictionary_statistics(outputStream* st);
+ static void print_table_statistics(outputStream* st);
// CMS support.
static void remember_new_clds(bool remember) { _saved_head = (remember ? _head : NULL); }
--- a/src/hotspot/share/classfile/stringTable.cpp Wed May 08 20:57:12 2019 +0800
+++ b/src/hotspot/share/classfile/stringTable.cpp Wed May 08 11:11:50 2019 -0500
@@ -579,6 +579,13 @@
};
};
+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,
const char* table_name) {
SizeFunc sz;
--- a/src/hotspot/share/classfile/stringTable.hpp Wed May 08 20:57:12 2019 +0800
+++ b/src/hotspot/share/classfile/stringTable.hpp Wed May 08 11:11:50 2019 -0500
@@ -99,6 +99,7 @@
// The string table
static StringTable* the_table() { return _the_table; }
size_t table_size();
+ TableStatistics get_table_statistics();
static OopStorage* weak_storage() { return the_table()->_weak_handles; }
--- a/src/hotspot/share/classfile/symbolTable.cpp Wed May 08 20:57:12 2019 +0800
+++ b/src/hotspot/share/classfile/symbolTable.cpp Wed May 08 11:11:50 2019 -0500
@@ -503,6 +503,13 @@
};
};
+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,
const char* table_name) {
SizeFunc sz;
--- a/src/hotspot/share/classfile/symbolTable.hpp Wed May 08 20:57:12 2019 +0800
+++ b/src/hotspot/share/classfile/symbolTable.hpp Wed May 08 11:11:50 2019 -0500
@@ -171,6 +171,7 @@
// The symbol table
static SymbolTable* the_table() { return _the_table; }
size_t table_size();
+ TableStatistics get_table_statistics();
enum {
symbol_alloc_batch_size = 8,
--- a/src/hotspot/share/classfile/systemDictionary.cpp Wed May 08 20:57:12 2019 +0800
+++ b/src/hotspot/share/classfile/systemDictionary.cpp Wed May 08 11:11:50 2019 -0500
@@ -2849,13 +2849,25 @@
print_on(st);
} else {
CDS_ONLY(SystemDictionaryShared::print_table_statistics(st));
- ClassLoaderDataGraph::print_dictionary_statistics(st);
+ ClassLoaderDataGraph::print_table_statistics(st);
placeholders()->print_table_statistics(st, "Placeholder 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.
SystemDictionaryDCmd::SystemDictionaryDCmd(outputStream* output, bool heap) :
DCmdWithParser(output, heap),
--- a/src/hotspot/share/classfile/systemDictionary.hpp Wed May 08 20:57:12 2019 +0800
+++ b/src/hotspot/share/classfile/systemDictionary.hpp Wed May 08 11:11:50 2019 -0500
@@ -681,6 +681,11 @@
static oop _java_platform_loader;
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
--- a/src/hotspot/share/jfr/metadata/metadata.xml Wed May 08 20:57:12 2019 +0800
+++ b/src/hotspot/share/jfr/metadata/metadata.xml Wed May 08 11:11:50 2019 -0500
@@ -1,7 +1,7 @@
<?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.
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)" />
</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">
<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" />
--- a/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp Wed May 08 20:57:12 2019 +0800
+++ b/src/hotspot/share/jfr/periodic/jfrPeriodic.cpp Wed May 08 11:11:50 2019 -0500
@@ -27,6 +27,9 @@
#include "classfile/classLoaderDataGraph.hpp"
#include "classfile/classLoaderStats.hpp"
#include "classfile/javaClasses.hpp"
+#include "classfile/stringTable.hpp"
+#include "classfile/symbolTable.hpp"
+#include "classfile/systemDictionary.hpp"
#include "code/codeCache.hpp"
#include "compiler/compileBroker.hpp"
#include "gc/g1/g1HeapRegionEventSender.hpp"
@@ -506,6 +509,46 @@
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) {
EventCompilerStatistics event;
event.set_compileCount(CompileBroker::get_total_compile_count());
--- a/src/hotspot/share/memory/allocation.hpp Wed May 08 20:57:12 2019 +0800
+++ b/src/hotspot/share/memory/allocation.hpp Wed May 08 11:11:50 2019 -0500
@@ -130,6 +130,7 @@
f(mtTest, "Test") /* Test type for verifying NMT */ \
f(mtTracing, "Tracing") \
f(mtLogging, "Logging") \
+ f(mtStatistics, "Statistics") \
f(mtArguments, "Arguments") \
f(mtModule, "Module") \
f(mtSafepoint, "Safepoint") \
--- a/src/hotspot/share/utilities/concurrentHashTable.hpp Wed May 08 20:57:12 2019 +0800
+++ b/src/hotspot/share/utilities/concurrentHashTable.hpp Wed May 08 11:11:50 2019 -0500
@@ -28,6 +28,7 @@
#include "memory/allocation.hpp"
#include "utilities/globalCounter.hpp"
#include "utilities/globalDefinitions.hpp"
+#include "utilities/tableStatistics.hpp"
// 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
@@ -380,6 +381,8 @@
~ConcurrentHashTable();
+ TableRateStatistics _stats_rate;
+
size_t get_size_log2(Thread* thread);
size_t get_node_size() const { return sizeof(Node); }
bool is_max_size_reached() { return _size_limit_reached; }
@@ -454,6 +457,15 @@
template <typename EVALUATE_FUNC, typename DELETE_FUNC>
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
// VALUE_SIZE_FUNC.
template <typename VALUE_SIZE_FUNC>
--- a/src/hotspot/share/utilities/concurrentHashTable.inline.hpp Wed May 08 20:57:12 2019 +0800
+++ b/src/hotspot/share/utilities/concurrentHashTable.inline.hpp Wed May 08 11:11:50 2019 -0500
@@ -485,6 +485,7 @@
GlobalCounter::write_synchronize();
delete_f(rem_n->value());
Node::destroy_node(rem_n);
+ JFR_ONLY(_stats_rate.remove();)
return true;
}
@@ -533,6 +534,7 @@
for (size_t node_it = 0; node_it < nd; node_it++) {
del_f(ndel[node_it]->value());
Node::destroy_node(ndel[node_it]);
+ JFR_ONLY(_stats_rate.remove();)
DEBUG_ONLY(ndel[node_it] = (Node*)POISON_PTR;)
}
cs_context = GlobalCounter::critical_section_begin(thread);
@@ -571,6 +573,7 @@
GlobalCounter::write_synchronize();
for (size_t node_it = 0; node_it < dels; node_it++) {
Node::destroy_node(ndel[node_it]);
+ JFR_ONLY(_stats_rate.remove();)
DEBUG_ONLY(ndel[node_it] = (Node*)POISON_PTR;)
}
}
@@ -900,6 +903,7 @@
if (old == NULL) {
new_node->set_next(first_at_start);
if (bucket->cas_first(new_node, first_at_start)) {
+ JFR_ONLY(_stats_rate.add();)
new_node = NULL;
ret = true;
break; /* leave critical section */
@@ -1008,6 +1012,7 @@
_size_limit_reached(false), _resize_lock_owner(NULL),
_invisible_epoch(0)
{
+ _stats_rate = TableRateStatistics();
_resize_lock =
new Mutex(Mutex::leaf, "ConcurrentHashTable", false,
Monitor::_safepoint_check_never);
@@ -1081,6 +1086,7 @@
if (!bucket->cas_first(new_node, bucket->first())) {
assert(false, "bad");
}
+ JFR_ONLY(_stats_rate.add();)
return true;
}
@@ -1182,24 +1188,18 @@
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)
+inline TableStatistics ConcurrentHashTable<VALUE, CONFIG, F>::
+ statistics_calculate(Thread* thread, VALUE_SIZE_FUNC& vs_f)
{
NumberSeq summary;
size_t literal_bytes = 0;
- if (!try_resize_lock(thread)) {
- st->print_cr("statistics unavailable at this moment");
- return;
- }
-
InternalTable* table = get_table();
for (size_t bucket_it = 0; bucket_it < table->_size; bucket_it++) {
ScopedCS cs(thread, this);
size_t count = 0;
Bucket* bucket = table->get_bucket(bucket_it);
if (bucket->have_redirect() || bucket->is_locked()) {
- continue;
+ continue;
}
Node* current_node = bucket->first();
while (current_node != NULL) {
@@ -1210,37 +1210,39 @@
summary.add((double)count);
}
- double num_buckets = summary.num();
- double num_entries = summary.sum();
+ return TableStatistics(_stats_rate, summary, literal_bytes, sizeof(Bucket), sizeof(Node));
+}
- size_t bucket_bytes = num_buckets * sizeof(Bucket);
- size_t entry_bytes = num_entries * sizeof(Node);
- size_t total_bytes = literal_bytes + bucket_bytes + entry_bytes;
+template <typename VALUE, typename CONFIG, MEMFLAGS F>
+template <typename VALUE_SIZE_FUNC>
+inline TableStatistics ConcurrentHashTable<VALUE, CONFIG, F>::
+ statistics_get(Thread* thread, VALUE_SIZE_FUNC& vs_f, TableStatistics old)
+{
+ if (!try_resize_lock(thread)) {
+ return 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);
+ TableStatistics ts = statistics_calculate(thread, vs_f);
+ unlock_resize_lock(thread);
- 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);
+ 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;
}
- st->print_cr("Total footprsize_t : %9s = %9" PRIuPTR " 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 : %9" PRIuPTR,
- (size_t)summary.maximum());
+
+ TableStatistics ts = statistics_calculate(thread, vs_f);
unlock_resize_lock(thread);
+
+ ts.print(st, table_name);
}
template <typename VALUE, typename CONFIG, MEMFLAGS F>
--- a/src/hotspot/share/utilities/hashtable.cpp Wed May 08 20:57:12 2019 +0800
+++ b/src/hotspot/share/utilities/hashtable.cpp Wed May 08 11:11:50 2019 -0500
@@ -191,18 +191,7 @@
}
}
-// Dump footprint and bucket length statistics
-//
-// 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>*)) {
+template <class T, MEMFLAGS F> TableStatistics Hashtable<T, F>::statistics_calculate(T (*literal_load_barrier)(HashtableEntry<T, F>*)) {
NumberSeq summary;
int literal_bytes = 0;
for (int i = 0; i < this->table_size(); ++i) {
@@ -215,28 +204,19 @@
}
summary.add((double)count);
}
- double num_buckets = summary.num();
- double num_entries = summary.sum();
-
- int bucket_bytes = (int)num_buckets * sizeof(HashtableBucket<F>);
- int entry_bytes = (int)num_entries * sizeof(HashtableEntry<T, F>);
- int total_bytes = literal_bytes + bucket_bytes + entry_bytes;
-
- int bucket_size = (num_buckets <= 0) ? 0 : (bucket_bytes / num_buckets);
- int entry_size = (num_entries <= 0) ? 0 : (entry_bytes / num_entries);
+ return TableStatistics(this->_stats_rate, summary, literal_bytes, sizeof(HashtableBucket<F>), sizeof(HashtableEntry<T, F>));
+}
- st->print_cr("%s statistics:", table_name);
- st->print_cr("Number of buckets : %9d = %9d bytes, each %d", (int)num_buckets, bucket_bytes, bucket_size);
- st->print_cr("Number of entries : %9d = %9d bytes, each %d", (int)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 : %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());
+// Dump footprint and bucket length statistics
+//
+// 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.
+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>*)) {
+ TableStatistics ts = statistics_calculate(literal_load_barrier);
+ ts.print(st, table_name);
}
#ifndef PRODUCT
--- a/src/hotspot/share/utilities/hashtable.hpp Wed May 08 20:57:12 2019 +0800
+++ b/src/hotspot/share/utilities/hashtable.hpp Wed May 08 11:11:50 2019 -0500
@@ -30,6 +30,7 @@
#include "oops/symbol.hpp"
#include "runtime/handles.hpp"
#include "utilities/growableArray.hpp"
+#include "utilities/tableStatistics.hpp"
// This is a generic hashtable, designed to be used for the symbol
// and string tables.
@@ -168,6 +169,8 @@
protected:
+ TableRateStatistics _stats_rate;
+
void initialize(int table_size, int entry_size, int number_of_entries);
// Accessor
@@ -245,6 +248,7 @@
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);
protected:
--- a/src/hotspot/share/utilities/hashtable.inline.hpp Wed May 08 20:57:12 2019 +0800
+++ b/src/hotspot/share/utilities/hashtable.inline.hpp Wed May 08 11:11:50 2019 -0500
@@ -43,6 +43,7 @@
for (int index = 0; index < _table_size; index++) {
_buckets[index].clear();
}
+ _stats_rate = TableRateStatistics();
}
@@ -52,6 +53,7 @@
// Called on startup, no locking needed
initialize(table_size, entry_size, number_of_entries);
_buckets = buckets;
+ _stats_rate = TableRateStatistics();
}
template <MEMFLAGS F> inline BasicHashtable<F>::~BasicHashtable() {
@@ -101,6 +103,11 @@
template <MEMFLAGS F> inline void BasicHashtable<F>::set_entry(int index, BasicHashtableEntry<F>* entry) {
_buckets[index].set_entry(entry);
+ if (entry != NULL) {
+ JFR_ONLY(_stats_rate.add();)
+ } else {
+ JFR_ONLY(_stats_rate.remove();)
+ }
}
@@ -108,12 +115,14 @@
entry->set_next(bucket(index));
_buckets[index].set_entry(entry);
++_number_of_entries;
+ JFR_ONLY(_stats_rate.add();)
}
template <MEMFLAGS F> inline void BasicHashtable<F>::free_entry(BasicHashtableEntry<F>* entry) {
entry->set_next(_free_list);
_free_list = entry;
--_number_of_entries;
+ JFR_ONLY(_stats_rate.remove();)
}
#endif // SHARE_UTILITIES_HASHTABLE_INLINE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/utilities/tableStatistics.cpp Wed May 08 11:11:50 2019 -0500
@@ -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);
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hotspot/share/utilities/tableStatistics.hpp Wed May 08 11:11:50 2019 -0500
@@ -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
--- a/src/jdk.jfr/share/conf/jfr/default.jfc Wed May 08 20:57:12 2019 +0800
+++ b/src/jdk.jfr/share/conf/jfr/default.jfc Wed May 08 11:11:50 2019 -0500
@@ -27,6 +27,31 @@
<setting name="period">1000 ms</setting>
</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">
<setting name="enabled">true</setting>
</event>
--- a/src/jdk.jfr/share/conf/jfr/profile.jfc Wed May 08 20:57:12 2019 +0800
+++ b/src/jdk.jfr/share/conf/jfr/profile.jfc Wed May 08 11:11:50 2019 -0500
@@ -27,6 +27,31 @@
<setting name="period">1000 ms</setting>
</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">
<setting name="enabled">true</setting>
</event>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/jdk/jfr/event/runtime/TestTableStatisticsEvent.java Wed May 08 11:11:50 2019 -0500
@@ -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);
+ }
+ }
+ }
+}
--- a/test/lib/jdk/test/lib/jfr/EventNames.java Wed May 08 20:57:12 2019 +0800
+++ b/test/lib/jdk/test/lib/jfr/EventNames.java Wed May 08 11:11:50 2019 -0500
@@ -82,6 +82,11 @@
public final static String BiasedLockRevocation = PREFIX + "BiasedLockRevocation";
public final static String BiasedLockSelfRevocation = PREFIX + "BiasedLockSelfRevocation";
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
public final static String ReservedStackActivation = PREFIX + "ReservedStackActivation";