Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8266936: Add a finalization JFR event #4731

Closed
wants to merge 28 commits into from
Closed
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
91fc403
8266936: Add a finalization JFR event
mgronlun Jun 16, 2021
2eb336d
shortcut calling Jfr::is_recording()
mgronlun Jun 16, 2021
cf14f67
8266936-alt: Add a finalization JFR event
mgronlun Jun 17, 2021
e652958
whitespace
mgronlun Jun 18, 2021
7aa9f81
update
mgronlun Jul 8, 2021
24fdf51
update
mgronlun Jul 8, 2021
4f34a7f
whitespace
mgronlun Jul 9, 2021
9969020
Code Source attribute for Finalizer event
mgronlun Jul 27, 2021
baa5dce
remove build directive
mgronlun Jul 27, 2021
1daa2b2
FinalizerTable for extending runtime management to finalizers
mgronlun Aug 19, 2021
6435a98
lock ranking
mgronlun Aug 23, 2021
7d2a030
enqueued data point
mgronlun Aug 23, 2021
74697ee
Model as finalizerService
mgronlun Aug 24, 2021
bc3fbca
FinalizerStatistics
mgronlun Aug 26, 2021
064307c
cleanup
mgronlun Aug 27, 2021
256948c
localize
mgronlun Aug 27, 2021
e365b01
mtStatistics
mgronlun Sep 2, 2021
f79c7d0
remove rehashing and rely on default grow_hint for table resize
mgronlun Sep 13, 2021
e0d800b
no precompiled headers and mtServiceability nmt classification
mgronlun Sep 24, 2021
c9da07c
jvm.h and JVM_Entry
mgronlun Sep 25, 2021
68c3832
symbols-unix
mgronlun Sep 25, 2021
d293953
rebased and adjusted for new lock rankings
mgronlun Oct 13, 2021
96a9d6b
cleanup
mgronlun Oct 13, 2021
40fafca
spelling
mgronlun Oct 14, 2021
9b41814
renames
mgronlun Oct 15, 2021
d10eb30
ThreadBlockInVM instead of ThreadToNativeFromVM for sampling exclusion
mgronlun Oct 16, 2021
85a4626
relax memory ordering constraint
mgronlun Oct 18, 2021
b3268c9
no constexpr for constant values
mgronlun Oct 18, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -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,30 +495,27 @@ 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);
}
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>
@@ -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
mgronlun marked this conversation as resolved.
Show resolved Hide resolved
#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
}