8266936: Add a finalization JFR event

Reviewed-by: coleenp, mchung, egahlin
This commit is contained in:
Markus Grönlund 2021-10-18 16:52:36 +00:00
parent bcbe3845d9
commit 72a976ef05
36 changed files with 1517 additions and 551 deletions

View File

@ -183,6 +183,7 @@ JVM_ReferenceRefersTo
JVM_RegisterLambdaProxyClassForArchiving
JVM_RegisterSignal
JVM_ReleaseUTF
JVM_ReportFinalizationComplete
JVM_ResumeThread
JVM_SetArrayElement
JVM_SetClassSigners

View File

@ -76,6 +76,7 @@
#include "runtime/signature.hpp"
#include "services/classLoadingService.hpp"
#include "services/diagnosticCommand.hpp"
#include "services/finalizerService.hpp"
#include "services/threadService.hpp"
#include "utilities/macros.hpp"
#include "utilities/utf8.hpp"
@ -1600,7 +1601,7 @@ bool SystemDictionary::do_unloading(GCTimer* gc_timer) {
if (unloading_occurred) {
MutexLocker ml2(is_concurrent ? Module_lock : NULL);
JFR_ONLY(Jfr::on_unloading_classes();)
MANAGEMENT_ONLY(FinalizerService::purge_unloaded();)
MutexLocker ml1(is_concurrent ? SystemDictionary_lock : NULL);
ClassLoaderDataGraph::clean_module_and_package_info();
constraints()->purge_loader_constraints();

View File

@ -753,6 +753,12 @@ JVM_AssertionStatusDirectives(JNIEnv *env, jclass unused);
JNIEXPORT jboolean JNICALL
JVM_SupportsCX8(void);
/*
* java.lang.ref.Finalizer
*/
JNIEXPORT void JNICALL
JVM_ReportFinalizationComplete(JNIEnv *env, jobject finalizee);
/*************************************************************************
PART 2: Support for the Verifier and Class File Format Checker
************************************************************************/

View File

@ -383,7 +383,7 @@ static void read_specialized_field(JavaValue* result, const Handle& h_oop, field
}
}
static bool find_field(InstanceKlass* ik,
static bool find_field(const InstanceKlass* ik,
Symbol* name_symbol,
Symbol* signature_symbol,
fieldDescriptor* fd,
@ -395,29 +395,32 @@ static bool find_field(InstanceKlass* ik,
return ik->find_local_field(name_symbol, signature_symbol, fd);
}
static void lookup_field(JfrJavaArguments* args, InstanceKlass* klass, fieldDescriptor* fd, bool static_field) {
static void lookup_field(JfrJavaArguments* args, const InstanceKlass* ik, fieldDescriptor* fd, bool static_field) {
assert(args != NULL, "invariant");
assert(klass != NULL, "invariant");
assert(klass->is_initialized(), "invariant");
assert(ik != NULL, "invariant");
assert(ik->is_initialized(), "invariant");
assert(fd != NULL, "invariant");
find_field(klass, args->name(), args->signature(), fd, static_field, true);
find_field(ik, args->name(), args->signature(), fd, static_field, true);
}
static void read_field(JfrJavaArguments* args, JavaValue* result, Thread* thread) {
const bool static_field = !args->has_receiver();
fieldDescriptor fd;
const InstanceKlass* const ik = static_cast<InstanceKlass*>(args->klass());
lookup_field(args, ik, &fd, static_field);
assert(fd.offset() > 0, "invariant");
HandleMark hm(thread);
Handle h_oop(static_field ? Handle(thread, ik->java_mirror()) : Handle(thread, args->receiver()));
read_specialized_field(result, h_oop, &fd);
}
static void read_field(JfrJavaArguments* args, JavaValue* result, TRAPS) {
assert(args != NULL, "invariant");
assert(result != NULL, "invariant");
DEBUG_ONLY(JfrJavaSupport::check_java_thread_in_vm(THREAD));
InstanceKlass* const klass = static_cast<InstanceKlass*>(args->klass());
klass->initialize(CHECK);
const bool static_field = !args->has_receiver();
fieldDescriptor fd;
lookup_field(args, klass, &fd, static_field);
assert(fd.offset() > 0, "invariant");
HandleMark hm(THREAD);
Handle h_oop(static_field ? Handle(THREAD, klass->java_mirror()) : Handle(THREAD, args->receiver()));
read_specialized_field(result, h_oop, &fd);
read_field(args, result, static_cast<Thread*>(THREAD));
}
static void write_field(JfrJavaArguments* args, JavaValue* result, TRAPS) {
@ -448,6 +451,11 @@ void JfrJavaSupport::get_field(JfrJavaArguments* args, TRAPS) {
read_field(args, args->result(), THREAD);
}
void JfrJavaSupport::get_field(JfrJavaArguments* args, Thread* thread) {
assert(args != NULL, "invariant");
read_field(args, args->result(), thread);
}
void JfrJavaSupport::get_field_local_ref(JfrJavaArguments* args, TRAPS) {
assert(args != NULL, "invariant");
DEBUG_ONLY(check_java_thread_in_vm(THREAD));
@ -487,20 +495,18 @@ Klass* JfrJavaSupport::klass(const jobject handle) {
return obj->klass();
}
static char* allocate_string(bool c_heap, int length, JavaThread* jt) {
static char* allocate_string(bool c_heap, int length, Thread* thread) {
return c_heap ? NEW_C_HEAP_ARRAY(char, length, mtTracing) :
NEW_RESOURCE_ARRAY_IN_THREAD(jt, char, length);
NEW_RESOURCE_ARRAY_IN_THREAD(thread, char, length);
}
const char* JfrJavaSupport::c_str(oop string, JavaThread* t, bool c_heap /* false */) {
DEBUG_ONLY(check_java_thread_in_vm(t));
const char* JfrJavaSupport::c_str(oop string, Thread* thread, bool c_heap /* false */) {
char* str = NULL;
const typeArrayOop value = java_lang_String::value(string);
if (value != NULL) {
const int length = java_lang_String::utf8_length(string, value);
str = allocate_string(c_heap, length + 1, t);
str = allocate_string(c_heap, length + 1, thread);
if (str == NULL) {
JfrJavaSupport::throw_out_of_memory_error("Unable to allocate native memory", t);
return NULL;
}
java_lang_String::as_utf8_string(string, value, str, length + 1);
@ -508,9 +514,8 @@ const char* JfrJavaSupport::c_str(oop string, JavaThread* t, bool c_heap /* fals
return str;
}
const char* JfrJavaSupport::c_str(jstring string, JavaThread* t, bool c_heap /* false */) {
DEBUG_ONLY(check_java_thread_in_vm(t));
return string != NULL ? c_str(resolve_non_null(string), t, c_heap) : NULL;
const char* JfrJavaSupport::c_str(jstring string, Thread* thread, bool c_heap /* false */) {
return string != NULL ? c_str(resolve_non_null(string), thread, c_heap) : NULL;
}
/*

View File

@ -56,6 +56,7 @@ class JfrJavaSupport : public AllStatic {
static void set_field(JfrJavaArguments* args, TRAPS);
static void get_field(JfrJavaArguments* args, TRAPS);
static void get_field(JfrJavaArguments* args, Thread* thread);
static void new_object(JfrJavaArguments* args, TRAPS);
// global jni handle result
@ -75,8 +76,8 @@ class JfrJavaSupport : public AllStatic {
// misc
static Klass* klass(const jobject handle);
static const char* c_str(jstring string, JavaThread* jt, bool c_heap = false);
static const char* c_str(oop string, JavaThread* jt, bool c_heap = false);
static const char* c_str(jstring string, Thread* thread, bool c_heap = false);
static const char* c_str(oop string, Thread* thread, bool c_heap = false);
// exceptions
static void throw_illegal_state_exception(const char* message, TRAPS);

View File

@ -1081,6 +1081,13 @@
<Field type="uint" name="stallCount" label="Stall Count" description="The number of Java threads stalled by the GC locker" />
</Event>
<Event name="FinalizerStatistics" category="Java Application, Statistics" label="Finalizer Statistics" description="Per class statistics about finalizers" thread="false" startTime="false" period="endChunk">
<Field type="Class" name="finalizableClass" label="Class Overriding Finalize" />
<Field type="Symbol" name="codeSource" label="Code Source" description="URL from where the class was loaded" />
<Field type="ulong" name="objects" label="Finalizable Objects on Heap" description="Number of objects on heap that can be finalized" />
<Field type="ulong" name="totalFinalizersRun" label="Finalizers Run" description="Total number of finalizers run since JVM start" />
</Event>
<Type name="DeoptimizationReason" label="Deoptimization Reason">
<Field type="string" name="reason" label="Reason" />
</Type>

View File

@ -0,0 +1,131 @@
/*
* Copyright (c) 2021, 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 "utilities/macros.hpp"
#if INCLUDE_MANAGEMENT
#include "classfile/classLoaderDataGraph.hpp"
#include "classfile/javaClasses.inline.hpp"
#include "jfr/jfrEvents.hpp"
#include "jfr/jni/jfrJavaSupport.hpp"
#include "jfr/periodic/jfrFinalizerStatisticsEvent.hpp"
#include "jfr/support/jfrSymbolTable.hpp"
#include "jfr/utilities/jfrTime.hpp"
#include "jfr/utilities/jfrTypes.hpp"
#include "oops/instanceKlass.inline.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/thread.inline.hpp"
#include "services/finalizerService.hpp"
static oop get_codesource(oop pd, Thread* thread) {
assert(pd != NULL, "invariant");
assert(thread != NULL, "invariant");
JavaValue result(T_OBJECT);
JfrJavaArguments args(&result);
args.set_klass(pd->klass());
args.set_name("codesource");
args.set_signature("Ljava/security/CodeSource;");
args.set_receiver(pd);
JfrJavaSupport::get_field(&args, thread);
return result.get_oop();
}
// Caller needs ResourceMark
static const char* get_locationNoFragString(oop codesource, Thread* thread) {
assert(codesource != NULL, "invariant");
assert(thread != NULL, "invariant");
JavaValue result(T_OBJECT);
JfrJavaArguments args(&result);
args.set_klass(codesource->klass());
args.set_name("locationNoFragString");
args.set_signature("Ljava/lang/String;");
args.set_receiver(codesource);
JfrJavaSupport::get_field(&args, thread);
const oop string_oop = result.get_oop();
return string_oop != NULL ? JfrJavaSupport::c_str(string_oop, thread) : NULL;
}
// Caller needs ResourceMark
static const char* codesource(const InstanceKlass* ik, Thread* thread) {
assert(ik != NULL, "invariant");
assert(thread != NULL, "invariant");
oop pd = java_lang_Class::protection_domain(ik->java_mirror());
if (pd == NULL) {
return NULL;
}
oop codesource = get_codesource(pd, thread);
return codesource != NULL ? get_locationNoFragString(codesource, thread) : NULL;
}
static void send_event(const FinalizerEntry* fe, const InstanceKlass* ik, const JfrTicks& timestamp, Thread* thread) {
assert(ik != NULL, "invariant");
assert(ik->has_finalizer(), "invariant");
assert(thread != NULL, "invariant");
const char* const url = codesource(ik, thread);
const traceid url_symbol_id = url != NULL ? JfrSymbolTable::add(url) : 0;
EventFinalizerStatistics event(UNTIMED);
event.set_endtime(timestamp);
event.set_finalizableClass(ik);
event.set_codeSource(url_symbol_id);
if (fe == NULL) {
event.set_objects(0);
event.set_totalFinalizersRun(0);
} else {
assert(fe->klass() == ik, "invariant");
event.set_objects(fe->objects_on_heap());
event.set_totalFinalizersRun(fe->total_finalizers_run());
}
event.commit();
}
void JfrFinalizerStatisticsEvent::send_unload_event(const InstanceKlass* ik) {
Thread* const thread = Thread::current();
ResourceMark rm(thread);
send_event(FinalizerService::lookup(ik, thread), ik, JfrTicks::now(), thread);
}
// Finalizer events generated by the periodic task will all have the same timestamp.
class FinalizerStatisticsEventClosure : public FinalizerEntryClosure {
private:
Thread* _thread;
const JfrTicks _timestamp;
public:
FinalizerStatisticsEventClosure(Thread* thread) : _thread(thread), _timestamp(JfrTicks::now()) {}
virtual bool do_entry(const FinalizerEntry* fe) {
assert(fe != NULL, "invariant");
send_event(fe, fe->klass(), _timestamp, _thread);
return true;
}
};
void JfrFinalizerStatisticsEvent::generate_events() {
Thread* const thread = Thread::current();
ResourceMark rm(thread);
FinalizerStatisticsEventClosure fsec(thread);
MutexLocker lock(ClassLoaderDataGraph_lock); // To prevent entries from being removed by class unloading.
FinalizerService::do_entries(&fsec, thread);
}
#endif // INCLUDE_MANAGEMENT

View File

@ -0,0 +1,38 @@
/*
* Copyright (c) 2021, 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_JFR_PERIODIC_JFRFINALIZERSTATISTICSEVENT_HPP
#define SHARE_JFR_PERIODIC_JFRFINALIZERSTATISTICSEVENT_HPP
#include "memory/allocation.hpp"
class InstanceKlass;
class JfrFinalizerStatisticsEvent : AllStatic {
public:
static void send_unload_event(const InstanceKlass* ik) NOT_MANAGEMENT_RETURN;
static void generate_events() NOT_MANAGEMENT_RETURN;
};
#endif // SHARE_JFR_PERIODIC_JFRFINALIZERSTATISTICSEVENT_HPP

View File

@ -37,6 +37,7 @@
#include "gc/shared/gcVMOperations.hpp"
#include "gc/shared/objectCountEventSender.hpp"
#include "jfr/jfrEvents.hpp"
#include "jfr/periodic/jfrFinalizerStatisticsEvent.hpp"
#include "jfr/periodic/jfrModuleEvent.hpp"
#include "jfr/periodic/jfrOSInterface.hpp"
#include "jfr/periodic/jfrThreadCPULoadEvent.hpp"
@ -472,10 +473,14 @@ TRACE_REQUEST_FUNC(JavaThreadStatistics) {
}
TRACE_REQUEST_FUNC(ClassLoadingStatistics) {
#if INCLUDE_MANAGEMENT
EventClassLoadingStatistics event;
event.set_loadedClassCount(ClassLoadingService::loaded_class_count());
event.set_unloadedClassCount(ClassLoadingService::unloaded_class_count());
event.commit();
#else
log_debug(jfr, system)("Unable to generate requestable event ClassLoadingStatistics. The required jvm feature 'management' is missing.");
#endif
}
class JfrClassLoaderStatsClosure : public ClassLoaderStatsClosure {
@ -635,7 +640,6 @@ TRACE_REQUEST_FUNC(CodeSweeperConfiguration) {
event.commit();
}
TRACE_REQUEST_FUNC(ShenandoahHeapRegionInformation) {
#if INCLUDE_SHENANDOAHGC
if (UseShenandoahGC) {
@ -644,3 +648,11 @@ TRACE_REQUEST_FUNC(ShenandoahHeapRegionInformation) {
}
#endif
}
TRACE_REQUEST_FUNC(FinalizerStatistics) {
#if INCLUDE_MANAGEMENT
JfrFinalizerStatisticsEvent::generate_events();
#else
log_debug(jfr, system)("Unable to generate requestable event FinalizerStatistics. The required jvm feature 'management' is missing.");
#endif
}

View File

@ -55,8 +55,8 @@ typedef const ModuleEntry* ModPtr;
typedef const ClassLoaderData* CldPtr;
typedef const Method* MethodPtr;
typedef const Symbol* SymbolPtr;
typedef const JfrSymbolId::SymbolEntry* SymbolEntryPtr;
typedef const JfrSymbolId::CStringEntry* CStringEntryPtr;
typedef const JfrSymbolTable::SymbolEntry* SymbolEntryPtr;
typedef const JfrSymbolTable::StringEntry* StringEntryPtr;
static JfrCheckpointWriter* _writer = NULL;
static JfrCheckpointWriter* _leakp_writer = NULL;
@ -64,18 +64,7 @@ static JfrArtifactSet* _artifacts = NULL;
static JfrArtifactClosure* _subsystem_callback = NULL;
static bool _class_unload = false;
static bool _flushpoint = false;
static bool _clear_artifacts = false;
// incremented on each rotation
static u8 checkpoint_id = 1;
// creates a unique id by combining a checkpoint relative symbol id (2^24)
// with the current checkpoint id (2^40)
#define CREATE_SYMBOL_ID(sym_id) (((u8)((checkpoint_id << 24) | sym_id)))
static traceid create_symbol_id(traceid artifact_id) {
return artifact_id != 0 ? CREATE_SYMBOL_ID(artifact_id) : 0;
}
static bool _initial_type_set = true;
static bool current_epoch() {
return _class_unload || _flushpoint;
@ -86,7 +75,7 @@ static bool previous_epoch() {
}
static bool is_initial_typeset_for_chunk() {
return _clear_artifacts && !_class_unload;
return _initial_type_set && !_class_unload;
}
static bool is_complete() {
@ -94,15 +83,15 @@ static bool is_complete() {
}
static traceid mark_symbol(KlassPtr klass, bool leakp) {
return klass != NULL ? create_symbol_id(_artifacts->mark(klass, leakp)) : 0;
return klass != NULL ? _artifacts->mark(klass, leakp) : 0;
}
static traceid mark_symbol(Symbol* symbol, bool leakp) {
return symbol != NULL ? create_symbol_id(_artifacts->mark(symbol, leakp)) : 0;
return symbol != NULL ? _artifacts->mark(symbol, leakp) : 0;
}
static traceid get_bootstrap_name(bool leakp) {
return create_symbol_id(_artifacts->bootstrap_name(leakp));
return _artifacts->bootstrap_name(leakp);
}
static const char* primitive_name(KlassPtr type_array_klass) {
@ -924,14 +913,14 @@ static void write_methods() {
}
template <>
void set_serialized<JfrSymbolId::SymbolEntry>(SymbolEntryPtr ptr) {
void set_serialized<JfrSymbolTable::SymbolEntry>(SymbolEntryPtr ptr) {
assert(ptr != NULL, "invariant");
ptr->set_serialized();
assert(ptr->is_serialized(), "invariant");
}
template <>
void set_serialized<JfrSymbolId::CStringEntry>(CStringEntryPtr ptr) {
void set_serialized<JfrSymbolTable::StringEntry>(StringEntryPtr ptr) {
assert(ptr != NULL, "invariant");
ptr->set_serialized();
assert(ptr->is_serialized(), "invariant");
@ -941,7 +930,7 @@ static int write_symbol(JfrCheckpointWriter* writer, SymbolEntryPtr entry, bool
assert(writer != NULL, "invariant");
assert(entry != NULL, "invariant");
ResourceMark rm;
writer->write(create_symbol_id(entry->id()));
writer->write(entry->id());
writer->write(entry->value()->as_C_string());
return 1;
}
@ -959,42 +948,42 @@ int write__symbol__leakp(JfrCheckpointWriter* writer, const void* e) {
return write_symbol(writer, entry, true);
}
static int write_cstring(JfrCheckpointWriter* writer, CStringEntryPtr entry, bool leakp) {
static int write_string(JfrCheckpointWriter* writer, StringEntryPtr entry, bool leakp) {
assert(writer != NULL, "invariant");
assert(entry != NULL, "invariant");
writer->write(create_symbol_id(entry->id()));
writer->write(entry->id());
writer->write(entry->value());
return 1;
}
int write__cstring(JfrCheckpointWriter* writer, const void* e) {
int write__string(JfrCheckpointWriter* writer, const void* e) {
assert(e != NULL, "invariant");
CStringEntryPtr entry = (CStringEntryPtr)e;
StringEntryPtr entry = (StringEntryPtr)e;
set_serialized(entry);
return write_cstring(writer, entry, false);
return write_string(writer, entry, false);
}
int write__cstring__leakp(JfrCheckpointWriter* writer, const void* e) {
int write__string__leakp(JfrCheckpointWriter* writer, const void* e) {
assert(e != NULL, "invariant");
CStringEntryPtr entry = (CStringEntryPtr)e;
return write_cstring(writer, entry, true);
StringEntryPtr entry = (StringEntryPtr)e;
return write_string(writer, entry, true);
}
typedef SymbolPredicate<SymbolEntryPtr, false> SymPredicate;
typedef JfrPredicatedTypeWriterImplHost<SymbolEntryPtr, SymPredicate, write__symbol> SymbolEntryWriterImpl;
typedef JfrTypeWriterHost<SymbolEntryWriterImpl, TYPE_SYMBOL> SymbolEntryWriter;
typedef SymbolPredicate<CStringEntryPtr, false> CStringPredicate;
typedef JfrPredicatedTypeWriterImplHost<CStringEntryPtr, CStringPredicate, write__cstring> CStringEntryWriterImpl;
typedef JfrTypeWriterHost<CStringEntryWriterImpl, TYPE_SYMBOL> CStringEntryWriter;
typedef SymbolPredicate<StringEntryPtr, false> StringPredicate;
typedef JfrPredicatedTypeWriterImplHost<StringEntryPtr, StringPredicate, write__string> StringEntryWriterImpl;
typedef JfrTypeWriterHost<StringEntryWriterImpl, TYPE_SYMBOL> StringEntryWriter;
typedef SymbolPredicate<SymbolEntryPtr, true> LeakSymPredicate;
typedef JfrPredicatedTypeWriterImplHost<SymbolEntryPtr, LeakSymPredicate, write__symbol__leakp> LeakSymbolEntryWriterImpl;
typedef JfrTypeWriterHost<LeakSymbolEntryWriterImpl, TYPE_SYMBOL> LeakSymbolEntryWriter;
typedef CompositeFunctor<SymbolEntryPtr, LeakSymbolEntryWriter, SymbolEntryWriter> CompositeSymbolWriter;
typedef SymbolPredicate<CStringEntryPtr, true> LeakCStringPredicate;
typedef JfrPredicatedTypeWriterImplHost<CStringEntryPtr, LeakCStringPredicate, write__cstring__leakp> LeakCStringEntryWriterImpl;
typedef JfrTypeWriterHost<LeakCStringEntryWriterImpl, TYPE_SYMBOL> LeakCStringEntryWriter;
typedef CompositeFunctor<CStringEntryPtr, LeakCStringEntryWriter, CStringEntryWriter> CompositeCStringWriter;
typedef SymbolPredicate<StringEntryPtr, true> LeakStringPredicate;
typedef JfrPredicatedTypeWriterImplHost<StringEntryPtr, LeakStringPredicate, write__string__leakp> LeakStringEntryWriterImpl;
typedef JfrTypeWriterHost<LeakStringEntryWriterImpl, TYPE_SYMBOL> LeakStringEntryWriter;
typedef CompositeFunctor<StringEntryPtr, LeakStringEntryWriter, StringEntryWriter> CompositeStringWriter;
static void write_symbols_with_leakp() {
assert(_leakp_writer != NULL, "invariant");
@ -1002,12 +991,12 @@ static void write_symbols_with_leakp() {
LeakSymbolEntryWriter lsw(_leakp_writer, _class_unload);
CompositeSymbolWriter csw(&lsw, &sw);
_artifacts->iterate_symbols(csw);
CStringEntryWriter ccsw(_writer, _class_unload, true); // skip header
LeakCStringEntryWriter lccsw(_leakp_writer, _class_unload, true); // skip header
CompositeCStringWriter cccsw(&lccsw, &ccsw);
_artifacts->iterate_cstrings(cccsw);
sw.add(ccsw.count());
lsw.add(lccsw.count());
StringEntryWriter sew(_writer, _class_unload, true); // skip header
LeakStringEntryWriter lsew(_leakp_writer, _class_unload, true); // skip header
CompositeStringWriter csew(&lsew, &sew);
_artifacts->iterate_strings(csew);
sw.add(sew.count());
lsw.add(lsew.count());
_artifacts->tally(sw);
}
@ -1019,9 +1008,9 @@ static void write_symbols() {
}
SymbolEntryWriter sw(_writer, _class_unload);
_artifacts->iterate_symbols(sw);
CStringEntryWriter csw(_writer, _class_unload, true); // skip header
_artifacts->iterate_cstrings(csw);
sw.add(csw.count());
StringEntryWriter sew(_writer, _class_unload, true); // skip header
_artifacts->iterate_strings(sew);
sw.add(sew.count());
_artifacts->tally(sw);
}
@ -1040,10 +1029,10 @@ static size_t teardown() {
if (previous_epoch()) {
clear_klasses_and_methods();
JfrKlassUnloading::clear();
_clear_artifacts = true;
++checkpoint_id;
_artifacts->increment_checkpoint_id();
_initial_type_set = true;
} else {
_clear_artifacts = false;
_initial_type_set = false;
}
return total_count;
}
@ -1056,7 +1045,7 @@ static void setup(JfrCheckpointWriter* writer, JfrCheckpointWriter* leakp_writer
if (_artifacts == NULL) {
_artifacts = new JfrArtifactSet(class_unload);
} else {
_artifacts->initialize(class_unload, _clear_artifacts);
_artifacts->initialize(class_unload);
}
if (!_class_unload) {
JfrKlassUnloading::sort(previous_epoch());
@ -1091,7 +1080,9 @@ size_t JfrTypeSet::serialize(JfrCheckpointWriter* writer, JfrCheckpointWriter* l
void JfrTypeSet::clear() {
ResourceMark rm;
JfrKlassUnloading::clear();
_clear_artifacts = true;
if (_artifacts != NULL) {
_artifacts->clear();
}
setup(NULL, NULL, false, false);
register_klasses();
clear_packages();

View File

@ -30,225 +30,7 @@
#include "oops/oop.inline.hpp"
#include "oops/symbol.hpp"
static JfrSymbolId::CStringEntry* bootstrap = NULL;
JfrSymbolId::JfrSymbolId() :
_sym_table(new SymbolTable(this)),
_cstring_table(new CStringTable(this)),
_sym_list(NULL),
_cstring_list(NULL),
_sym_query(NULL),
_cstring_query(NULL),
_symbol_id_counter(1),
_class_unload(false) {
assert(_sym_table != NULL, "invariant");
assert(_cstring_table != NULL, "invariant");
bootstrap = new CStringEntry(0, (const char*)&BOOTSTRAP_LOADER_NAME);
assert(bootstrap != NULL, "invariant");
bootstrap->set_id(1);
_cstring_list = bootstrap;
}
JfrSymbolId::~JfrSymbolId() {
clear();
delete _sym_table;
delete _cstring_table;
delete bootstrap;
}
void JfrSymbolId::clear() {
assert(_sym_table != NULL, "invariant");
if (_sym_table->has_entries()) {
_sym_table->clear_entries();
}
assert(!_sym_table->has_entries(), "invariant");
assert(_cstring_table != NULL, "invariant");
if (_cstring_table->has_entries()) {
_cstring_table->clear_entries();
}
assert(!_cstring_table->has_entries(), "invariant");
_sym_list = NULL;
_symbol_id_counter = 1;
_sym_query = NULL;
_cstring_query = NULL;
assert(bootstrap != NULL, "invariant");
bootstrap->reset();
_cstring_list = bootstrap;
}
void JfrSymbolId::set_class_unload(bool class_unload) {
_class_unload = class_unload;
}
void JfrSymbolId::on_link(const SymbolEntry* entry) {
assert(entry != NULL, "invariant");
const_cast<Symbol*>(entry->literal())->increment_refcount();
assert(entry->id() == 0, "invariant");
entry->set_id(++_symbol_id_counter);
entry->set_list_next(_sym_list);
_sym_list = entry;
}
bool JfrSymbolId::on_equals(uintptr_t hash, const SymbolEntry* entry) {
assert(entry != NULL, "invariant");
assert(entry->hash() == hash, "invariant");
assert(_sym_query != NULL, "invariant");
return _sym_query == entry->literal();
}
void JfrSymbolId::on_unlink(const SymbolEntry* entry) {
assert(entry != NULL, "invariant");
const_cast<Symbol*>(entry->literal())->decrement_refcount();
}
static const char* resource_to_cstring(const char* resource_str) {
assert(resource_str != NULL, "invariant");
const size_t length = strlen(resource_str);
char* const c_string = JfrCHeapObj::new_array<char>(length + 1);
assert(c_string != NULL, "invariant");
strncpy(c_string, resource_str, length + 1);
return c_string;
}
void JfrSymbolId::on_link(const CStringEntry* entry) {
assert(entry != NULL, "invariant");
assert(entry->id() == 0, "invariant");
entry->set_id(++_symbol_id_counter);
const_cast<CStringEntry*>(entry)->set_literal(resource_to_cstring(entry->literal()));
entry->set_list_next(_cstring_list);
_cstring_list = entry;
}
static bool string_compare(const char* query, const char* candidate) {
assert(query != NULL, "invariant");
assert(candidate != NULL, "invariant");
const size_t length = strlen(query);
return strncmp(query, candidate, length) == 0;
}
bool JfrSymbolId::on_equals(uintptr_t hash, const CStringEntry* entry) {
assert(entry != NULL, "invariant");
assert(entry->hash() == hash, "invariant");
assert(_cstring_query != NULL, "invariant");
return string_compare(_cstring_query, entry->literal());
}
void JfrSymbolId::on_unlink(const CStringEntry* entry) {
assert(entry != NULL, "invariant");
JfrCHeapObj::free(const_cast<char*>(entry->literal()), strlen(entry->literal() + 1));
}
traceid JfrSymbolId::bootstrap_name(bool leakp) {
assert(bootstrap != NULL, "invariant");
if (leakp) {
bootstrap->set_leakp();
}
return 1;
}
traceid JfrSymbolId::mark(const Symbol* symbol, bool leakp) {
assert(symbol != NULL, "invariant");
return mark((uintptr_t)symbol->identity_hash(), symbol, leakp);
}
traceid JfrSymbolId::mark(uintptr_t hash, const Symbol* data, bool leakp) {
assert(data != NULL, "invariant");
assert(_sym_table != NULL, "invariant");
_sym_query = data;
const SymbolEntry& entry = _sym_table->lookup_put(hash, data);
if (_class_unload) {
entry.set_unloading();
}
if (leakp) {
entry.set_leakp();
}
return entry.id();
}
traceid JfrSymbolId::mark(uintptr_t hash, const char* str, bool leakp) {
assert(str != NULL, "invariant");
assert(_cstring_table != NULL, "invariant");
_cstring_query = str;
const CStringEntry& entry = _cstring_table->lookup_put(hash, str);
if (_class_unload) {
entry.set_unloading();
}
if (leakp) {
entry.set_leakp();
}
return entry.id();
}
/*
* hidden classes symbol is the external name +
* the address of its InstanceKlass slash appended:
* java.lang.invoke.LambdaForm$BMH/22626602
*
* caller needs ResourceMark
*/
uintptr_t JfrSymbolId::hidden_klass_name_hash(const InstanceKlass* ik) {
assert(ik != NULL, "invariant");
assert(ik->is_hidden(), "invariant");
const oop mirror = ik->java_mirror_no_keepalive();
assert(mirror != NULL, "invariant");
return (uintptr_t)mirror->identity_hash();
}
static const char* create_hidden_klass_symbol(const InstanceKlass* ik, uintptr_t hash) {
assert(ik != NULL, "invariant");
assert(ik->is_hidden(), "invariant");
assert(hash != 0, "invariant");
char* hidden_symbol = NULL;
const oop mirror = ik->java_mirror_no_keepalive();
assert(mirror != NULL, "invariant");
char hash_buf[40];
sprintf(hash_buf, "/" UINTX_FORMAT, hash);
const size_t hash_len = strlen(hash_buf);
const size_t result_len = ik->name()->utf8_length();
hidden_symbol = NEW_RESOURCE_ARRAY(char, result_len + hash_len + 1);
ik->name()->as_klass_external_name(hidden_symbol, (int)result_len + 1);
assert(strlen(hidden_symbol) == result_len, "invariant");
strcpy(hidden_symbol + result_len, hash_buf);
assert(strlen(hidden_symbol) == result_len + hash_len, "invariant");
return hidden_symbol;
}
bool JfrSymbolId::is_hidden_klass(const Klass* k) {
assert(k != NULL, "invariant");
return k->is_instance_klass() && ((const InstanceKlass*)k)->is_hidden();
}
traceid JfrSymbolId::mark_hidden_klass_name(const InstanceKlass* ik, bool leakp) {
assert(ik != NULL, "invariant");
assert(ik->is_hidden(), "invariant");
const uintptr_t hash = hidden_klass_name_hash(ik);
const char* const hidden_symbol = create_hidden_klass_symbol(ik, hash);
return mark(hash, hidden_symbol, leakp);
}
traceid JfrSymbolId::mark(const Klass* k, bool leakp) {
assert(k != NULL, "invariant");
traceid symbol_id = 0;
if (is_hidden_klass(k)) {
assert(k->is_instance_klass(), "invariant");
symbol_id = mark_hidden_klass_name((const InstanceKlass*)k, leakp);
}
if (0 == symbol_id) {
Symbol* const sym = k->name();
if (sym != NULL) {
symbol_id = mark(sym, leakp);
}
}
assert(symbol_id > 0, "a symbol handler must mark the symbol for writing");
return symbol_id;
}
JfrArtifactSet::JfrArtifactSet(bool class_unload) : _symbol_id(new JfrSymbolId()),
JfrArtifactSet::JfrArtifactSet(bool class_unload) : _symbol_table(NULL),
_klass_list(NULL),
_total_count(0) {
initialize(class_unload);
@ -258,47 +40,53 @@ JfrArtifactSet::JfrArtifactSet(bool class_unload) : _symbol_id(new JfrSymbolId()
static const size_t initial_klass_list_size = 256;
const int initial_klass_loader_set_size = 64;
void JfrArtifactSet::initialize(bool class_unload, bool clear /* false */) {
assert(_symbol_id != NULL, "invariant");
if (clear) {
_symbol_id->clear();
void JfrArtifactSet::initialize(bool class_unload) {
if (_symbol_table == NULL) {
_symbol_table = JfrSymbolTable::create();
assert(_symbol_table != NULL, "invariant");
}
_symbol_id->set_class_unload(class_unload);
assert(_symbol_table != NULL, "invariant");
_symbol_table->set_class_unload(class_unload);
_total_count = 0;
// resource allocation
_klass_list = new GrowableArray<const Klass*>(initial_klass_list_size);
_klass_loader_set = new GrowableArray<const Klass*>(initial_klass_loader_set_size);
}
void JfrArtifactSet::clear() {
if (_symbol_table != NULL) {
_symbol_table->clear();
}
}
JfrArtifactSet::~JfrArtifactSet() {
_symbol_id->clear();
delete _symbol_id;
delete _symbol_table;
// _klass_list and _klass_loader_list will be cleared by a ResourceMark
}
traceid JfrArtifactSet::bootstrap_name(bool leakp) {
return _symbol_id->bootstrap_name(leakp);
return _symbol_table->bootstrap_name(leakp);
}
traceid JfrArtifactSet::mark_hidden_klass_name(const Klass* klass, bool leakp) {
assert(klass->is_instance_klass(), "invariant");
return _symbol_id->mark_hidden_klass_name((const InstanceKlass*)klass, leakp);
return _symbol_table->mark_hidden_klass_name((const InstanceKlass*)klass, leakp);
}
traceid JfrArtifactSet::mark(uintptr_t hash, const Symbol* sym, bool leakp) {
return _symbol_id->mark(hash, sym, leakp);
return _symbol_table->mark(hash, sym, leakp);
}
traceid JfrArtifactSet::mark(const Klass* klass, bool leakp) {
return _symbol_id->mark(klass, leakp);
return _symbol_table->mark(klass, leakp);
}
traceid JfrArtifactSet::mark(const Symbol* symbol, bool leakp) {
return _symbol_id->mark(symbol, leakp);
return _symbol_table->mark(symbol, leakp);
}
traceid JfrArtifactSet::mark(uintptr_t hash, const char* const str, bool leakp) {
return _symbol_id->mark(hash, str, leakp);
return _symbol_table->mark(hash, str, leakp);
}
bool JfrArtifactSet::has_klass_entries() const {
@ -324,3 +112,9 @@ void JfrArtifactSet::register_klass(const Klass* k) {
size_t JfrArtifactSet::total_count() const {
return _total_count;
}
void JfrArtifactSet::increment_checkpoint_id() {
assert(_symbol_table != NULL, "invariant");
_symbol_table->increment_checkpoint_id();
}

View File

@ -26,8 +26,8 @@
#define SHARE_JFR_RECORDER_CHECKPOINT_TYPES_JFRTYPESETUTILS_HPP
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
#include "jfr/support/jfrSymbolTable.hpp"
#include "jfr/utilities/jfrAllocation.hpp"
#include "jfr/utilities/jfrHashtable.hpp"
#include "oops/klass.hpp"
#include "oops/method.hpp"
@ -191,98 +191,6 @@ class LeakPredicate<const Method*> {
}
};
template <typename T, typename IdType>
class ListEntry : public JfrHashtableEntry<T, IdType> {
public:
ListEntry(uintptr_t hash, const T& data) : JfrHashtableEntry<T, IdType>(hash, data),
_list_next(NULL), _serialized(false), _unloading(false), _leakp(false) {}
const ListEntry<T, IdType>* list_next() const { return _list_next; }
void reset() const {
_list_next = NULL; _serialized = false; _unloading = false; _leakp = false;
}
void set_list_next(const ListEntry<T, IdType>* next) const { _list_next = next; }
bool is_serialized() const { return _serialized; }
void set_serialized() const { _serialized = true; }
bool is_unloading() const { return _unloading; }
void set_unloading() const { _unloading = true; }
bool is_leakp() const { return _leakp; }
void set_leakp() const { _leakp = true; }
private:
mutable const ListEntry<T, IdType>* _list_next;
mutable bool _serialized;
mutable bool _unloading;
mutable bool _leakp;
};
class JfrSymbolId : public JfrCHeapObj {
template <typename, typename, template<typename, typename> class, typename, size_t>
friend class HashTableHost;
typedef HashTableHost<const Symbol*, traceid, ListEntry, JfrSymbolId> SymbolTable;
typedef HashTableHost<const char*, traceid, ListEntry, JfrSymbolId> CStringTable;
friend class JfrArtifactSet;
public:
typedef SymbolTable::HashEntry SymbolEntry;
typedef CStringTable::HashEntry CStringEntry;
private:
SymbolTable* _sym_table;
CStringTable* _cstring_table;
const SymbolEntry* _sym_list;
const CStringEntry* _cstring_list;
const Symbol* _sym_query;
const char* _cstring_query;
traceid _symbol_id_counter;
bool _class_unload;
// hashtable(s) callbacks
void on_link(const SymbolEntry* entry);
bool on_equals(uintptr_t hash, const SymbolEntry* entry);
void on_unlink(const SymbolEntry* entry);
void on_link(const CStringEntry* entry);
bool on_equals(uintptr_t hash, const CStringEntry* entry);
void on_unlink(const CStringEntry* entry);
template <typename Functor, typename T>
void iterate(Functor& functor, const T* list) {
const T* symbol = list;
while (symbol != NULL) {
const T* next = symbol->list_next();
functor(symbol);
symbol = next;
}
}
traceid mark_hidden_klass_name(const InstanceKlass* k, bool leakp);
bool is_hidden_klass(const Klass* k);
uintptr_t hidden_klass_name_hash(const InstanceKlass* ik);
public:
JfrSymbolId();
~JfrSymbolId();
void clear();
void set_class_unload(bool class_unload);
traceid mark(uintptr_t hash, const Symbol* sym, bool leakp);
traceid mark(const Klass* k, bool leakp);
traceid mark(const Symbol* symbol, bool leakp);
traceid mark(uintptr_t hash, const char* str, bool leakp);
traceid bootstrap_name(bool leakp);
template <typename Functor>
void iterate_symbols(Functor& functor) {
iterate(functor, _sym_list);
}
template <typename Functor>
void iterate_cstrings(Functor& functor) {
iterate(functor, _cstring_list);
}
bool has_entries() const { return has_symbol_entries() || has_cstring_entries(); }
bool has_symbol_entries() const { return _sym_list != NULL; }
bool has_cstring_entries() const { return _cstring_list != NULL; }
};
/**
* When processing a set of artifacts, there will be a need
* to track transitive dependencies originating with each artifact.
@ -299,7 +207,7 @@ class JfrSymbolId : public JfrCHeapObj {
*/
class JfrArtifactSet : public JfrCHeapObj {
private:
JfrSymbolId* _symbol_id;
JfrSymbolTable* _symbol_table;
GrowableArray<const Klass*>* _klass_list;
GrowableArray<const Klass*>* _klass_loader_set;
size_t _total_count;
@ -309,7 +217,8 @@ class JfrArtifactSet : public JfrCHeapObj {
~JfrArtifactSet();
// caller needs ResourceMark
void initialize(bool class_unload, bool clear = false);
void initialize(bool class_unload);
void clear();
traceid mark(uintptr_t hash, const Symbol* sym, bool leakp);
traceid mark(const Klass* klass, bool leakp);
@ -318,15 +227,16 @@ class JfrArtifactSet : public JfrCHeapObj {
traceid mark_hidden_klass_name(const Klass* klass, bool leakp);
traceid bootstrap_name(bool leakp);
const JfrSymbolId::SymbolEntry* map_symbol(const Symbol* symbol) const;
const JfrSymbolId::SymbolEntry* map_symbol(uintptr_t hash) const;
const JfrSymbolId::CStringEntry* map_cstring(uintptr_t hash) const;
const JfrSymbolTable::SymbolEntry* map_symbol(const Symbol* symbol) const;
const JfrSymbolTable::SymbolEntry* map_symbol(uintptr_t hash) const;
const JfrSymbolTable::StringEntry* map_string(uintptr_t hash) const;
bool has_klass_entries() const;
int entries() const;
size_t total_count() const;
void register_klass(const Klass* k);
bool should_do_loader_klass(const Klass* k);
void increment_checkpoint_id();
template <typename Functor>
void iterate_klasses(Functor& functor) const {
@ -339,12 +249,12 @@ class JfrArtifactSet : public JfrCHeapObj {
template <typename T>
void iterate_symbols(T& functor) {
_symbol_id->iterate_symbols(functor);
_symbol_table->iterate_symbols(functor);
}
template <typename T>
void iterate_cstrings(T& functor) {
_symbol_id->iterate_cstrings(functor);
void iterate_strings(T& functor) {
_symbol_table->iterate_strings(functor);
}
template <typename Writer>

View File

@ -26,6 +26,8 @@
#include "jfr/recorder/service/jfrPostBox.hpp"
#include "jfr/utilities/jfrTryLock.hpp"
#include "runtime/atomic.hpp"
#include "runtime/handles.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/thread.inline.hpp"
#define MSG_IS_SYNCHRONOUS ( (MSGBIT(MSG_ROTATE)) | \
@ -110,7 +112,9 @@ void JfrPostBox::asynchronous_post(int msg) {
void JfrPostBox::synchronous_post(int msg) {
assert(is_synchronous(msg), "invariant");
assert(!JfrMsg_lock->owned_by_self(), "should not hold JfrMsg_lock here!");
MonitorLocker msg_lock(JfrMsg_lock);
NoHandleMark nhm;
ThreadBlockInVM transition(JavaThread::current());
MonitorLocker msg_lock(JfrMsg_lock, Mutex::_no_safepoint_check_flag);
deposit(msg);
// serial_id is used to check when what we send in has been processed.
// _msg_read_serial is read under JfrMsg_lock protection.
@ -168,6 +172,6 @@ void JfrPostBox::notify_waiters() {
// safeguard to ensure no threads are left waiting
void JfrPostBox::notify_collection_stop() {
MutexLocker msg_lock(JfrMsg_lock);
assert(JfrMsg_lock->owned_by_self(), "invariant");
JfrMsg_lock->notify_all();
}

View File

@ -51,23 +51,24 @@ void recorderthread_entry(JavaThread* thread, JavaThread* unused) {
log_debug(jfr, system)("Recorder thread STARTED");
{
// Run as _thread_in_native to minimize impact on safepoint synchronization.
NoHandleMark nhm;
ThreadToNativeFromVM transition(thread);
bool done = false;
int msgs = 0;
JfrRecorderService service;
MutexLocker msg_lock(JfrMsg_lock);
MonitorLocker msg_lock(JfrMsg_lock, Mutex::_no_safepoint_check_flag);
// JFR MESSAGE LOOP PROCESSING - BEGIN
while (!done) {
if (post_box.is_empty()) {
JfrMsg_lock->wait();
msg_lock.wait();
}
msgs = post_box.collect();
JfrMsg_lock->unlock();
{
// Run as _thread_in_native as much a possible
// to minimize impact on safepoint synchronizations.
NoHandleMark nhm;
ThreadToNativeFromVM transition(thread);
MutexUnlocker mul(JfrMsg_lock, Mutex::_no_safepoint_check_flag);
if (PROCESS_FULL_BUFFERS) {
service.process_full_buffers();
}
@ -82,18 +83,16 @@ void recorderthread_entry(JavaThread* thread, JavaThread* unused) {
service.flushpoint();
}
}
JfrMsg_lock->lock();
post_box.notify_waiters();
if (SHUTDOWN) {
log_debug(jfr, system)("Request to STOP recorder");
done = true;
}
} // JFR MESSAGE LOOP PROCESSING - END
} // JfrMsg_lock scope
post_box.notify_collection_stop();
} // JfrMsg_lock scope and the thread returns to _thread_in_vm
assert(!JfrMsg_lock->owned_by_self(), "invariant");
post_box.notify_collection_stop();
JfrRecorder::on_recorder_thread_exit();
#undef START

View File

@ -23,11 +23,14 @@
*/
#include "precompiled.hpp"
#include "jfr/jfrEvents.hpp"
#include "jfr/periodic/jfrFinalizerStatisticsEvent.hpp"
#include "jfr/recorder/checkpoint/types/traceid/jfrTraceId.inline.hpp"
#include "jfr/support/jfrKlassUnloading.hpp"
#include "jfr/utilities/jfrPredicate.hpp"
#include "jfr/utilities/jfrRelation.hpp"
#include "runtime/mutexLocker.hpp"
#include "utilities/macros.hpp"
static const int initial_array_size = 64;
@ -107,9 +110,22 @@ static bool add_to_unloaded_klass_set(traceid klass_id, bool current_epoch) {
return true;
}
#if INCLUDE_MANAGEMENT
static void send_finalizer_event(const Klass* k) {
if (!k->is_instance_klass()) {
return;
}
const InstanceKlass* const ik = InstanceKlass::cast(k);
if (ik->has_finalizer()) {
JfrFinalizerStatisticsEvent::send_unload_event(ik);
}
}
#endif
bool JfrKlassUnloading::on_unload(const Klass* k) {
assert(k != NULL, "invariant");
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
MANAGEMENT_ONLY(send_finalizer_event(k);)
if (IS_JDK_JFR_EVENT_SUBKLASS(k)) {
++event_klass_unloaded_count;
}

View File

@ -0,0 +1,316 @@
/*
* Copyright (c) 2021, 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 "classfile/javaClasses.inline.hpp"
#include "classfile/classLoaderData.hpp"
#include "jfr/support/jfrSymbolTable.hpp"
#include "oops/instanceKlass.hpp"
#include "oops/oop.inline.hpp"
#include "oops/symbol.hpp"
#include "runtime/mutexLocker.hpp"
// incremented on each rotation
static u8 checkpoint_id = 1;
// creates a unique id by combining a checkpoint relative symbol id (2^24)
// with the current checkpoint id (2^40)
#define CREATE_SYMBOL_ID(sym_id) (((u8)((checkpoint_id << 24) | sym_id)))
static traceid create_symbol_id(traceid artifact_id) {
return artifact_id != 0 ? CREATE_SYMBOL_ID(artifact_id) : 0;
}
static uintptr_t string_hash(const char* str) {
return java_lang_String::hash_code(reinterpret_cast<const jbyte*>(str), static_cast<int>(strlen(str)));
}
static JfrSymbolTable::StringEntry* bootstrap = NULL;
static JfrSymbolTable* _instance = NULL;
static JfrSymbolTable& instance() {
assert(_instance != NULL, "invariant");
return *_instance;
}
JfrSymbolTable* JfrSymbolTable::create() {
assert(_instance == NULL, "invariant");
assert_lock_strong(ClassLoaderDataGraph_lock);
_instance = new JfrSymbolTable();
return _instance;
}
void JfrSymbolTable::destroy() {
assert_lock_strong(ClassLoaderDataGraph_lock);
if (_instance != NULL) {
delete _instance;
_instance = NULL;
}
assert(_instance == NULL, "invariant");
}
JfrSymbolTable::JfrSymbolTable() :
_symbols(new Symbols(this)),
_strings(new Strings(this)),
_symbol_list(NULL),
_string_list(NULL),
_symbol_query(NULL),
_string_query(NULL),
_id_counter(1),
_class_unload(false) {
assert(_symbols != NULL, "invariant");
assert(_strings != NULL, "invariant");
bootstrap = new StringEntry(0, (const char*)&BOOTSTRAP_LOADER_NAME);
assert(bootstrap != NULL, "invariant");
bootstrap->set_id(create_symbol_id(1));
_string_list = bootstrap;
}
JfrSymbolTable::~JfrSymbolTable() {
clear();
delete _symbols;
delete _strings;
delete bootstrap;
}
void JfrSymbolTable::clear() {
assert(_symbols != NULL, "invariant");
if (_symbols->has_entries()) {
_symbols->clear_entries();
}
assert(!_symbols->has_entries(), "invariant");
assert(_strings != NULL, "invariant");
if (_strings->has_entries()) {
_strings->clear_entries();
}
assert(!_strings->has_entries(), "invariant");
_symbol_list = NULL;
_id_counter = 1;
_symbol_query = NULL;
_string_query = NULL;
assert(bootstrap != NULL, "invariant");
bootstrap->reset();
_string_list = bootstrap;
}
void JfrSymbolTable::set_class_unload(bool class_unload) {
_class_unload = class_unload;
}
void JfrSymbolTable::increment_checkpoint_id() {
assert_lock_strong(ClassLoaderDataGraph_lock);
clear();
++checkpoint_id;
}
template <typename T>
inline void JfrSymbolTable::assign_id(T* entry) {
assert(entry != NULL, "invariant");
assert(entry->id() == 0, "invariant");
entry->set_id(create_symbol_id(++_id_counter));
}
void JfrSymbolTable::on_link(const SymbolEntry* entry) {
assign_id(entry);
const_cast<Symbol*>(entry->literal())->increment_refcount();
entry->set_list_next(_symbol_list);
_symbol_list = entry;
}
bool JfrSymbolTable::on_equals(uintptr_t hash, const SymbolEntry* entry) {
assert(entry != NULL, "invariant");
assert(entry->hash() == hash, "invariant");
assert(_symbol_query != NULL, "invariant");
return _symbol_query == entry->literal();
}
void JfrSymbolTable::on_unlink(const SymbolEntry* entry) {
assert(entry != NULL, "invariant");
const_cast<Symbol*>(entry->literal())->decrement_refcount();
}
static const char* resource_to_c_heap_string(const char* resource_str) {
assert(resource_str != NULL, "invariant");
const size_t length = strlen(resource_str);
char* const c_string = JfrCHeapObj::new_array<char>(length + 1);
assert(c_string != NULL, "invariant");
strncpy(c_string, resource_str, length + 1);
return c_string;
}
void JfrSymbolTable::on_link(const StringEntry* entry) {
assign_id(entry);
const_cast<StringEntry*>(entry)->set_literal(resource_to_c_heap_string(entry->literal()));
entry->set_list_next(_string_list);
_string_list = entry;
}
static bool string_compare(const char* query, const char* candidate) {
assert(query != NULL, "invariant");
assert(candidate != NULL, "invariant");
const size_t length = strlen(query);
return strncmp(query, candidate, length) == 0;
}
bool JfrSymbolTable::on_equals(uintptr_t hash, const StringEntry* entry) {
assert(entry != NULL, "invariant");
assert(entry->hash() == hash, "invariant");
assert(_string_query != NULL, "invariant");
return string_compare(_string_query, entry->literal());
}
void JfrSymbolTable::on_unlink(const StringEntry* entry) {
assert(entry != NULL, "invariant");
JfrCHeapObj::free(const_cast<char*>(entry->literal()), strlen(entry->literal() + 1));
}
traceid JfrSymbolTable::bootstrap_name(bool leakp) {
assert(bootstrap != NULL, "invariant");
if (leakp) {
bootstrap->set_leakp();
}
return bootstrap->id();
}
traceid JfrSymbolTable::mark(const Symbol* sym, bool leakp /* false */) {
assert(sym != NULL, "invariant");
return mark((uintptr_t)sym->identity_hash(), sym, leakp);
}
traceid JfrSymbolTable::mark(uintptr_t hash, const Symbol* sym, bool leakp) {
assert(sym != NULL, "invariant");
assert(_symbols != NULL, "invariant");
_symbol_query = sym;
const SymbolEntry& entry = _symbols->lookup_put(hash, sym);
if (_class_unload) {
entry.set_unloading();
}
if (leakp) {
entry.set_leakp();
}
return entry.id();
}
traceid JfrSymbolTable::mark(const char* str, bool leakp /* false*/) {
return mark(string_hash(str), str, leakp);
}
traceid JfrSymbolTable::mark(uintptr_t hash, const char* str, bool leakp) {
assert(str != NULL, "invariant");
assert(_strings != NULL, "invariant");
_string_query = str;
const StringEntry& entry = _strings->lookup_put(hash, str);
if (_class_unload) {
entry.set_unloading();
}
if (leakp) {
entry.set_leakp();
}
return entry.id();
}
/*
* hidden classes symbol is the external name +
* the address of its InstanceKlass slash appended:
* java.lang.invoke.LambdaForm$BMH/22626602
*
* caller needs ResourceMark
*/
uintptr_t JfrSymbolTable::hidden_klass_name_hash(const InstanceKlass* ik) {
assert(ik != NULL, "invariant");
assert(ik->is_hidden(), "invariant");
const oop mirror = ik->java_mirror_no_keepalive();
assert(mirror != NULL, "invariant");
return (uintptr_t)mirror->identity_hash();
}
static const char* create_hidden_klass_symbol(const InstanceKlass* ik, uintptr_t hash) {
assert(ik != NULL, "invariant");
assert(ik->is_hidden(), "invariant");
assert(hash != 0, "invariant");
char* hidden_symbol = NULL;
const oop mirror = ik->java_mirror_no_keepalive();
assert(mirror != NULL, "invariant");
char hash_buf[40];
sprintf(hash_buf, "/" UINTX_FORMAT, hash);
const size_t hash_len = strlen(hash_buf);
const size_t result_len = ik->name()->utf8_length();
hidden_symbol = NEW_RESOURCE_ARRAY(char, result_len + hash_len + 1);
ik->name()->as_klass_external_name(hidden_symbol, (int)result_len + 1);
assert(strlen(hidden_symbol) == result_len, "invariant");
strcpy(hidden_symbol + result_len, hash_buf);
assert(strlen(hidden_symbol) == result_len + hash_len, "invariant");
return hidden_symbol;
}
bool JfrSymbolTable::is_hidden_klass(const Klass* k) {
assert(k != NULL, "invariant");
return k->is_instance_klass() && ((const InstanceKlass*)k)->is_hidden();
}
traceid JfrSymbolTable::mark_hidden_klass_name(const InstanceKlass* ik, bool leakp) {
assert(ik != NULL, "invariant");
assert(ik->is_hidden(), "invariant");
const uintptr_t hash = hidden_klass_name_hash(ik);
const char* const hidden_symbol = create_hidden_klass_symbol(ik, hash);
return mark(hash, hidden_symbol, leakp);
}
traceid JfrSymbolTable::mark(const Klass* k, bool leakp) {
assert(k != NULL, "invariant");
traceid symbol_id = 0;
if (is_hidden_klass(k)) {
assert(k->is_instance_klass(), "invariant");
symbol_id = mark_hidden_klass_name((const InstanceKlass*)k, leakp);
} else {
Symbol* const sym = k->name();
if (sym != NULL) {
symbol_id = mark(sym, leakp);
}
}
assert(symbol_id > 0, "a symbol handler must mark the symbol for writing");
return symbol_id;
}
template <typename T>
traceid JfrSymbolTable::add_impl(const T* sym) {
assert(sym != NULL, "invariant");
assert(_instance != NULL, "invariant");
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
return instance().mark(sym);
}
traceid JfrSymbolTable::add(const Symbol* sym) {
return add_impl(sym);
}
traceid JfrSymbolTable::add(const char* str) {
return add_impl(str);
}

View File

@ -0,0 +1,148 @@
/*
* Copyright (c) 2021, 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_JFR_SUPPORT_JFRSYMBOLTABLE_HPP
#define SHARE_JFR_SUPPORT_JFRSYMBOLTABLE_HPP
#include "jfr/utilities/jfrHashtable.hpp"
#include "jfr/utilities/jfrTypes.hpp"
template <typename T, typename IdType>
class ListEntry : public JfrHashtableEntry<T, IdType> {
public:
ListEntry(uintptr_t hash, const T& data) : JfrHashtableEntry<T, IdType>(hash, data),
_list_next(NULL), _serialized(false), _unloading(false), _leakp(false) {}
const ListEntry<T, IdType>* list_next() const { return _list_next; }
void reset() const {
_list_next = NULL; _serialized = false; _unloading = false; _leakp = false;
}
void set_list_next(const ListEntry<T, IdType>* next) const { _list_next = next; }
bool is_serialized() const { return _serialized; }
void set_serialized() const { _serialized = true; }
bool is_unloading() const { return _unloading; }
void set_unloading() const { _unloading = true; }
bool is_leakp() const { return _leakp; }
void set_leakp() const { _leakp = true; }
private:
mutable const ListEntry<T, IdType>* _list_next;
mutable bool _serialized;
mutable bool _unloading;
mutable bool _leakp;
};
/*
* This table maps an oop/Symbol* or a char* to the Jfr type 'Symbol'.
*
* It provides an interface over the corresponding constant pool (TYPE_SYMBOL),
* which is represented in the binary format as a sequence of checkpoint events.
* The returned id can be used as a foreign key, but please note that the id is
* epoch-relative, and is therefore only valid in the current epoch / chunk.
* The table is cleared as part of rotation.
*
* Caller must ensure mutual exclusion by means of the ClassLoaderDataGraph_lock or by safepointing.
*/
class JfrSymbolTable : public JfrCHeapObj {
template <typename, typename, template<typename, typename> class, typename, size_t>
friend class HashTableHost;
typedef HashTableHost<const Symbol*, traceid, ListEntry, JfrSymbolTable> Symbols;
typedef HashTableHost<const char*, traceid, ListEntry, JfrSymbolTable> Strings;
friend class JfrArtifactSet;
public:
typedef Symbols::HashEntry SymbolEntry;
typedef Strings::HashEntry StringEntry;
static traceid add(const Symbol* sym);
static traceid add(const char* str);
private:
Symbols* _symbols;
Strings* _strings;
const SymbolEntry* _symbol_list;
const StringEntry* _string_list;
const Symbol* _symbol_query;
const char* _string_query;
traceid _id_counter;
bool _class_unload;
JfrSymbolTable();
~JfrSymbolTable();
static JfrSymbolTable* create();
static void destroy();
void clear();
void increment_checkpoint_id();
void set_class_unload(bool class_unload);
traceid mark(uintptr_t hash, const Symbol* sym, bool leakp);
traceid mark(const Klass* k, bool leakp);
traceid mark(const Symbol* sym, bool leakp = false);
traceid mark(const char* str, bool leakp = false);
traceid mark(uintptr_t hash, const char* str, bool leakp);
traceid bootstrap_name(bool leakp);
bool has_entries() const { return has_symbol_entries() || has_string_entries(); }
bool has_symbol_entries() const { return _symbol_list != NULL; }
bool has_string_entries() const { return _string_list != NULL; }
traceid mark_hidden_klass_name(const InstanceKlass* k, bool leakp);
bool is_hidden_klass(const Klass* k);
uintptr_t hidden_klass_name_hash(const InstanceKlass* ik);
// hashtable(s) callbacks
void on_link(const SymbolEntry* entry);
bool on_equals(uintptr_t hash, const SymbolEntry* entry);
void on_unlink(const SymbolEntry* entry);
void on_link(const StringEntry* entry);
bool on_equals(uintptr_t hash, const StringEntry* entry);
void on_unlink(const StringEntry* entry);
template <typename T>
static traceid add_impl(const T* sym);
template <typename T>
void assign_id(T* entry);
template <typename Functor>
void iterate_symbols(Functor& functor) {
iterate(functor, _symbol_list);
}
template <typename Functor>
void iterate_strings(Functor& functor) {
iterate(functor, _string_list);
}
template <typename Functor, typename T>
void iterate(Functor& functor, const T* list) {
const T* symbol = list;
while (symbol != NULL) {
const T* next = symbol->list_next();
functor(symbol);
symbol = next;
}
}
};
#endif // SHARE_JFR_SUPPORT_JFRSYMBOLTABLE_HPP

View File

@ -71,6 +71,7 @@
LOG_TAG(event) \
LOG_TAG(exceptions) \
LOG_TAG(exit) \
LOG_TAG(finalizer) \
LOG_TAG(fingerprint) \
DEBUG_ONLY(LOG_TAG(foreign)) \
LOG_TAG(free) \

View File

@ -83,6 +83,7 @@
#include "runtime/reflectionUtils.hpp"
#include "runtime/thread.inline.hpp"
#include "services/classLoadingService.hpp"
#include "services/finalizerService.hpp"
#include "services/threadService.hpp"
#include "utilities/dtrace.hpp"
#include "utilities/events.hpp"
@ -96,7 +97,6 @@
#include "jfr/jfrEvents.hpp"
#endif
#ifdef DTRACE_ENABLED
@ -1405,8 +1405,9 @@ instanceOop InstanceKlass::register_finalizer(instanceOop i, TRAPS) {
// Pass the handle as argument, JavaCalls::call expects oop as jobjects
JavaValue result(T_VOID);
JavaCallArguments args(h_i);
methodHandle mh (THREAD, Universe::finalizer_register_method());
methodHandle mh(THREAD, Universe::finalizer_register_method());
JavaCalls::call(&result, mh, &args, CHECK_NULL);
MANAGEMENT_ONLY(FinalizerService::on_register(h_i(), THREAD);)
return h_i();
}

View File

@ -104,6 +104,9 @@
#if INCLUDE_JFR
#include "jfr/jfr.hpp"
#endif
#if INCLUDE_MANAGEMENT
#include "services/finalizerService.hpp"
#endif
#include <errno.h>
@ -681,6 +684,12 @@ JVM_ENTRY(jobject, JVM_Clone(JNIEnv* env, jobject handle))
return JNIHandles::make_local(THREAD, new_obj());
JVM_END
// java.lang.ref.Finalizer ////////////////////////////////////////////////////
JVM_ENTRY(void, JVM_ReportFinalizationComplete(JNIEnv * env, jobject finalizee))
MANAGEMENT_ONLY(FinalizerService::on_complete(JNIHandles::resolve_non_null(finalizee), THREAD);)
JVM_END
// java.io.File ///////////////////////////////////////////////////////////////
JVM_LEAF(char*, JVM_NativePath(char* path))
@ -3851,3 +3860,4 @@ JVM_END
JVM_ENTRY_NO_ENV(jint, JVM_FindSignal(const char *name))
return os::get_signal_number(name);
JVM_END

View File

@ -305,6 +305,7 @@ void mutex_init() {
#if INCLUDE_JFR
def(JfrBuffer_lock , PaddedMutex , nosafepoint, true);
def(JfrMsg_lock , PaddedMonitor, nosafepoint-3, true);
def(JfrStacktrace_lock , PaddedMutex , stackwatermark-1, true);
def(JfrThreadSampler_lock , PaddedMonitor, nosafepoint, true);
#endif
@ -365,10 +366,6 @@ void mutex_init() {
defl(Module_lock , PaddedMutex , ClassLoaderDataGraph_lock, false);
defl(SystemDictionary_lock , PaddedMonitor, Module_lock, true);
defl(JNICritical_lock , PaddedMonitor, MultiArray_lock, true); // used for JNI critical regions
#if INCLUDE_JFR
defl(JfrMsg_lock , PaddedMonitor, Module_lock, true);
#endif
}
GCMutexLocker::GCMutexLocker(Mutex* mutex) {

View File

@ -47,6 +47,7 @@
#include "prims/resolvedMethodTable.hpp"
#include "services/diagnosticArgument.hpp"
#include "services/diagnosticFramework.hpp"
#include "services/finalizerService.hpp"
#include "services/gcNotifier.hpp"
#include "services/lowMemoryDetector.hpp"
#include "services/threadIdTable.hpp"
@ -115,6 +116,7 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
bool has_dcmd_notification_event = false;
bool stringtable_work = false;
bool symboltable_work = false;
bool finalizerservice_work = false;
bool resolved_method_table_work = false;
bool thread_id_table_work = false;
bool protection_domain_table_work = false;
@ -145,6 +147,7 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
(has_dcmd_notification_event = (!UseNotificationThread && DCmdFactory::has_pending_jmx_notification())) |
(stringtable_work = StringTable::has_work()) |
(symboltable_work = SymbolTable::has_work()) |
(finalizerservice_work = FinalizerService::has_work()) |
(resolved_method_table_work = ResolvedMethodTable::has_work()) |
(thread_id_table_work = ThreadIdTable::has_work()) |
(protection_domain_table_work = SystemDictionary::pd_cache_table()->has_work()) |
@ -172,6 +175,10 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
SymbolTable::do_concurrent_work(jt);
}
if (finalizerservice_work) {
FinalizerService::do_concurrent_work(jt);
}
if (has_jvmti_events) {
_jvmti_event->post();
_jvmti_event = NULL; // reset

View File

@ -119,6 +119,93 @@ void ClassLoadingService::init() {
}
}
bool ClassLoadingService::set_verbose(bool verbose) {
MutexLocker m(Management_lock);
// verbose will be set to the previous value
LogLevelType level = verbose ? LogLevel::Info : LogLevel::Off;
LogConfiguration::configure_stdout(level, false, LOG_TAGS(class, load));
reset_trace_class_unloading();
return verbose;
}
// Caller to this function must own Management_lock
void ClassLoadingService::reset_trace_class_unloading() {
assert(Management_lock->owned_by_self(), "Must own the Management_lock");
bool value = MemoryService::get_verbose() || ClassLoadingService::get_verbose();
LogLevelType level = value ? LogLevel::Info : LogLevel::Off;
LogConfiguration::configure_stdout(level, false, LOG_TAGS(class, unload));
}
jlong ClassLoadingService::loaded_class_count() {
return _classes_loaded_count->get_value() + _shared_classes_loaded_count->get_value();
}
jlong ClassLoadingService::unloaded_class_count() {
return _classes_unloaded_count->get_value() + _shared_classes_unloaded_count->get_value();
}
jlong ClassLoadingService::loaded_class_bytes() {
return UsePerfData ? _classbytes_loaded->get_value() + _shared_classbytes_loaded->get_value() : -1;
}
jlong ClassLoadingService::unloaded_class_bytes() {
return UsePerfData ? _classbytes_unloaded->get_value() + _shared_classbytes_unloaded->get_value() : -1;
}
jlong ClassLoadingService::loaded_shared_class_count() {
return _shared_classes_loaded_count->get_value();
}
jlong ClassLoadingService::unloaded_shared_class_count() {
return _shared_classes_unloaded_count->get_value();
}
jlong ClassLoadingService::loaded_shared_class_bytes() {
return UsePerfData ? _shared_classbytes_loaded->get_value() : -1;
}
jlong ClassLoadingService::unloaded_shared_class_bytes() {
return UsePerfData ? _shared_classbytes_unloaded->get_value() : -1;
}
jlong ClassLoadingService::class_method_data_size() {
return UsePerfData ? _class_methods_size->get_value() : -1;
}
static size_t compute_class_size(InstanceKlass* k) {
// lifted from ClassStatistics.do_class(Klass* k)
size_t class_size = k->size();
if (k->is_instance_klass()) {
class_size += k->methods()->size();
// FIXME: Need to count the contents of methods
class_size += k->constants()->size();
class_size += k->local_interfaces()->size();
if (k->transitive_interfaces() != NULL) {
class_size += k->transitive_interfaces()->size();
}
// We do not have to count implementors, since we only store one!
// FIXME: How should these be accounted for, now when they have moved.
//class_size += k->fields()->size();
}
return class_size * oopSize;
}
void ClassLoadingService::notify_class_loaded(InstanceKlass* k, bool shared_class) {
DTRACE_CLASSLOAD_PROBE(loaded, k, shared_class);
PerfCounter* classes_counter = (shared_class ? _shared_classes_loaded_count
: _classes_loaded_count);
// increment the count
classes_counter->inc();
if (UsePerfData) {
PerfCounter* classbytes_counter = (shared_class ? _shared_classbytes_loaded
: _classbytes_loaded);
// add the class size
size_t size = compute_class_size(k);
classbytes_counter->inc(size);
}
}
void ClassLoadingService::notify_class_unloaded(InstanceKlass* k) {
DTRACE_CLASSLOAD_PROBE(unloaded, k, false);
// Classes that can be unloaded must be non-shared
@ -139,59 +226,4 @@ void ClassLoadingService::notify_class_unloaded(InstanceKlass* k) {
}
}
void ClassLoadingService::notify_class_loaded(InstanceKlass* k, bool shared_class) {
DTRACE_CLASSLOAD_PROBE(loaded, k, shared_class);
PerfCounter* classes_counter = (shared_class ? _shared_classes_loaded_count
: _classes_loaded_count);
// increment the count
classes_counter->inc();
if (UsePerfData) {
PerfCounter* classbytes_counter = (shared_class ? _shared_classbytes_loaded
: _classbytes_loaded);
// add the class size
size_t size = compute_class_size(k);
classbytes_counter->inc(size);
}
}
size_t ClassLoadingService::compute_class_size(InstanceKlass* k) {
// lifted from ClassStatistics.do_class(Klass* k)
size_t class_size = 0;
class_size += k->size();
if (k->is_instance_klass()) {
class_size += k->methods()->size();
// FIXME: Need to count the contents of methods
class_size += k->constants()->size();
class_size += k->local_interfaces()->size();
if (k->transitive_interfaces() != NULL) {
class_size += k->transitive_interfaces()->size();
}
// We do not have to count implementors, since we only store one!
// FIXME: How should these be accounted for, now when they have moved.
//class_size += k->fields()->size();
}
return class_size * oopSize;
}
bool ClassLoadingService::set_verbose(bool verbose) {
MutexLocker m(Management_lock);
// verbose will be set to the previous value
LogLevelType level = verbose ? LogLevel::Info : LogLevel::Off;
LogConfiguration::configure_stdout(level, false, LOG_TAGS(class, load));
reset_trace_class_unloading();
return verbose;
}
// Caller to this function must own Management_lock
void ClassLoadingService::reset_trace_class_unloading() {
assert(Management_lock->owned_by_self(), "Must own the Management_lock");
bool value = MemoryService::get_verbose() || ClassLoadingService::get_verbose();
LogLevelType level = value ? LogLevel::Info : LogLevel::Off;
LogConfiguration::configure_stdout(level, false, LOG_TAGS(class, unload));
}
#endif // INCLUDE_MANAGEMENT

View File

@ -35,7 +35,7 @@ class InstanceKlass;
// VM monitoring and management support for the Class Loading subsystem
class ClassLoadingService : public AllStatic {
private:
private:
// Counters for classes loaded from class files
static PerfCounter* _classes_loaded_count;
static PerfCounter* _classes_unloaded_count;
@ -50,59 +50,20 @@ private:
static PerfVariable* _class_methods_size;
static size_t compute_class_size(InstanceKlass* k);
public:
static void init();
static bool get_verbose() { return log_is_enabled(Info, class, load); }
static bool set_verbose(bool verbose);
public:
static void init() NOT_MANAGEMENT_RETURN;
static bool set_verbose(bool verbose) NOT_MANAGEMENT_RETURN_(false);
static void reset_trace_class_unloading() NOT_MANAGEMENT_RETURN;
static jlong loaded_class_count() {
return _classes_loaded_count->get_value() + _shared_classes_loaded_count->get_value();
}
static jlong unloaded_class_count() {
return _classes_unloaded_count->get_value() + _shared_classes_unloaded_count->get_value();
}
static jlong loaded_class_bytes() {
if (UsePerfData) {
return _classbytes_loaded->get_value() + _shared_classbytes_loaded->get_value();
} else {
return -1;
}
}
static jlong unloaded_class_bytes() {
if (UsePerfData) {
return _classbytes_unloaded->get_value() + _shared_classbytes_unloaded->get_value();
} else {
return -1;
}
}
static jlong loaded_shared_class_count() {
return _shared_classes_loaded_count->get_value();
}
static jlong unloaded_shared_class_count() {
return _shared_classes_unloaded_count->get_value();
}
static jlong loaded_shared_class_bytes() {
if (UsePerfData) {
return _shared_classbytes_loaded->get_value();
} else {
return -1;
}
}
static jlong unloaded_shared_class_bytes() {
if (UsePerfData) {
return _shared_classbytes_unloaded->get_value();
} else {
return -1;
}
}
static jlong class_method_data_size() {
return (UsePerfData ? _class_methods_size->get_value() : -1);
}
static jlong loaded_class_count() NOT_MANAGEMENT_RETURN_(0L);
static jlong unloaded_class_count() NOT_MANAGEMENT_RETURN_(0L);
static jlong loaded_class_bytes() NOT_MANAGEMENT_RETURN_(0L);
static jlong unloaded_class_bytes() NOT_MANAGEMENT_RETURN_(0L);
static jlong loaded_shared_class_count() NOT_MANAGEMENT_RETURN_(0L);
static jlong unloaded_shared_class_count() NOT_MANAGEMENT_RETURN_(0L);
static jlong loaded_shared_class_bytes() NOT_MANAGEMENT_RETURN_(0L);
static jlong unloaded_shared_class_bytes() NOT_MANAGEMENT_RETURN_(0L);
static jlong class_method_data_size() NOT_MANAGEMENT_RETURN_(0L);
static bool get_verbose() { return log_is_enabled(Info, class, load); }
static void notify_class_loaded(InstanceKlass* k, bool shared_class)
NOT_MANAGEMENT_RETURN;

View File

@ -0,0 +1,313 @@
/*
* Copyright (c) 2021, 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 "utilities/macros.hpp"
#if INCLUDE_MANAGEMENT
#include "classfile/classLoaderDataGraph.inline.hpp"
#include "memory/resourceArea.hpp"
#include "logging/log.hpp"
#include "runtime/atomic.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/synchronizer.hpp"
#include "runtime/thread.inline.hpp"
#include "services/finalizerService.hpp"
#include "utilities/concurrentHashTableTasks.inline.hpp"
#include "utilities/debug.hpp"
FinalizerEntry::FinalizerEntry(const InstanceKlass* ik) :
_ik(ik),
_objects_on_heap(0),
_total_finalizers_run(0) {}
const InstanceKlass* FinalizerEntry::klass() const {
return _ik;
}
uintptr_t FinalizerEntry::objects_on_heap() const {
return Atomic::load(&_objects_on_heap);
}
uintptr_t FinalizerEntry::total_finalizers_run() const {
return Atomic::load(&_total_finalizers_run);
}
void FinalizerEntry::on_register() {
Atomic::inc(&_objects_on_heap, memory_order_relaxed);
}
void FinalizerEntry::on_complete() {
Atomic::inc(&_total_finalizers_run, memory_order_relaxed);
Atomic::dec(&_objects_on_heap, memory_order_relaxed);
}
static inline uintx hash_function(const InstanceKlass* ik) {
assert(ik != nullptr, "invariant");
return primitive_hash(ik);
}
static inline uintx hash_function(const FinalizerEntry* fe) {
return hash_function(fe->klass());
}
class FinalizerEntryLookup : StackObj {
private:
const InstanceKlass* const _ik;
public:
FinalizerEntryLookup(const InstanceKlass* ik) : _ik(ik) {}
uintx get_hash() const { return hash_function(_ik); }
bool equals(FinalizerEntry** value, bool* is_dead) {
assert(value != nullptr, "invariant");
assert(*value != nullptr, "invariant");
return (*value)->klass() == _ik;
}
};
class FinalizerTableConfig : public AllStatic {
public:
typedef FinalizerEntry* Value; // value of the Node in the hashtable
static uintx get_hash(Value const& value, bool* is_dead) {
return hash_function(value);
}
static void* allocate_node(void* context, size_t size, Value const& value) {
return AllocateHeap(size, mtServiceability);
}
static void free_node(void* context, void* memory, Value const& value) {
FreeHeap(memory);
}
};
typedef ConcurrentHashTable<FinalizerTableConfig, mtServiceability> FinalizerHashtable;
static FinalizerHashtable* _table = nullptr;
static const size_t DEFAULT_TABLE_SIZE = 2048;
// 2^24 is max size, like StringTable.
static const size_t MAX_SIZE = 24;
static volatile bool _has_work = false;
static size_t ceil_log2(size_t value) {
size_t ret;
for (ret = 1; ((size_t)1 << ret) < value; ++ret);
return ret;
}
class FinalizerEntryLookupResult {
private:
FinalizerEntry* _result;
public:
FinalizerEntryLookupResult() : _result(nullptr) {}
void operator()(FinalizerEntry* node) {
assert(node != nullptr, "invariant");
_result = node;
}
FinalizerEntry* result() const { return _result; }
};
class FinalizerEntryLookupGet {
private:
FinalizerEntry* _result;
public:
FinalizerEntryLookupGet() : _result(nullptr) {}
void operator()(FinalizerEntry** node) {
assert(node != nullptr, "invariant");
_result = *node;
}
FinalizerEntry* result() const { return _result; }
};
static inline void set_has_work(bool value) {
Atomic::store(&_has_work, value);
}
static inline bool has_work() {
return Atomic::load(&_has_work);
}
static void request_resize() {
if (!has_work()) {
MutexLocker ml(Service_lock, Mutex::_no_safepoint_check_flag);
if (!has_work()) {
set_has_work(true);
Service_lock->notify_all();
}
}
}
static FinalizerEntry* add_to_table_if_needed(const InstanceKlass* ik, Thread* thread) {
FinalizerEntryLookup lookup(ik);
FinalizerEntry* entry = nullptr;
bool grow_hint = false;
do {
// We have looked up the entry once, proceed with insertion.
entry = new FinalizerEntry(ik);
if (_table->insert(thread, lookup, entry, &grow_hint)) {
break;
}
// In case another thread did a concurrent add, return value already in the table.
// This could fail if the entry got deleted concurrently, so loop back until success.
FinalizerEntryLookupGet felg;
if (_table->get(thread, lookup, felg, &grow_hint)) {
entry = felg.result();
break;
}
} while (true);
if (grow_hint) {
request_resize();
}
assert(entry != nullptr, "invariant");
return entry;
}
static void do_table_concurrent_work(JavaThread* jt) {
if (!_table->is_max_size_reached()) {
FinalizerHashtable::GrowTask gt(_table);
if (!gt.prepare(jt)) {
return;
}
while (gt.do_task(jt)) {
gt.pause(jt);
{
ThreadBlockInVM tbivm(jt);
}
gt.cont(jt);
}
gt.done(jt);
}
set_has_work(false);
}
bool FinalizerService::has_work() {
return ::has_work();
}
void FinalizerService::do_concurrent_work(JavaThread* service_thread) {
assert(service_thread != nullptr, "invariant");
assert(has_work(), "invariant");
do_table_concurrent_work(service_thread);
}
void FinalizerService::init() {
assert(_table == nullptr, "invariant");
const size_t start_size_log_2 = ceil_log2(DEFAULT_TABLE_SIZE);
_table = new FinalizerHashtable(start_size_log_2, MAX_SIZE);
}
static FinalizerEntry* lookup_entry(const InstanceKlass* ik, Thread* thread) {
FinalizerEntryLookup lookup(ik);
FinalizerEntryLookupGet felg;
_table->get(thread, lookup, felg);
return felg.result();
}
const FinalizerEntry* FinalizerService::lookup(const InstanceKlass* ik, Thread* thread) {
assert(ik != nullptr, "invariant");
assert(thread != nullptr, "invariant");
assert(ik->has_finalizer(), "invariant");
return lookup_entry(ik, thread);
}
// Add if not exist.
static FinalizerEntry* get_entry(const InstanceKlass* ik, Thread* thread) {
assert(ik != nullptr, "invariant");
assert(ik->has_finalizer(), "invariant");
FinalizerEntry* const entry = lookup_entry(ik, thread);
return entry != nullptr ? entry : add_to_table_if_needed(ik, thread);
}
static FinalizerEntry* get_entry(oop finalizee, Thread* thread) {
assert(finalizee != nullptr, "invariant");
assert(finalizee->is_instance(), "invariant");
return get_entry(InstanceKlass::cast(finalizee->klass()), thread);
}
static void log_registered(oop finalizee, Thread* thread) {
ResourceMark rm(thread);
const intptr_t identity_hash = ObjectSynchronizer::FastHashCode(thread, finalizee);
log_info(finalizer)("Registered object (" INTPTR_FORMAT ") of class %s as finalizable", identity_hash, finalizee->klass()->external_name());
}
void FinalizerService::on_register(oop finalizee, Thread* thread) {
FinalizerEntry* const fe = get_entry(finalizee, thread);
assert(fe != nullptr, "invariant");
fe->on_register();
if (log_is_enabled(Info, finalizer)) {
log_registered(finalizee, thread);
}
}
static void log_completed(oop finalizee, Thread* thread) {
ResourceMark rm(thread);
const intptr_t identity_hash = ObjectSynchronizer::FastHashCode(thread, finalizee);
log_info(finalizer)("Finalizer was run for object (" INTPTR_FORMAT ") of class %s", identity_hash, finalizee->klass()->external_name());
}
void FinalizerService::on_complete(oop finalizee, JavaThread* finalizer_thread) {
FinalizerEntry* const fe = get_entry(finalizee, finalizer_thread);
assert(fe != nullptr, "invariant");
fe->on_complete();
if (log_is_enabled(Info, finalizer)) {
log_completed(finalizee, finalizer_thread);
}
}
class FinalizerScan : public StackObj {
private:
FinalizerEntryClosure* _closure;
public:
FinalizerScan(FinalizerEntryClosure* closure) : _closure(closure) {}
bool operator()(FinalizerEntry** fe) {
return _closure->do_entry(*fe);
}
};
void FinalizerService::do_entries(FinalizerEntryClosure* closure, Thread* thread) {
assert(closure != nullptr, "invariant");
FinalizerScan scan(closure);
_table->do_scan(thread, scan);
}
static bool remove_entry(const InstanceKlass* ik) {
assert(ik != nullptr, "invariant");
FinalizerEntryLookup lookup(ik);
return _table->remove(Thread::current(), lookup);
}
static void on_unloading(Klass* klass) {
assert(klass != nullptr, "invariant");
if (!klass->is_instance_klass()) {
return;
}
const InstanceKlass* const ik = InstanceKlass::cast(klass);
if (ik->has_finalizer()) {
remove_entry(ik);
}
}
void FinalizerService::purge_unloaded() {
assert_locked_or_safepoint(ClassLoaderDataGraph_lock);
ClassLoaderDataGraph::classes_unloading_do(&on_unloading);
}
#endif // INCLUDE_MANAGEMENT

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2021, 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_SERVICES_FINALIZERSERVICE_HPP
#define SHARE_SERVICES_FINALIZERSERVICE_HPP
#include "memory/allocation.hpp"
#include "oops/oopsHierarchy.hpp"
class InstanceKlass;
class JavaThread;
class Thread;
class FinalizerEntry : public CHeapObj<mtServiceability> {
private:
const InstanceKlass* const _ik;
uintptr_t _objects_on_heap;
uintptr_t _total_finalizers_run;
public:
FinalizerEntry(const InstanceKlass* ik);
const InstanceKlass* klass() const NOT_MANAGEMENT_RETURN_(nullptr);
uintptr_t objects_on_heap() const NOT_MANAGEMENT_RETURN_(0L);
uintptr_t total_finalizers_run() const NOT_MANAGEMENT_RETURN_(0L);
void on_register() NOT_MANAGEMENT_RETURN;
void on_complete() NOT_MANAGEMENT_RETURN;
};
class FinalizerEntryClosure : public StackObj {
public:
virtual bool do_entry(const FinalizerEntry* fe) = 0;
};
class FinalizerService : AllStatic {
friend class ServiceThread;
private:
static bool has_work() NOT_MANAGEMENT_RETURN_(false);
static void do_concurrent_work(JavaThread* service_thread) NOT_MANAGEMENT_RETURN;
public:
static void init() NOT_MANAGEMENT_RETURN;
static void purge_unloaded() NOT_MANAGEMENT_RETURN;
static void on_register(oop finalizee, Thread* thread) NOT_MANAGEMENT_RETURN;
static void on_complete(oop finalizee, JavaThread* finalizer_thread) NOT_MANAGEMENT_RETURN;
static void do_entries(FinalizerEntryClosure* closure, Thread* thread) NOT_MANAGEMENT_RETURN;
static const FinalizerEntry* lookup(const InstanceKlass* ik, Thread* thread) NOT_MANAGEMENT_RETURN_(nullptr);
};
#endif // SHARE_SERVICES_FINALIZERSERVICE_HPP

View File

@ -55,6 +55,7 @@
#include "services/classLoadingService.hpp"
#include "services/diagnosticCommand.hpp"
#include "services/diagnosticFramework.hpp"
#include "services/finalizerService.hpp"
#include "services/writeableFlags.hpp"
#include "services/heapDumper.hpp"
#include "services/lowMemoryDetector.hpp"
@ -94,6 +95,7 @@ void management_init() {
ThreadService::init();
RuntimeService::init();
ClassLoadingService::init();
FinalizerService::init();
#else
ThreadService::init();
#endif // INCLUDE_MANAGEMENT
@ -206,6 +208,15 @@ InstanceKlass* Management::initialize_klass(Klass* k, TRAPS) {
return ik;
}
void Management::record_vm_init_completed() {
// Initialize the timestamp to get the current time
_vm_init_done_time->set_value(os::javaTimeMillis());
// Update the timestamp to the vm init done time
_stamp.update();
}
void Management::record_vm_startup_time(jlong begin, jlong duration) {
// if the performance counter is not initialized,
// then vm initialization failed; simply return.
@ -216,6 +227,14 @@ void Management::record_vm_startup_time(jlong begin, jlong duration) {
PerfMemory::set_accessible(true);
}
jlong Management::begin_vm_creation_time() {
return _begin_vm_creation_time->get_value();
}
jlong Management::vm_init_done_time() {
return _vm_init_done_time->get_value();
}
jlong Management::timestamp() {
TimeStamp t;
t.update();

View File

@ -70,20 +70,10 @@ public:
static void record_vm_startup_time(jlong begin, jlong duration)
NOT_MANAGEMENT_RETURN;
static void record_vm_init_completed() {
// Initialize the timestamp to get the current time
_vm_init_done_time->set_value(os::javaTimeMillis());
static void record_vm_init_completed() NOT_MANAGEMENT_RETURN;
// Update the timestamp to the vm init done time
_stamp.update();
}
static jlong begin_vm_creation_time() {
return _begin_vm_creation_time->get_value();
}
static jlong vm_init_done_time() {
return _vm_init_done_time->get_value();
}
static jlong begin_vm_creation_time() NOT_MANAGEMENT_RETURN_(0L);
static jlong vm_init_done_time() NOT_MANAGEMENT_RETURN_(0L);
// methods to return a Klass*.
static InstanceKlass* java_lang_management_ThreadInfo_klass(TRAPS);

View File

@ -126,9 +126,11 @@
#if INCLUDE_MANAGEMENT
#define NOT_MANAGEMENT_RETURN /* next token must be ; */
#define NOT_MANAGEMENT_RETURN_(code) /* next token must be ; */
#define MANAGEMENT_ONLY(x) x
#else
#define NOT_MANAGEMENT_RETURN {}
#define NOT_MANAGEMENT_RETURN_(code) { return code; }
#define MANAGEMENT_ONLY(x)
#endif // INCLUDE_MANAGEMENT
#ifndef INCLUDE_EPSILONGC

View File

@ -86,6 +86,7 @@ final class Finalizer extends FinalReference<Object> { /* Package-private; must
assert finalizee != null;
if (!(finalizee instanceof java.lang.Enum)) {
jla.invokeFinalize(finalizee);
reportComplete(finalizee);
// Clear stack slot containing this variable, to decrease
// the chances of false retention with a conservative GC
@ -95,6 +96,8 @@ final class Finalizer extends FinalReference<Object> { /* Package-private; must
super.clear();
}
private static native void reportComplete(Object finalizee);
/* Create a privileged secondary finalizer thread in the system thread
* group for the given Runnable, and wait for it to complete.
*

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2021, 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.
*/
#include "jvm.h"
#include "java_lang_ref_Finalizer.h"
JNIEXPORT void JNICALL
Java_java_lang_ref_Finalizer_reportComplete(JNIEnv* env, jclass cls, jobject finalizee) {
JVM_ReportFinalizationComplete(env, finalizee);
}

View File

@ -807,6 +807,11 @@
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.FinalizerStatistics">
<setting name="enabled">true</setting>
<setting name="period">endChunk</setting>
</event>

View File

@ -807,6 +807,11 @@
<setting name="stackTrace">true</setting>
</event>
<event name="jdk.FinalizerStatistics">
<setting name="enabled">true</setting>
<setting name="period">endChunk</setting>
</event>

View File

@ -0,0 +1,129 @@
/*
* Copyright (c) 2021, 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.
*/
package jdk.jfr.event.runtime;
import java.util.List;
import jdk.jfr.Recording;
import jdk.jfr.consumer.RecordedClass;
import jdk.jfr.consumer.RecordedEvent;
import jdk.test.lib.Asserts;
import jdk.test.lib.jfr.EventNames;
import jdk.test.lib.jfr.Events;
import jdk.test.lib.jfr.TestClassLoader;
/**
* @test
* @summary The test verifies that classes overriding finalize() are represented as events.
* @key jfr
* @requires vm.hasJFR
* @library /test/lib /test/jdk
* @run main/othervm -Xlog:class+unload,finalizer -Xmx16m jdk.jfr.event.runtime.TestFinalizerStatisticsEvent
*/
public final class TestFinalizerStatisticsEvent {
private final static String TEST_CLASS_NAME = "jdk.jfr.event.runtime.TestFinalizerStatisticsEvent$TestClassOverridingFinalize";
private final static String TEST_CLASS_UNLOAD_NAME = "jdk.jfr.event.runtime.TestFinalizerStatisticsEvent$TestClassUnloadOverridingFinalize";
private final static String EVENT_PATH = EventNames.FinalizerStatistics;
// Declare as public static to prevent the compiler from optimizing away all unread writes
public static TestClassLoader unloadableClassLoader;
public static Class<?> unloadOverrideClass;
public static Object overridingInstance;
public static void main(String[] args) throws Throwable {
Recording recording1 = new Recording();
recording1.enable(EVENT_PATH);
Recording recording2 = new Recording();
recording2.enable(EVENT_PATH);
recording1.start();
allocateAndGC();
recording2.start(); // rotation writes an event for TEST_CLASS_NAME into recording1
unloadableClassLoader = new TestClassLoader();
unloadOverrideClass = unloadableClassLoader.loadClass(TEST_CLASS_UNLOAD_NAME);
unloadOverrideClass = null;
unloadableClassLoader = null;
allocateAndGC(); // the unloading of class TEST_CLASS_UNLOAD_NAME is intercepted and an event is written into both recording1 and recording2
recording2.stop(); // rotation writes an event for TEST_CLASS_NAME into both recording1 and recording2
allocateAndGC();
recording1.stop(); // rotation writes an event for TEST_CLASS_NAME into recording1 which now has 4 events reflecting this test case (3 chunks + 1 unload)
try {
verify(recording2);
verify(recording1);
}
finally {
recording2.close();
recording1.close();
}
}
private static void allocateAndGC() {
overridingInstance = new TestClassOverridingFinalize();
overridingInstance = null;
System.gc();
}
private static void verify(Recording recording) throws Throwable {
boolean foundTestClassName = false;
boolean foundTestClassUnloadName = false;
List<RecordedEvent> events = Events.fromRecording(recording);
Events.hasEvents(events);
for (RecordedEvent event : events) {
System.out.println("Event:" + event);
RecordedClass overridingClass = event.getValue("finalizableClass");
switch (overridingClass.getName()) {
case TEST_CLASS_NAME: {
Asserts.assertTrue(event.getString("codeSource").startsWith("file://"));
foundTestClassName = true;
break;
}
case TEST_CLASS_UNLOAD_NAME: {
Asserts.assertTrue(event.getString("codeSource").startsWith("file://"));
foundTestClassUnloadName = true;
break;
}
}
}
Asserts.assertTrue(foundTestClassName, "The class: " + TEST_CLASS_NAME + " overriding finalize() is not found");
Asserts.assertTrue(foundTestClassUnloadName, "The class: " + TEST_CLASS_UNLOAD_NAME + " overriding finalize() is not found");
}
static public class TestClassOverridingFinalize {
public boolean finalized = false;
@Override
protected void finalize() {
finalized = true;
}
}
static public class TestClassUnloadOverridingFinalize {
public boolean finalized = false;
@Override
protected void finalize() {
finalized = true;
}
}
}

View File

@ -88,6 +88,7 @@ public class EventNames {
public static final String RedefineClasses = PREFIX + "RedefineClasses";
public static final String RetransformClasses = PREFIX + "RetransformClasses";
public static final String ClassRedefinition = PREFIX + "ClassRedefinition";
public static final String FinalizerStatistics = PREFIX + "FinalizerStatistics";
// This event is hard to test
public final static String ReservedStackActivation = PREFIX + "ReservedStackActivation";

View File

@ -28,6 +28,10 @@ package jdk.test.lib.jfr;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.security.cert.Certificate;
import java.security.CodeSource;
import java.security.ProtectionDomain;
/**
* Custom class loader which will try to load the class via getResourceAsStream().
@ -53,7 +57,10 @@ public class TestClassLoader extends ClassLoader {
dis = new DataInputStream(is);
dis.readFully(buf);
dis.close();
return defineClass(name, buf, 0, buf.length);
URL url = getResource(resourceName);
CodeSource cs = new CodeSource(url, (Certificate[])null);
ProtectionDomain pd = new ProtectionDomain(cs, null);
return defineClass(name, buf, 0, buf.length, pd);
}
} catch (SecurityException e) {
// This error will happen quite often (for example when loading