mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-27 19:33:39 +00:00
8266936: Add a finalization JFR event
Reviewed-by: coleenp, mchung, egahlin
This commit is contained in:
parent
bcbe3845d9
commit
72a976ef05
@ -183,6 +183,7 @@ JVM_ReferenceRefersTo
|
||||
JVM_RegisterLambdaProxyClassForArchiving
|
||||
JVM_RegisterSignal
|
||||
JVM_ReleaseUTF
|
||||
JVM_ReportFinalizationComplete
|
||||
JVM_ResumeThread
|
||||
JVM_SetArrayElement
|
||||
JVM_SetClassSigners
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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
|
||||
************************************************************************/
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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>
|
||||
|
||||
131
src/hotspot/share/jfr/periodic/jfrFinalizerStatisticsEvent.cpp
Normal file
131
src/hotspot/share/jfr/periodic/jfrFinalizerStatisticsEvent.cpp
Normal 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
|
||||
@ -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
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
316
src/hotspot/share/jfr/support/jfrSymbolTable.cpp
Normal file
316
src/hotspot/share/jfr/support/jfrSymbolTable.cpp
Normal 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);
|
||||
}
|
||||
148
src/hotspot/share/jfr/support/jfrSymbolTable.hpp
Normal file
148
src/hotspot/share/jfr/support/jfrSymbolTable.hpp
Normal 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
|
||||
@ -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) \
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
313
src/hotspot/share/services/finalizerService.cpp
Normal file
313
src/hotspot/share/services/finalizerService.cpp
Normal 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
|
||||
68
src/hotspot/share/services/finalizerService.hpp
Normal file
68
src/hotspot/share/services/finalizerService.hpp
Normal 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
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
*
|
||||
|
||||
35
src/java.base/share/native/libjava/Finalizer.c
Normal file
35
src/java.base/share/native/libjava/Finalizer.c
Normal 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);
|
||||
}
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
129
test/jdk/jdk/jfr/event/runtime/TestFinalizerStatisticsEvent.java
Normal file
129
test/jdk/jdk/jfr/event/runtime/TestFinalizerStatisticsEvent.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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";
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user