Skip to content
Permalink
Browse files
8266936: Add a finalization JFR event
Reviewed-by: coleenp, mchung, egahlin
  • Loading branch information
Markus Grönlund committed Oct 18, 2021
1 parent bcbe384 commit 72a976ef05fc2c62657920a560a0abc60b27c852
Showing with 1,508 additions and 542 deletions.
  1. +1 −0 make/data/hotspot-symbols/symbols-unix
  2. +2 −1 src/hotspot/share/classfile/systemDictionary.cpp
  3. +6 −0 src/hotspot/share/include/jvm.h
  4. +28 −23 src/hotspot/share/jfr/jni/jfrJavaSupport.cpp
  5. +3 −2 src/hotspot/share/jfr/jni/jfrJavaSupport.hpp
  6. +7 −0 src/hotspot/share/jfr/metadata/metadata.xml
  7. +131 −0 src/hotspot/share/jfr/periodic/jfrFinalizerStatisticsEvent.cpp
  8. +38 −0 src/hotspot/share/jfr/periodic/jfrFinalizerStatisticsEvent.hpp
  9. +13 −1 src/hotspot/share/jfr/periodic/jfrPeriodic.cpp
  10. +41 −50 src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSet.cpp
  11. +26 −232 src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.cpp
  12. +11 −101 src/hotspot/share/jfr/recorder/checkpoint/types/jfrTypeSetUtils.hpp
  13. +6 −2 src/hotspot/share/jfr/recorder/service/jfrPostBox.cpp
  14. +10 −11 src/hotspot/share/jfr/recorder/service/jfrRecorderThreadLoop.cpp
  15. +16 −0 src/hotspot/share/jfr/support/jfrKlassUnloading.cpp
  16. +316 −0 src/hotspot/share/jfr/support/jfrSymbolTable.cpp
  17. +148 −0 src/hotspot/share/jfr/support/jfrSymbolTable.hpp
  18. +1 −0 src/hotspot/share/logging/logTag.hpp
  19. +3 −2 src/hotspot/share/oops/instanceKlass.cpp
  20. +10 −0 src/hotspot/share/prims/jvm.cpp
  21. +1 −4 src/hotspot/share/runtime/mutexLocker.cpp
  22. +7 −0 src/hotspot/share/runtime/serviceThread.cpp
  23. +78 −46 src/hotspot/share/services/classLoadingService.cpp
  24. +14 −53 src/hotspot/share/services/classLoadingService.hpp
  25. +313 −0 src/hotspot/share/services/finalizerService.cpp
  26. +68 −0 src/hotspot/share/services/finalizerService.hpp
  27. +19 −0 src/hotspot/share/services/management.cpp
  28. +3 −13 src/hotspot/share/services/management.hpp
  29. +2 −0 src/hotspot/share/utilities/macros.hpp
  30. +3 −0 src/java.base/share/classes/java/lang/ref/Finalizer.java
  31. +35 −0 src/java.base/share/native/libjava/Finalizer.c
  32. +5 −0 src/jdk.jfr/share/conf/jfr/default.jfc
  33. +5 −0 src/jdk.jfr/share/conf/jfr/profile.jfc
  34. +129 −0 test/jdk/jdk/jfr/event/runtime/TestFinalizerStatisticsEvent.java
  35. +1 −0 test/lib/jdk/test/lib/jfr/EventNames.java
  36. +8 −1 test/lib/jdk/test/lib/jfr/TestClassLoader.java
@@ -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
#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
}

5 comments on commit 72a976e

@openjdk-notifier
Copy link

@openjdk-notifier openjdk-notifier bot commented on 72a976e Oct 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jbachorik
Copy link

@jbachorik jbachorik commented on 72a976e Apr 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/backport jdk17u

@openjdk
Copy link

@openjdk openjdk bot commented on 72a976e Apr 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jbachorik Could not automatically backport 72a976ef to openjdk/jdk17u due to conflicts in the following files:

  • src/hotspot/share/runtime/mutexLocker.cpp

To manually resolve these conflicts run the following commands in your personal fork of openjdk/jdk17u:

$ git checkout -b jbachorik-backport-72a976ef
$ git fetch --no-tags https://git.openjdk.java.net/jdk 72a976ef05fc2c62657920a560a0abc60b27c852
$ git cherry-pick --no-commit 72a976ef05fc2c62657920a560a0abc60b27c852
$ # Resolve conflicts
$ git add files/with/resolved/conflicts
$ git commit -m 'Backport 72a976ef05fc2c62657920a560a0abc60b27c852'

Once you have resolved the conflicts as explained above continue with creating a pull request towards the openjdk/jdk17u with the title Backport 72a976ef05fc2c62657920a560a0abc60b27c852.

@jbachorik
Copy link

@jbachorik jbachorik commented on 72a976e Apr 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/backport jdk17u-dev

@openjdk
Copy link

@openjdk openjdk bot commented on 72a976e Apr 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jbachorik Could not automatically backport 72a976ef to openjdk/jdk17u-dev due to conflicts in the following files:

  • src/hotspot/share/runtime/mutexLocker.cpp

To manually resolve these conflicts run the following commands in your personal fork of openjdk/jdk17u-dev:

$ git checkout -b jbachorik-backport-72a976ef
$ git fetch --no-tags https://git.openjdk.java.net/jdk 72a976ef05fc2c62657920a560a0abc60b27c852
$ git cherry-pick --no-commit 72a976ef05fc2c62657920a560a0abc60b27c852
$ # Resolve conflicts
$ git add files/with/resolved/conflicts
$ git commit -m 'Backport 72a976ef05fc2c62657920a560a0abc60b27c852'

Once you have resolved the conflicts as explained above continue with creating a pull request towards the openjdk/jdk17u-dev with the title Backport 72a976ef05fc2c62657920a560a0abc60b27c852.

Please sign in to comment.