8235573: Move JFR ObjectSample oop into OopStorage

Reviewed-by: mgronlun, dholmes, kbarrett
This commit is contained in:
Coleen Phillimore 2020-08-05 10:25:49 -04:00
parent c200b4f1cb
commit 97bbbbba51
19 changed files with 116 additions and 135 deletions

View File

@ -38,7 +38,7 @@ class OopStorageSet : public AllStatic {
public:
// Must be updated when new OopStorages are introduced
static const uint strong_count = 2;
static const uint weak_count = 4;
static const uint weak_count = 4 JFR_ONLY(+ 1);
static const uint all_count = strong_count + weak_count;
private:

View File

@ -27,19 +27,15 @@
#include "utilities/debug.hpp"
#include "utilities/macros.hpp"
#if INCLUDE_JFR
#include "jfr/jfr.hpp"
#endif // INCLUDE_JFR
#if INCLUDE_JVMTI
#include "prims/jvmtiExport.hpp"
#endif // INCLUDE_JVMTI
// serial_phase_count is 0 if JFR and JVMTI are both not built,
// serial_phase_count is 0 if JVMTI is not built,
// requiring some code to be careful to avoid tautological checks
// that some compilers warn about.
#define HAVE_SERIAL_PHASES (INCLUDE_JVMTI || INCLUDE_JFR)
#define HAVE_SERIAL_PHASES INCLUDE_JVMTI
WeakProcessorPhases::Phase WeakProcessorPhases::serial_phase(uint value) {
#if HAVE_SERIAL_PHASES
@ -109,7 +105,6 @@ void WeakProcessorPhases::Iterator::verify_dereferenceable() const {
const char* WeakProcessorPhases::description(Phase phase) {
switch (phase) {
JVMTI_ONLY(case jvmti: return "JVMTI weak processing";)
JFR_ONLY(case jfr: return "JFR weak processing";)
default:
ShouldNotReachHere();
return "Invalid serial weak processing phase";
@ -119,7 +114,6 @@ const char* WeakProcessorPhases::description(Phase phase) {
WeakProcessorPhases::Processor WeakProcessorPhases::processor(Phase phase) {
switch (phase) {
JVMTI_ONLY(case jvmti: return &JvmtiExport::weak_oops_do;)
JFR_ONLY(case jfr: return &Jfr::weak_oops_do;)
default:
ShouldNotReachHere();
return NULL;

View File

@ -41,15 +41,14 @@ public:
typedef void (*Processor)(BoolObjectClosure*, OopClosure*);
enum Phase {
// Serial phases.
JVMTI_ONLY(jvmti JFR_ONLY(COMMA))
JFR_ONLY(jfr)
// Serial phase.
JVMTI_ONLY(jvmti)
// Additional implicit phase values follow for oopstorages.
};
static const uint serial_phase_start = 0;
static const uint serial_phase_count = 0 JVMTI_ONLY(+ 1) JFR_ONLY(+ 1);
static const uint serial_phase_count = 0 JVMTI_ONLY(+ 1);
static const uint oopstorage_phase_start = serial_phase_count;
static const uint oopstorage_phase_count = OopStorageSet::weak_count;
static const uint phase_count = serial_phase_count + oopstorage_phase_count;

View File

@ -25,7 +25,6 @@
#ifndef SHARE_GC_SHENANDOAH_SHENANDOAHPHASETIMINGS_HPP
#define SHARE_GC_SHENANDOAH_SHENANDOAHPHASETIMINGS_HPP
#include "jfr/jfrEvents.hpp"
#include "gc/shenandoah/shenandoahNumberSeq.hpp"
#include "gc/shared/workerDataArray.hpp"
#include "memory/allocation.hpp"
@ -43,7 +42,6 @@ class outputStream;
f(CNT_PREFIX ## ObjectSynchronizerRoots, DESC_PREFIX "Synchronizer Roots") \
f(CNT_PREFIX ## CLDGRoots, DESC_PREFIX "CLDG Roots") \
f(CNT_PREFIX ## JVMTIWeakRoots, DESC_PREFIX "JVMTI Weak Roots") \
f(CNT_PREFIX ## JFRWeakRoots, DESC_PREFIX "JFR Weak Roots") \
f(CNT_PREFIX ## StringDedupTableRoots, DESC_PREFIX "Dedup Table Roots") \
f(CNT_PREFIX ## StringDedupQueueRoots, DESC_PREFIX "Dedup Queue Roots") \
f(CNT_PREFIX ## FinishQueues, DESC_PREFIX "Finish Queues") \

View File

@ -35,7 +35,6 @@
#include "gc/shenandoah/shenandoahPhaseTimings.hpp"
#include "gc/shenandoah/shenandoahStringDedup.hpp"
#include "gc/shenandoah/shenandoahVMOperations.hpp"
#include "jfr/jfr.hpp"
#include "memory/iterator.hpp"
#include "memory/resourceArea.hpp"
#include "memory/universe.hpp"
@ -81,15 +80,8 @@ ShenandoahJVMTIWeakRoot::ShenandoahJVMTIWeakRoot(ShenandoahPhaseTimings::Phase p
}
#endif // INCLUDE_JVMTI
#if INCLUDE_JFR
ShenandoahJFRWeakRoot::ShenandoahJFRWeakRoot(ShenandoahPhaseTimings::Phase phase) :
ShenandoahWeakSerialRoot(&Jfr::weak_oops_do, phase, ShenandoahPhaseTimings::JFRWeakRoots) {
}
#endif // INCLUDE_JFR
void ShenandoahSerialWeakRoots::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive, uint worker_id) {
JVMTI_ONLY(_jvmti_weak_roots.weak_oops_do(is_alive, keep_alive, worker_id);)
JFR_ONLY(_jfr_weak_roots.weak_oops_do(is_alive, keep_alive, worker_id);)
}
void ShenandoahSerialWeakRoots::weak_oops_do(OopClosure* cl, uint worker_id) {

View File

@ -79,21 +79,12 @@ public:
};
#endif // INCLUDE_JVMTI
#if INCLUDE_JFR
class ShenandoahJFRWeakRoot : public ShenandoahWeakSerialRoot {
public:
ShenandoahJFRWeakRoot(ShenandoahPhaseTimings::Phase phase);
};
#endif // INCLUDE_JFR
class ShenandoahSerialWeakRoots {
private:
JVMTI_ONLY(ShenandoahJVMTIWeakRoot _jvmti_weak_roots;)
JFR_ONLY(ShenandoahJFRWeakRoot _jfr_weak_roots;)
public:
ShenandoahSerialWeakRoots(ShenandoahPhaseTimings::Phase phase)
JVMTI_ONLY(: _jvmti_weak_roots(phase))
JFR_ONLY(NOT_JVMTI(:) JVMTI_ONLY(COMMA) _jfr_weak_roots(phase))
{};
void weak_oops_do(BoolObjectClosure* is_alive, OopClosure* keep_alive, uint worker_id);

View File

@ -50,9 +50,6 @@
#include "runtime/thread.hpp"
#include "runtime/vmThread.hpp"
#include "utilities/debug.hpp"
#if INCLUDE_JFR
#include "jfr/jfr.hpp"
#endif
static const ZStatSubPhase ZSubPhasePauseRootsSetup("Pause Roots Setup");
static const ZStatSubPhase ZSubPhasePauseRoots("Pause Roots");
@ -74,7 +71,6 @@ static const ZStatSubPhase ZSubPhasePauseWeakRootsSetup("Pause Weak Roots Setup"
static const ZStatSubPhase ZSubPhasePauseWeakRoots("Pause Weak Roots");
static const ZStatSubPhase ZSubPhasePauseWeakRootsTeardown("Pause Weak Roots Teardown");
static const ZStatSubPhase ZSubPhasePauseWeakRootsJVMTIWeakExport("Pause Weak Roots JVMTIWeakExport");
static const ZStatSubPhase ZSubPhasePauseWeakRootsJFRWeak("Pause Weak Roots JFRWeak");
static const ZStatSubPhase ZSubPhaseConcurrentWeakRoots("Concurrent Weak Roots");
static const ZStatSubPhase ZSubPhaseConcurrentWeakRootsOopStorageSet("Concurrent Weak Roots OopStorageSet");
@ -295,8 +291,7 @@ void ZConcurrentRootsIterator::oops_do(ZRootsIteratorClosure* cl) {
}
ZWeakRootsIterator::ZWeakRootsIterator() :
_jvmti_weak_export(this),
_jfr_weak(this) {
_jvmti_weak_export(this) {
assert(SafepointSynchronize::is_at_safepoint(), "Should be at safepoint");
ZStatTimer timer(ZSubPhasePauseWeakRootsSetup);
}
@ -310,17 +305,9 @@ void ZWeakRootsIterator::do_jvmti_weak_export(BoolObjectClosure* is_alive, ZRoot
JvmtiExport::weak_oops_do(is_alive, cl);
}
void ZWeakRootsIterator::do_jfr_weak(BoolObjectClosure* is_alive, ZRootsIteratorClosure* cl) {
#if INCLUDE_JFR
ZStatTimer timer(ZSubPhasePauseWeakRootsJFRWeak);
Jfr::weak_oops_do(is_alive, cl);
#endif
}
void ZWeakRootsIterator::weak_oops_do(BoolObjectClosure* is_alive, ZRootsIteratorClosure* cl) {
ZStatTimer timer(ZSubPhasePauseWeakRoots);
_jvmti_weak_export.weak_oops_do(is_alive, cl);
_jfr_weak.weak_oops_do(is_alive, cl);
}
void ZWeakRootsIterator::oops_do(ZRootsIteratorClosure* cl) {

View File

@ -170,10 +170,8 @@ public:
class ZWeakRootsIterator {
private:
void do_jvmti_weak_export(BoolObjectClosure* is_alive, ZRootsIteratorClosure* cl);
void do_jfr_weak(BoolObjectClosure* is_alive, ZRootsIteratorClosure* cl);
ZSerialWeakOopsDo<ZWeakRootsIterator, &ZWeakRootsIterator::do_jvmti_weak_export> _jvmti_weak_export;
ZSerialWeakOopsDo<ZWeakRootsIterator, &ZWeakRootsIterator::do_jfr_weak> _jfr_weak;
public:
ZWeakRootsIterator();

View File

@ -102,12 +102,6 @@ void Jfr::on_vm_error_report(outputStream* st) {
}
}
void Jfr::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f) {
if (LeakProfiler::is_running()) {
LeakProfiler::weak_oops_do(is_alive, f);
}
}
bool Jfr::on_flight_recorder_option(const JavaVMOption** option, char* delimiter) {
return JfrOptionSet::parse_flight_recorder_option(option, delimiter);
}

View File

@ -28,9 +28,7 @@
#include "jni.h"
#include "memory/allocation.hpp"
class BoolObjectClosure;
class JavaThread;
class OopClosure;
class Thread;
extern "C" void JNICALL jfr_register_natives(JNIEnv*, jclass);
@ -53,7 +51,6 @@ class Jfr : AllStatic {
static bool on_flight_recorder_option(const JavaVMOption** option, char* delimiter);
static bool on_start_flight_recording_option(const JavaVMOption** option, char* delimiter);
static void on_vm_error_report(outputStream* st);
static void weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f);
static void exclude_thread(Thread* thread);
static bool is_excluded(Thread* thread);
static void include_thread(Thread* thread);

View File

@ -89,14 +89,6 @@ void LeakProfiler::emit_events(int64_t cutoff_ticks, bool emit_all, bool skip_bf
ObjectSampler::release();
}
void LeakProfiler::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f) {
assert(SafepointSynchronize::is_at_safepoint(),
"Leak Profiler::oops_do(...) may only be called during safepoint");
if (is_running()) {
ObjectSampler::weak_oops_do(is_alive, f);
}
}
void LeakProfiler::sample(HeapWord* object, size_t size, JavaThread* thread) {
assert(is_running(), "invariant");
assert(thread != NULL, "invariant");

View File

@ -27,8 +27,6 @@
#include "memory/allocation.hpp"
class BoolObjectClosure;
class OopClosure;
class JavaThread;
class LeakProfiler : public AllStatic {
@ -39,9 +37,6 @@ class LeakProfiler : public AllStatic {
static void emit_events(int64_t cutoff_ticks, bool emit_all, bool skip_bfs);
static void sample(HeapWord* object, size_t size, JavaThread* thread);
// Called by GC
static void weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f);
};
#endif // SHARE_JFR_LEAKPROFILER_LEAKPROFILER_HPP

View File

@ -23,16 +23,36 @@
*/
#include "precompiled.hpp"
#include "jfr/leakprofiler/sampling/objectSample.hpp"
#include "oops/access.inline.hpp"
#include "jfr/leakprofiler/sampling/objectSampler.hpp"
#include "oops/weakHandle.inline.hpp"
#include "runtime/handles.inline.hpp"
const oop ObjectSample::object() const {
return NativeAccess<ON_PHANTOM_OOP_REF | AS_NO_KEEPALIVE>::oop_load(&_object);
void ObjectSample::reset() {
release();
set_stack_trace_id(0);
set_stack_trace_hash(0);
release_references();
}
const oop ObjectSample::object_raw() const {
return RawAccess<>::oop_load(&_object);
const oop ObjectSample::object() const {
return _object.resolve();
}
bool ObjectSample::is_dead() const {
return _object.peek() == NULL;
}
const oop* ObjectSample::object_addr() const {
return _object.ptr_raw();
}
void ObjectSample::set_object(oop object) {
NativeAccess<ON_PHANTOM_OOP_REF>::oop_store(&_object, object);
assert(_object.is_empty(), "should be empty");
Handle h(Thread::current(), object);
_object = WeakHandle(ObjectSampler::oop_storage(), h);
}
void ObjectSample::release() {
_object.release(ObjectSampler::oop_storage());
_object = WeakHandle();
}

View File

@ -31,6 +31,7 @@
#include "jfr/utilities/jfrTypes.hpp"
#include "memory/allocation.hpp"
#include "oops/oop.hpp"
#include "oops/weakHandle.hpp"
#include "utilities/ticks.hpp"
/*
@ -48,7 +49,7 @@ class ObjectSample : public JfrCHeapObj {
JfrBlobHandle _stacktrace;
JfrBlobHandle _thread;
JfrBlobHandle _type_set;
oop _object;
WeakHandle _object;
Ticks _allocation_time;
traceid _stack_trace_id;
traceid _thread_id;
@ -64,12 +65,7 @@ class ObjectSample : public JfrCHeapObj {
_type_set.~JfrBlobHandle();
}
void reset() {
_object = NULL;
set_stack_trace_id(0);
set_stack_trace_hash(0);
release_references();
}
void reset();
public:
ObjectSample() : _next(NULL),
@ -77,7 +73,6 @@ class ObjectSample : public JfrCHeapObj {
_stacktrace(),
_thread(),
_type_set(),
_object(NULL),
_allocation_time(),
_stack_trace_id(0),
_thread_id(0),
@ -103,17 +98,14 @@ class ObjectSample : public JfrCHeapObj {
_previous = prev;
}
bool is_dead() const {
return object() == NULL;
}
bool is_dead() const;
const oop object() const;
const oop object_raw() const;
void set_object(oop object);
const oop* object_addr() const {
return &_object;
}
const oop* object_addr() const;
void release();
int index() const {
return _index;

View File

@ -23,6 +23,8 @@
*/
#include "precompiled.hpp"
#include "gc/shared/oopStorage.hpp"
#include "gc/shared/oopStorageSet.hpp"
#include "jfr/jfrEvents.hpp"
#include "jfr/leakprofiler/sampling/objectSample.hpp"
#include "jfr/leakprofiler/sampling/objectSampler.hpp"
@ -41,6 +43,40 @@
#include "runtime/safepoint.hpp"
#include "runtime/thread.hpp"
// Timestamp of when the gc last processed the set of sampled objects.
static JfrTicks _last_sweep;
// Condition variable to communicate that some sampled objects have been cleared by the gc
// and can therefore be removed from the sample priority queue.
static bool volatile _dead_samples = false;
// The OopStorage instance is used to hold weak references to sampled objects.
// It is constructed and registered during VM initialization. This is a singleton
// that persist independent of the state of the ObjectSampler.
static OopStorage* _oop_storage = NULL;
OopStorage* ObjectSampler::oop_storage() { return _oop_storage; }
// Callback invoked by the GC after an iteration over the oop storage
// that may have cleared dead referents. num_dead is the number of entries
// already NULL or cleared by the iteration.
void ObjectSampler::oop_storage_gc_notification(size_t num_dead) {
if (num_dead != 0) {
// The ObjectSampler instance may have already been cleaned or a new
// instance was created concurrently. This allows for a small race where cleaning
// could be done again.
Atomic::store(&_dead_samples, true);
_last_sweep = JfrTicks::now();
}
}
bool ObjectSampler::create_oop_storage() {
_oop_storage = OopStorageSet::create_weak("Weak JFR Old Object Samples");
assert(_oop_storage != NULL, "invariant");
_oop_storage->register_num_dead_callback(&oop_storage_gc_notification);
return true;
}
static ObjectSampler* _instance = NULL;
static ObjectSampler& instance() {
@ -49,13 +85,14 @@ static ObjectSampler& instance() {
}
ObjectSampler::ObjectSampler(size_t size) :
_priority_queue(new SamplePriorityQueue(size)),
_list(new SampleList(size)),
_last_sweep(JfrTicks::now()),
_total_allocated(0),
_threshold(0),
_size(size),
_dead_samples(false) {}
_priority_queue(new SamplePriorityQueue(size)),
_list(new SampleList(size)),
_total_allocated(0),
_threshold(0),
_size(size) {
_last_sweep = JfrTicks::now();
Atomic::store(&_dead_samples, false);
}
ObjectSampler::~ObjectSampler() {
delete _priority_queue;
@ -66,6 +103,7 @@ ObjectSampler::~ObjectSampler() {
bool ObjectSampler::create(size_t size) {
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
assert(_oop_storage != NULL, "should be already created");
assert(_instance == NULL, "invariant");
_instance = new ObjectSampler(size);
return _instance != NULL;
@ -92,13 +130,11 @@ void ObjectSampler::destroy() {
static volatile int _lock = 0;
ObjectSampler* ObjectSampler::acquire() {
assert(is_created(), "invariant");
while (Atomic::cmpxchg(&_lock, 0, 1) == 1) {}
return _instance;
}
void ObjectSampler::release() {
assert(is_created(), "invariant");
OrderAccess::fence();
_lock = 0;
}
@ -150,9 +186,11 @@ void ObjectSampler::add(HeapWord* obj, size_t allocated, traceid thread_id, Java
assert(thread != NULL, "invariant");
assert(thread->jfr_thread_local()->has_thread_blob(), "invariant");
if (_dead_samples) {
if (Atomic::load(&_dead_samples)) {
// There's a small race where a GC scan might reset this to true, potentially
// causing a back-to-back scavenge.
Atomic::store(&_dead_samples, false);
scavenge();
assert(!_dead_samples, "invariant");
}
_total_allocated += allocated;
@ -199,12 +237,13 @@ void ObjectSampler::scavenge() {
}
current = next;
}
_dead_samples = false;
}
void ObjectSampler::remove_dead(ObjectSample* sample) {
assert(sample != NULL, "invariant");
assert(sample->is_dead(), "invariant");
sample->release();
ObjectSample* const previous = sample->prev();
// push span onto previous
if (previous != NULL) {
@ -216,27 +255,6 @@ void ObjectSampler::remove_dead(ObjectSample* sample) {
_list->release(sample);
}
void ObjectSampler::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f) {
assert(is_created(), "invariant");
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
ObjectSampler& sampler = instance();
ObjectSample* current = sampler._list->last();
while (current != NULL) {
if (current->_object != NULL) {
if (is_alive->do_object_b(current->object_raw())) {
// The weakly referenced object is alive, update pointer
f->do_oop(const_cast<oop*>(current->object_addr()));
} else {
// clear existing field to assist GC barriers
current->_object = NULL;
sampler._dead_samples = true;
}
}
current = current->next();
}
sampler._last_sweep = JfrTicks::now();
}
ObjectSample* ObjectSampler::last() const {
return _list->last();
}
@ -267,6 +285,6 @@ ObjectSample* ObjectSampler::item_at(int index) {
);
}
const JfrTicks& ObjectSampler::last_sweep() const {
const JfrTicks& ObjectSampler::last_sweep() {
return _last_sweep;
}

View File

@ -30,9 +30,8 @@
typedef u8 traceid;
class BoolObjectClosure;
class JavaThread;
class OopClosure;
class OopStorage;
class ObjectSample;
class SampleList;
class SamplePriorityQueue;
@ -41,17 +40,17 @@ class SamplePriorityQueue;
// making sure the samples are evenly distributed as
// new entries are added and removed.
class ObjectSampler : public CHeapObj<mtTracing> {
friend class JfrRecorder;
friend class LeakProfiler;
friend class ObjectSample;
friend class StartOperation;
friend class StopOperation;
private:
SamplePriorityQueue* _priority_queue;
SampleList* _list;
JfrTicks _last_sweep;
size_t _total_allocated;
size_t _threshold;
size_t _size;
bool _dead_samples;
// Lifecycle
explicit ObjectSampler(size_t size);
@ -66,24 +65,26 @@ class ObjectSampler : public CHeapObj<mtTracing> {
void scavenge();
void remove_dead(ObjectSample* sample);
// Called by GC
static void weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f);
const ObjectSample* item_at(int index) const;
ObjectSample* item_at(int index);
int item_count() const;
// OopStorage
static bool create_oop_storage();
static OopStorage* oop_storage();
// Invoked by the GC post oop storage processing.
static void oop_storage_gc_notification(size_t num_dead);
public:
static ObjectSampler* sampler();
// For operations that require exclusive access (non-safepoint)
static ObjectSampler* acquire();
static void release();
static const JfrTicks& last_sweep();
const ObjectSample* first() const;
ObjectSample* last() const;
const ObjectSample* last_resolved() const;
void set_last_resolved(const ObjectSample* sample);
const JfrTicks& last_sweep() const;
};
#endif // SHARE_JFR_LEAKPROFILER_SAMPLING_OBJECTSAMPLER_HPP

View File

@ -27,6 +27,7 @@
#include "jfr/dcmd/jfrDcmds.hpp"
#include "jfr/instrumentation/jfrJvmtiAgent.hpp"
#include "jfr/jni/jfrJavaSupport.hpp"
#include "jfr/leakprofiler/sampling/objectSampler.hpp"
#include "jfr/periodic/jfrOSInterface.hpp"
#include "jfr/periodic/sampling/jfrThreadSampler.hpp"
#include "jfr/recorder/jfrRecorder.hpp"
@ -73,12 +74,20 @@ bool JfrRecorder::is_enabled() {
return _enabled;
}
bool JfrRecorder::create_oop_storages() {
// currently only a single weak oop storage for Leak Profiler
return ObjectSampler::create_oop_storage();
}
bool JfrRecorder::on_create_vm_1() {
if (!is_disabled()) {
if (FlightRecorder || StartFlightRecording != NULL) {
enable();
}
}
if (!create_oop_storages()) {
return false;
}
// fast time initialization
return JfrTime::initialize();
}

View File

@ -45,6 +45,7 @@ class JfrRecorder : public JfrCHeapObj {
static bool create_chunk_repository();
static bool create_java_event_writer();
static bool create_jvmti_agent();
static bool create_oop_storages();
static bool create_os_interface();
static bool create_post_box();
static bool create_recorder_thread();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2020, 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
@ -58,6 +58,9 @@ class WeakHandle {
void print() const;
void print_on(outputStream* st) const;
bool is_empty() const { return _obj == NULL; }
oop* ptr_raw() const { return _obj; }
};
#endif // SHARE_OOPS_WEAKHANDLE_HPP