8203394: Implementation of JEP 331: Low-Overhead Heap Profiling

Implement Low-Overhead Heap Profiling

Reviewed-by: eosterlund, gthornbr, rehn, sspitsyn, tschatzl
This commit is contained in:
Jean Christophe Beyler 2018-06-15 00:49:54 -07:00
parent 724e41cbb6
commit 6129ed590c
17 changed files with 569 additions and 76 deletions

View File

@ -6153,6 +6153,9 @@
<df name="IsModifiableModule">
<in>libIsModifiableModuleTest.c</in>
</df>
<df name="HeapMonitorModule">
<in>libHeapMonitorTest.c</in>
</df>
<df name="ModuleAwareAgents">
<df name="ClassFileLoadHook">
<in>libMAAClassFileLoadHook.c</in>
@ -40154,6 +40157,11 @@
tool="0"
flavor2="0">
</item>
<item path="../../test/hotspot/jtreg/serviceability/jvmti/HeapMonitor/libHeapMonitorTest.c"
ex="false"
tool="0"
flavor2="0">
</item>
<item path="../../test/hotspot/jtreg/serviceability/jvmti/ModuleAwareAgents/ClassFileLoadHook/libMAAClassFileLoadHook.c"
ex="false"
tool="0"

View File

@ -378,6 +378,27 @@ HeapWord* CollectedHeap::obj_allocate_raw(Klass* klass, size_t size,
}
HeapWord* CollectedHeap::allocate_from_tlab_slow(Klass* klass, size_t size, TRAPS) {
HeapWord* obj = NULL;
// In assertion mode, check that there was a sampling collector present
// in the stack. This enforces checking that no path is without a sampling
// collector.
// Only check if the sampler could actually sample something in this call path.
assert(!JvmtiExport::should_post_sampled_object_alloc()
|| !JvmtiSampledObjectAllocEventCollector::object_alloc_is_safe_to_sample()
|| THREAD->heap_sampler().sampling_collector_present(),
"Sampling collector not present.");
if (ThreadHeapSampler::enabled()) {
// Try to allocate the sampled object from TLAB, it is possible a sample
// point was put and the TLAB still has space.
obj = THREAD->tlab().allocate_sampled_object(size);
if (obj != NULL) {
return obj;
}
}
ThreadLocalAllocBuffer& tlab = THREAD->tlab();
// Retain tlab and allocate object in shared space if
@ -401,7 +422,7 @@ HeapWord* CollectedHeap::allocate_from_tlab_slow(Klass* klass, size_t size, TRAP
// between minimal and new_tlab_size is accepted.
size_t actual_tlab_size = 0;
size_t min_tlab_size = ThreadLocalAllocBuffer::compute_min_size(size);
HeapWord* obj = Universe::heap()->allocate_new_tlab(min_tlab_size, new_tlab_size, &actual_tlab_size);
obj = Universe::heap()->allocate_new_tlab(min_tlab_size, new_tlab_size, &actual_tlab_size);
if (obj == NULL) {
assert(actual_tlab_size == 0, "Allocation failed, but actual size was updated. min: " SIZE_FORMAT ", desired: " SIZE_FORMAT ", actual: " SIZE_FORMAT,
min_tlab_size, new_tlab_size, actual_tlab_size);
@ -425,6 +446,14 @@ HeapWord* CollectedHeap::allocate_from_tlab_slow(Klass* klass, size_t size, TRAP
Copy::fill_to_words(obj + hdr_size, actual_tlab_size - hdr_size, badHeapWordVal);
#endif // ASSERT
}
// Send the thread information about this allocation in case a sample is
// requested.
if (ThreadHeapSampler::enabled()) {
size_t tlab_bytes_since_last_sample = THREAD->tlab().bytes_since_last_sample_point();
THREAD->heap_sampler().check_for_sampling(obj, size, tlab_bytes_since_last_sample);
}
tlab.fill(obj, obj + size, actual_tlab_size);
return obj;
}

View File

@ -194,6 +194,18 @@ class CollectedHeap : public CHeapObj<mtInternal> {
virtual void trace_heap(GCWhen::Type when, const GCTracer* tracer);
// Internal allocation methods.
inline static HeapWord* common_allocate_memory(Klass* klass, int size,
void (*post_setup)(Klass*, HeapWord*, int),
int size_for_post, bool init_memory,
TRAPS);
// Internal allocation method for common obj/class/array allocations.
inline static HeapWord* allocate_memory(Klass* klass, int size,
void (*post_setup)(Klass*, HeapWord*, int),
int size_for_post, bool init_memory,
TRAPS);
// Verification functions
virtual void check_for_bad_heap_word_value(HeapWord* addr, size_t size)
PRODUCT_RETURN;

View File

@ -34,6 +34,7 @@
#include "oops/oop.inline.hpp"
#include "prims/jvmtiExport.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/thread.inline.hpp"
#include "services/lowMemoryDetector.hpp"
#include "utilities/align.hpp"
@ -200,9 +201,15 @@ HeapWord* CollectedHeap::allocate_outside_tlab(Klass* klass, size_t size,
NOT_PRODUCT(Universe::heap()->check_for_non_bad_heap_word_value(result, size));
assert(!HAS_PENDING_EXCEPTION,
"Unexpected exception, will result in uninitialized storage");
THREAD->incr_allocated_bytes(size * HeapWordSize);
size_t size_in_bytes = size * HeapWordSize;
THREAD->incr_allocated_bytes(size_in_bytes);
AllocTracer::send_allocation_outside_tlab(klass, result, size_in_bytes, THREAD);
if (ThreadHeapSampler::enabled()) {
THREAD->heap_sampler().check_for_sampling(result, size_in_bytes);
}
AllocTracer::send_allocation_outside_tlab(klass, result, size * HeapWordSize, THREAD);
return result;
}
@ -214,12 +221,58 @@ void CollectedHeap::init_obj(HeapWord* obj, size_t size) {
Copy::fill_to_aligned_words(obj + hs, size - hs);
}
HeapWord* CollectedHeap::common_allocate_memory(Klass* klass, int size,
void (*post_setup)(Klass*, HeapWord*, int),
int size_for_post, bool init_memory,
TRAPS) {
HeapWord* obj;
if (init_memory) {
obj = common_mem_allocate_init(klass, size, CHECK_NULL);
} else {
obj = common_mem_allocate_noinit(klass, size, CHECK_NULL);
}
post_setup(klass, obj, size_for_post);
return obj;
}
HeapWord* CollectedHeap::allocate_memory(Klass* klass, int size,
void (*post_setup)(Klass*, HeapWord*, int),
int size_for_post, bool init_memory,
TRAPS) {
HeapWord* obj;
assert(JavaThread::current()->heap_sampler().add_sampling_collector(),
"Should never return false.");
if (JvmtiExport::should_post_sampled_object_alloc()) {
HandleMark hm(THREAD);
Handle obj_h;
{
JvmtiSampledObjectAllocEventCollector collector;
obj = common_allocate_memory(klass, size, post_setup, size_for_post,
init_memory, CHECK_NULL);
// If we want to be sampling, protect the allocated object with a Handle
// before doing the callback. The callback is done in the destructor of
// the JvmtiSampledObjectAllocEventCollector.
obj_h = Handle(THREAD, (oop) obj);
}
obj = (HeapWord*) obj_h();
} else {
obj = common_allocate_memory(klass, size, post_setup, size_for_post,
init_memory, CHECK_NULL);
}
assert(JavaThread::current()->heap_sampler().remove_sampling_collector(),
"Should never return false.");
return obj;
}
oop CollectedHeap::obj_allocate(Klass* klass, int size, TRAPS) {
debug_only(check_for_valid_allocation_state());
assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed");
assert(size >= 0, "int won't convert to size_t");
HeapWord* obj = common_mem_allocate_init(klass, size, CHECK_NULL);
post_allocation_setup_obj(klass, obj, size);
HeapWord* obj = allocate_memory(klass, size, post_allocation_setup_obj,
size, true, CHECK_NULL);
NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size));
return (oop)obj;
}
@ -228,8 +281,8 @@ oop CollectedHeap::class_allocate(Klass* klass, int size, TRAPS) {
debug_only(check_for_valid_allocation_state());
assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed");
assert(size >= 0, "int won't convert to size_t");
HeapWord* obj = common_mem_allocate_init(klass, size, CHECK_NULL);
post_allocation_setup_class(klass, obj, size); // set oop_size
HeapWord* obj = allocate_memory(klass, size, post_allocation_setup_class,
size, true, CHECK_NULL);
NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size));
return (oop)obj;
}
@ -241,8 +294,8 @@ oop CollectedHeap::array_allocate(Klass* klass,
debug_only(check_for_valid_allocation_state());
assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed");
assert(size >= 0, "int won't convert to size_t");
HeapWord* obj = common_mem_allocate_init(klass, size, CHECK_NULL);
post_allocation_setup_array(klass, obj, length);
HeapWord* obj = allocate_memory(klass, size, post_allocation_setup_array,
length, true, CHECK_NULL);
NOT_PRODUCT(Universe::heap()->check_for_bad_heap_word_value(obj, size));
return (oop)obj;
}
@ -254,9 +307,9 @@ oop CollectedHeap::array_allocate_nozero(Klass* klass,
debug_only(check_for_valid_allocation_state());
assert(!Universe::heap()->is_gc_active(), "Allocation during gc not allowed");
assert(size >= 0, "int won't convert to size_t");
HeapWord* obj = common_mem_allocate_noinit(klass, size, CHECK_NULL);
((oop)obj)->set_klass_gap(0);
post_allocation_setup_array(klass, obj, length);
HeapWord* obj = allocate_memory(klass, size, post_allocation_setup_array,
length, false, CHECK_NULL);
#ifndef PRODUCT
const size_t hs = oopDesc::header_size()+1;
Universe::heap()->check_for_non_bad_heap_word_value(obj+hs, size-hs);

View File

@ -45,6 +45,14 @@ void ThreadLocalAllocBuffer::clear_before_allocation() {
make_parsable(true); // also retire the TLAB
}
size_t ThreadLocalAllocBuffer::remaining() {
if (end() == NULL) {
return 0;
}
return pointer_delta(hard_end(), top());
}
void ThreadLocalAllocBuffer::accumulate_statistics_before_gc() {
global_stats()->initialize();
@ -121,10 +129,12 @@ void ThreadLocalAllocBuffer::make_parsable(bool retire, bool zap) {
set_top(NULL);
set_pf_top(NULL);
set_end(NULL);
set_allocation_end(NULL);
}
}
assert(!(retire || ZeroTLAB) ||
(start() == NULL && end() == NULL && top() == NULL),
(start() == NULL && end() == NULL && top() == NULL &&
_allocation_end == NULL),
"TLAB must be reset");
}
@ -172,8 +182,13 @@ void ThreadLocalAllocBuffer::fill(HeapWord* start,
_allocated_size += new_size;
print_stats("fill");
assert(top <= start + new_size - alignment_reserve(), "size too small");
initialize(start, top, start + new_size - alignment_reserve());
if (ThreadHeapSampler::enabled()) {
set_sample_end();
}
// Reset amount of internal fragmentation
set_refill_waste_limit(initial_refill_waste_limit());
}
@ -185,6 +200,7 @@ void ThreadLocalAllocBuffer::initialize(HeapWord* start,
set_top(top);
set_pf_top(top);
set_end(end);
set_allocation_end(end);
invariants();
}
@ -306,12 +322,45 @@ void ThreadLocalAllocBuffer::verify() {
guarantee(p == top(), "end of last object must match end of space");
}
void ThreadLocalAllocBuffer::set_sample_end() {
size_t heap_words_remaining = pointer_delta(_end, _top);
size_t bytes_until_sample = myThread()->heap_sampler().bytes_until_sample();
size_t words_until_sample = bytes_until_sample / HeapWordSize;;
if (heap_words_remaining > words_until_sample) {
HeapWord* new_end = _top + words_until_sample;
set_end(new_end);
_bytes_since_last_sample_point = bytes_until_sample;
} else {
_bytes_since_last_sample_point = heap_words_remaining * HeapWordSize;;
}
}
Thread* ThreadLocalAllocBuffer::myThread() {
return (Thread*)(((char *)this) +
in_bytes(start_offset()) -
in_bytes(Thread::tlab_start_offset()));
}
void ThreadLocalAllocBuffer::set_back_allocation_end() {
_end = _allocation_end;
}
HeapWord* ThreadLocalAllocBuffer::allocate_sampled_object(size_t size) {
set_back_allocation_end();
HeapWord* result = allocate(size);
if (result) {
myThread()->heap_sampler().check_for_sampling(result, size * HeapWordSize, _bytes_since_last_sample_point);
set_sample_end();
}
return result;
}
HeapWord* ThreadLocalAllocBuffer::hard_end() {
return _allocation_end + alignment_reserve();
}
GlobalTLABStats::GlobalTLABStats() :
_allocating_threads_avg(TLABAllocationWeight) {

View File

@ -37,6 +37,12 @@ class GlobalTLABStats;
// It is thread-private at any time, but maybe multiplexed over
// time across multiple threads. The park()/unpark() pair is
// used to make it available for such multiplexing.
//
// Heap sampling is performed via the end and allocation_end
// fields.
// allocation_end contains the real end of the tlab allocation,
// whereas end can be set to an arbitrary spot in the tlab to
// trip the return and sample the allocation.
class ThreadLocalAllocBuffer: public CHeapObj<mtThread> {
friend class VMStructs;
friend class JVMCIVMStructs;
@ -44,10 +50,13 @@ private:
HeapWord* _start; // address of TLAB
HeapWord* _top; // address after last allocation
HeapWord* _pf_top; // allocation prefetch watermark
HeapWord* _end; // allocation end (excluding alignment_reserve)
HeapWord* _end; // allocation end (can be the sampling end point or _allocation_end)
HeapWord* _allocation_end; // end for allocations (actual TLAB end, excluding alignment_reserve)
size_t _desired_size; // desired size (including alignment_reserve)
size_t _refill_waste_limit; // hold onto tlab if free() is larger than this
size_t _allocated_before_last_gc; // total bytes allocated up until the last gc
size_t _bytes_since_last_sample_point; // bytes since last sample point.
static size_t _max_size; // maximum size of any TLAB
static int _reserve_for_allocation_prefetch; // Reserve at the end of the TLAB
@ -67,6 +76,7 @@ private:
void set_start(HeapWord* start) { _start = start; }
void set_end(HeapWord* end) { _end = end; }
void set_allocation_end(HeapWord* ptr) { _allocation_end = ptr; }
void set_top(HeapWord* top) { _top = top; }
void set_pf_top(HeapWord* pf_top) { _pf_top = pf_top; }
void set_desired_size(size_t desired_size) { _desired_size = desired_size; }
@ -77,7 +87,7 @@ private:
static int target_refills() { return _target_refills; }
size_t initial_desired_size();
size_t remaining() const { return end() == NULL ? 0 : pointer_delta(hard_end(), top()); }
size_t remaining();
bool is_last_allocation(HeapWord* obj, size_t size) { return pointer_delta(top(), obj) == size; }
@ -118,8 +128,8 @@ public:
HeapWord* start() const { return _start; }
HeapWord* end() const { return _end; }
HeapWord* hard_end() const { return _end + alignment_reserve(); }
HeapWord* top() const { return _top; }
HeapWord* hard_end();
HeapWord* pf_top() const { return _pf_top; }
size_t desired_size() const { return _desired_size; }
size_t used() const { return pointer_delta(top(), start()); }
@ -127,9 +137,11 @@ public:
size_t free() const { return pointer_delta(end(), top()); }
// Don't discard tlab if remaining space is larger than this.
size_t refill_waste_limit() const { return _refill_waste_limit; }
size_t bytes_since_last_sample_point() const { return _bytes_since_last_sample_point; }
// Allocate size HeapWords. The memory is NOT initialized to zero.
inline HeapWord* allocate(size_t size);
HeapWord* allocate_sampled_object(size_t size);
// Undo last allocation.
inline bool undo_allocate(HeapWord* obj, size_t size);
@ -171,6 +183,9 @@ public:
void fill(HeapWord* start, HeapWord* top, size_t new_size);
void initialize();
void set_back_allocation_end();
void set_sample_end();
static size_t refill_waste_limit_increment() { return TLABWasteIncrement; }
template <typename T> void addresses_do(T f) {
@ -178,6 +193,7 @@ public:
f(&_top);
f(&_pf_top);
f(&_end);
f(&_allocation_end);
}
// Code generation support

View File

@ -10353,6 +10353,14 @@ myInit() {
See <eventlink id="ClassFileLoadHook"/>.
</description>
</capabilityfield>
<capabilityfield id="can_generate_sampled_object_alloc_events" since="11">
<description>
Can generate sampled allocation events.
If this capability is enabled then the heap sampling method
<functionlink id="SetHeapSamplingRate"></functionlink> can be
called and <eventlink id="SampledObjectAlloc"></eventlink> events can be generated.
</description>
</capabilityfield>
</capabilitiestypedef>
<function id="GetPotentialCapabilities" jkernel="yes" phase="onload" num="140">
@ -11531,6 +11539,47 @@ myInit() {
</category>
<category id="heap_monitoring" label="Heap Monitoring">
<function id="SetHeapSamplingRate" phase="onload" num="156" since="11">
<synopsis>Set Heap Sampling Rate</synopsis>
<description>
Generate a <eventlink id="SampledObjectAlloc"/> event when objects are allocated.
Each thread keeps a counter of bytes allocated. The event will only be generated
when that counter exceeds an average of <paramlink id="sampling_rate"></paramlink>
since the last sample.
<p/>
Setting <paramlink id="sampling_rate"></paramlink> to 0 will cause an event to be
generated by each allocation supported by the system.
</description>
<origin>new</origin>
<capabilities>
<required id="can_generate_sampled_object_alloc_events"></required>
</capabilities>
<parameters>
<param id="sampling_rate">
<jint/>
<description>
The sampling rate in bytes. The sampler uses a statistical approach to
generate an event, on average, once for every <paramlink id="sampling_rate"/> bytes of
memory allocated by a given thread.
<p/>
Passing 0 as a sampling rate generates a sample for every allocation.
<p/>
Note: The overhead of this feature is directly correlated with the sampling rate.
A high sampling rate, such as 1024 bytes, will incur a high overhead.
A lower rate, such as 1024KB, will have a much lower overhead. Sampling should only
be used with an understanding that it may impact performance.
</description>
</param>
</parameters>
<errors>
<error id="JVMTI_ERROR_ILLEGAL_ARGUMENT">
<paramlink id="sampling_rate"></paramlink> is less than zero.
</error>
</errors>
</function>
</category>
</functionsection>
<errorsection label="Error Reference">
@ -13495,13 +13544,13 @@ myInit() {
<param id="object">
<jobject/>
<description>
JNI local reference to the object that was allocated
JNI local reference to the object that was allocated.
</description>
</param>
<param id="object_klass">
<jclass/>
<description>
JNI local reference to the class of the object
JNI local reference to the class of the object.
</description>
</param>
<param id="size">
@ -13513,8 +13562,75 @@ myInit() {
</parameters>
</event>
<event label="Sampled Object Allocation"
id="SampledObjectAlloc" const="JVMTI_EVENT_SAMPLED_OBJECT_ALLOC" num="86" since="11">
<description>
Sent when an allocated object is sampled.
By default, the sampling rate is a geometric variable with a 512KB mean.
Each thread tracks how many bytes it has allocated since it sent the last event.
When the number of bytes exceeds the sampling rate, it will send another event.
This implies that, on average, one object will be sampled every time a thread has
allocated 512KB bytes since the last sample.
<p/>
Note that this is a geometric variable: it will not sample every 512KB precisely.
The goal of this is to ensure high quality sampling even if allocation is
happening in a fixed pattern (i.e., the same set of objects are being allocated
every 512KB).
<p/>
If another sampling rate is required, the user can call
<functionlink id="SetHeapSamplingRate"></functionlink> with a strictly positive integer value, representing
the new sampling rate.
<p/>
This event is sent once the sampled allocation has been performed. It provides the object, stack trace
of the allocation, the thread allocating, the size of allocation, and the object's class.
<p/>
A typical use case of this system is to determine where heap allocations originate.
In conjunction with weak references and the function
<functionlink id="GetStackTrace"></functionlink>, a user can track which objects were allocated from which
stack trace, and which are still live during the execution of the program.
</description>
<origin>new</origin>
<capabilities>
<required id="can_generate_sampled_object_alloc_events"></required>
</capabilities>
<parameters>
<param id="jni_env">
<outptr>
<struct>JNIEnv</struct>
</outptr>
<description>
The JNI environment of the event (current) thread.
</description>
</param>
<param id="thread">
<jthread/>
<description>
Thread allocating the object.
</description>
</param>
<param id="object">
<jobject/>
<description>
JNI local reference to the object that was allocated.
</description>
</param>
<param id="object_klass">
<jclass/>
<description>
JNI local reference to the class of the object
</description>
</param>
<param id="size">
<jlong/>
<description>
Size of the object (in bytes). See <functionlink id="GetObjectSize"/>.
</description>
</param>
</parameters>
</event>
<event label="Object Free"
id="ObjectFree" const="JVMTI_EVENT_OBJECT_FREE" num="83">
id="ObjectFree" const="JVMTI_EVENT_OBJECT_FREE" num="83">
<description>
An Object Free event is sent when the garbage collector frees an object.
Events are only sent for tagged objects--see
@ -13534,7 +13650,7 @@ myInit() {
<jlong/>
<description>
The freed object's tag
</description>
</description>
</param>
</parameters>
</event>

View File

@ -64,6 +64,7 @@
#include "runtime/reflectionUtils.hpp"
#include "runtime/signature.hpp"
#include "runtime/thread.inline.hpp"
#include "runtime/threadHeapSampler.hpp"
#include "runtime/threadSMR.hpp"
#include "runtime/timerTrace.hpp"
#include "runtime/vframe.inline.hpp"
@ -537,10 +538,17 @@ JvmtiEnv::SetEventNotificationMode(jvmtiEventMode mode, jvmtiEvent event_type, j
if (event_type == JVMTI_EVENT_CLASS_FILE_LOAD_HOOK && enabled) {
record_class_file_load_hook_enabled();
}
if (event_type == JVMTI_EVENT_SAMPLED_OBJECT_ALLOC) {
if (enabled) {
ThreadHeapSampler::enable();
} else {
ThreadHeapSampler::disable();
}
}
JvmtiEventController::set_user_enabled(this, (JavaThread*) NULL, event_type, enabled);
} else {
// We have a specified event_thread.
JavaThread* java_thread = NULL;
ThreadsListHandle tlh;
jvmtiError err = JvmtiExport::cv_external_thread_to_JavaThread(tlh.list(), event_thread, &java_thread, NULL);
@ -3631,6 +3639,15 @@ JvmtiEnv::GetAvailableProcessors(jint* processor_count_ptr) {
return JVMTI_ERROR_NONE;
} /* end GetAvailableProcessors */
jvmtiError
JvmtiEnv::SetHeapSamplingRate(jint sampling_rate) {
if (sampling_rate < 0) {
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
}
ThreadHeapSampler::set_sampling_rate(sampling_rate);
return JVMTI_ERROR_NONE;
} /* end SetHeapSamplingRate */
//
// System Properties functions
//

View File

@ -84,6 +84,7 @@ static const jlong GARBAGE_COLLECTION_FINISH_BIT = (((jlong)1) << (JVMTI_EVENT_
static const jlong OBJECT_FREE_BIT = (((jlong)1) << (JVMTI_EVENT_OBJECT_FREE - TOTAL_MIN_EVENT_TYPE_VAL));
static const jlong RESOURCE_EXHAUSTED_BIT = (((jlong)1) << (JVMTI_EVENT_RESOURCE_EXHAUSTED - TOTAL_MIN_EVENT_TYPE_VAL));
static const jlong VM_OBJECT_ALLOC_BIT = (((jlong)1) << (JVMTI_EVENT_VM_OBJECT_ALLOC - TOTAL_MIN_EVENT_TYPE_VAL));
static const jlong SAMPLED_OBJECT_ALLOC_BIT = (((jlong)1) << (JVMTI_EVENT_SAMPLED_OBJECT_ALLOC - TOTAL_MIN_EVENT_TYPE_VAL));
// bits for extension events
static const jlong CLASS_UNLOAD_BIT = (((jlong)1) << (EXT_EVENT_CLASS_UNLOAD - TOTAL_MIN_EVENT_TYPE_VAL));
@ -620,6 +621,7 @@ JvmtiEventControllerPrivate::recompute_enabled() {
JvmtiExport::set_should_post_compiled_method_load((any_env_thread_enabled & COMPILED_METHOD_LOAD_BIT) != 0);
JvmtiExport::set_should_post_compiled_method_unload((any_env_thread_enabled & COMPILED_METHOD_UNLOAD_BIT) != 0);
JvmtiExport::set_should_post_vm_object_alloc((any_env_thread_enabled & VM_OBJECT_ALLOC_BIT) != 0);
JvmtiExport::set_should_post_sampled_object_alloc((any_env_thread_enabled & SAMPLED_OBJECT_ALLOC_BIT) != 0);
// need this if we want thread events or we need them to init data
JvmtiExport::set_should_post_thread_life((any_env_thread_enabled & NEED_THREAD_LIFE_EVENTS) != 0);

View File

@ -1028,12 +1028,12 @@ static inline Klass* oop_to_klass(oop obj) {
return k;
}
class JvmtiVMObjectAllocEventMark : public JvmtiClassEventMark {
class JvmtiObjectAllocEventMark : public JvmtiClassEventMark {
private:
jobject _jobj;
jlong _size;
public:
JvmtiVMObjectAllocEventMark(JavaThread *thread, oop obj) : JvmtiClassEventMark(thread, oop_to_klass(obj)) {
JvmtiObjectAllocEventMark(JavaThread *thread, oop obj) : JvmtiClassEventMark(thread, oop_to_klass(obj)) {
_jobj = (jobject)to_jobject(obj);
_size = obj->size() * wordSize;
};
@ -1198,6 +1198,7 @@ bool JvmtiExport::_should_post_garbage_collection_finish = fals
bool JvmtiExport::_should_post_object_free = false;
bool JvmtiExport::_should_post_resource_exhausted = false;
bool JvmtiExport::_should_post_vm_object_alloc = false;
bool JvmtiExport::_should_post_sampled_object_alloc = false;
bool JvmtiExport::_should_post_on_exceptions = false;
////////////////////////////////////////////////////////////////////////////////////////////////
@ -2280,7 +2281,7 @@ void JvmtiExport::record_vm_internal_object_allocation(oop obj) {
// Can not take safepoint here so can not use state_for to get
// jvmti thread state.
JvmtiThreadState *state = ((JavaThread*)thread)->jvmti_thread_state();
if (state != NULL ) {
if (state != NULL) {
// state is non NULL when VMObjectAllocEventCollector is enabled.
JvmtiVMObjectAllocEventCollector *collector;
collector = state->get_vm_object_alloc_event_collector();
@ -2295,6 +2296,27 @@ void JvmtiExport::record_vm_internal_object_allocation(oop obj) {
}
}
// Collect all the sampled allocated objects.
void JvmtiExport::record_sampled_internal_object_allocation(oop obj) {
Thread* thread = Thread::current_or_null();
if (thread != NULL && thread->is_Java_thread()) {
// Can not take safepoint here.
NoSafepointVerifier no_sfpt;
// Can not take safepoint here so can not use state_for to get
// jvmti thread state.
JvmtiThreadState *state = ((JavaThread*)thread)->jvmti_thread_state();
if (state != NULL) {
// state is non NULL when SampledObjectAllocEventCollector is enabled.
JvmtiSampledObjectAllocEventCollector *collector;
collector = state->get_sampled_object_alloc_event_collector();
if (collector != NULL && collector->is_enabled()) {
collector->record_allocation(obj);
}
}
}
}
void JvmtiExport::post_garbage_collection_finish() {
Thread *thread = Thread::current(); // this event is posted from VM-Thread.
EVT_TRIG_TRACE(JVMTI_EVENT_GARBAGE_COLLECTION_FINISH,
@ -2484,8 +2506,7 @@ void JvmtiExport::post_monitor_waited(JavaThread *thread, ObjectMonitor *obj_mnt
}
}
void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) {
void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) {
EVT_TRIG_TRACE(JVMTI_EVENT_VM_OBJECT_ALLOC, ("[%s] Trg vm object alloc triggered",
JvmtiTrace::safe_get_thread_name(thread)));
if (object == NULL) {
@ -2500,7 +2521,7 @@ void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) {
JvmtiTrace::safe_get_thread_name(thread),
object==NULL? "NULL" : object->klass()->external_name()));
JvmtiVMObjectAllocEventMark jem(thread, h());
JvmtiObjectAllocEventMark jem(thread, h());
JvmtiJavaThreadEventTransition jet(thread);
jvmtiEventVMObjectAlloc callback = env->callbacks()->VMObjectAlloc;
if (callback != NULL) {
@ -2511,6 +2532,34 @@ void JvmtiExport::post_vm_object_alloc(JavaThread *thread, oop object) {
}
}
void JvmtiExport::post_sampled_object_alloc(JavaThread *thread, oop object) {
EVT_TRIG_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC,
("[%s] Trg sampled object alloc triggered",
JvmtiTrace::safe_get_thread_name(thread)));
if (object == NULL) {
return;
}
HandleMark hm(thread);
Handle h(thread, object);
JvmtiEnvIterator it;
for (JvmtiEnv* env = it.first(); env != NULL; env = it.next(env)) {
if (env->is_enabled(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC)) {
EVT_TRACE(JVMTI_EVENT_SAMPLED_OBJECT_ALLOC,
("[%s] Evt sampled object alloc sent %s",
JvmtiTrace::safe_get_thread_name(thread),
object == NULL ? "NULL" : object->klass()->external_name()));
JvmtiObjectAllocEventMark jem(thread, h());
JvmtiJavaThreadEventTransition jet(thread);
jvmtiEventSampledObjectAlloc callback = env->callbacks()->SampledObjectAlloc;
if (callback != NULL) {
(*callback)(env->jvmti_external(), jem.jni_env(), jem.jni_thread(),
jem.jni_jobject(), jem.jni_class(), jem.size());
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////
void JvmtiExport::cleanup_thread(JavaThread* thread) {
@ -2536,7 +2585,7 @@ void JvmtiExport::clear_detected_exception(JavaThread* thread) {
void JvmtiExport::oops_do(OopClosure* f) {
JvmtiCurrentBreakpoints::oops_do(f);
JvmtiVMObjectAllocEventCollector::oops_do_for_all_threads(f);
JvmtiObjectAllocEventCollector::oops_do_for_all_threads(f);
}
void JvmtiExport::weak_oops_do(BoolObjectClosure* is_alive, OopClosure* f) {
@ -2669,12 +2718,28 @@ void JvmtiEventCollector::setup_jvmti_thread_state() {
} else if (is_dynamic_code_event()) {
_prev = state->get_dynamic_code_event_collector();
state->set_dynamic_code_event_collector((JvmtiDynamicCodeEventCollector *)this);
} else if (is_sampled_object_alloc_event()) {
JvmtiSampledObjectAllocEventCollector *prev = state->get_sampled_object_alloc_event_collector();
if (prev) {
// JvmtiSampledObjectAllocEventCollector wants only one active collector
// enabled. This allows to have a collector detect a user code requiring
// a sample in the callback.
return;
}
state->set_sampled_object_alloc_event_collector((JvmtiSampledObjectAllocEventCollector*) this);
}
_unset_jvmti_thread_state = true;
}
// Unset current event collection in this thread and reset it with previous
// collector.
void JvmtiEventCollector::unset_jvmti_thread_state() {
if (!_unset_jvmti_thread_state) {
return;
}
JvmtiThreadState* state = JavaThread::current()->jvmti_thread_state();
if (state != NULL) {
// restore the previous event collector (if any)
@ -2685,14 +2750,19 @@ void JvmtiEventCollector::unset_jvmti_thread_state() {
// this thread's jvmti state was created during the scope of
// the event collector.
}
} else {
if (is_dynamic_code_event()) {
if (state->get_dynamic_code_event_collector() == this) {
state->set_dynamic_code_event_collector((JvmtiDynamicCodeEventCollector *)_prev);
} else {
// this thread's jvmti state was created during the scope of
// the event collector.
}
} else if (is_dynamic_code_event()) {
if (state->get_dynamic_code_event_collector() == this) {
state->set_dynamic_code_event_collector((JvmtiDynamicCodeEventCollector *)_prev);
} else {
// this thread's jvmti state was created during the scope of
// the event collector.
}
} else if (is_sampled_object_alloc_event()) {
if (state->get_sampled_object_alloc_event_collector() == this) {
state->set_sampled_object_alloc_event_collector((JvmtiSampledObjectAllocEventCollector*)_prev);
} else {
// this thread's jvmti state was created during the scope of
// the event collector.
}
}
}
@ -2730,31 +2800,25 @@ void JvmtiDynamicCodeEventCollector::register_stub(const char* name, address sta
}
// Setup current thread to record vm allocated objects.
JvmtiVMObjectAllocEventCollector::JvmtiVMObjectAllocEventCollector() : _allocated(NULL) {
if (JvmtiExport::should_post_vm_object_alloc()) {
_enable = true;
setup_jvmti_thread_state();
} else {
_enable = false;
}
JvmtiObjectAllocEventCollector::JvmtiObjectAllocEventCollector() :
_allocated(NULL), _enable(false), _post_callback(NULL) {
}
// Post vm_object_alloc event for vm allocated objects visible to java
// world.
JvmtiVMObjectAllocEventCollector::~JvmtiVMObjectAllocEventCollector() {
if (_allocated != NULL) {
void JvmtiObjectAllocEventCollector::generate_call_for_allocated() {
if (_allocated) {
set_enabled(false);
for (int i = 0; i < _allocated->length(); i++) {
oop obj = _allocated->at(i);
JvmtiExport::post_vm_object_alloc(JavaThread::current(), obj);
_post_callback(JavaThread::current(), obj);
}
delete _allocated;
delete _allocated, _allocated = NULL;
}
unset_jvmti_thread_state();
}
void JvmtiVMObjectAllocEventCollector::record_allocation(oop obj) {
assert(is_enabled(), "VM object alloc event collector is not enabled");
void JvmtiObjectAllocEventCollector::record_allocation(oop obj) {
assert(is_enabled(), "Object alloc event collector is not enabled");
if (_allocated == NULL) {
_allocated = new (ResourceObj::C_HEAP, mtInternal) GrowableArray<oop>(1, true);
}
@ -2762,9 +2826,9 @@ void JvmtiVMObjectAllocEventCollector::record_allocation(oop obj) {
}
// GC support.
void JvmtiVMObjectAllocEventCollector::oops_do(OopClosure* f) {
if (_allocated != NULL) {
for(int i=_allocated->length() - 1; i >= 0; i--) {
void JvmtiObjectAllocEventCollector::oops_do(OopClosure* f) {
if (_allocated) {
for(int i = _allocated->length() - 1; i >= 0; i--) {
if (_allocated->at(i) != NULL) {
f->do_oop(_allocated->adr_at(i));
}
@ -2772,7 +2836,7 @@ void JvmtiVMObjectAllocEventCollector::oops_do(OopClosure* f) {
}
}
void JvmtiVMObjectAllocEventCollector::oops_do_for_all_threads(OopClosure* f) {
void JvmtiObjectAllocEventCollector::oops_do_for_all_threads(OopClosure* f) {
// no-op if jvmti not enabled
if (!JvmtiEnv::environments_might_exist()) {
return;
@ -2781,11 +2845,17 @@ void JvmtiVMObjectAllocEventCollector::oops_do_for_all_threads(OopClosure* f) {
for (JavaThreadIteratorWithHandle jtiwh; JavaThread *jthr = jtiwh.next(); ) {
JvmtiThreadState *state = jthr->jvmti_thread_state();
if (state != NULL) {
JvmtiVMObjectAllocEventCollector *collector;
JvmtiObjectAllocEventCollector *collector;
collector = state->get_vm_object_alloc_event_collector();
while (collector != NULL) {
collector->oops_do(f);
collector = (JvmtiVMObjectAllocEventCollector *)collector->get_prev();
collector = (JvmtiObjectAllocEventCollector*) collector->get_prev();
}
collector = state->get_sampled_object_alloc_event_collector();
while (collector != NULL) {
collector->oops_do(f);
collector = (JvmtiObjectAllocEventCollector*) collector->get_prev();
}
}
}
@ -2820,6 +2890,63 @@ NoJvmtiVMObjectAllocMark::~NoJvmtiVMObjectAllocMark() {
}
};
// Setup current thread to record vm allocated objects.
JvmtiVMObjectAllocEventCollector::JvmtiVMObjectAllocEventCollector() {
if (JvmtiExport::should_post_vm_object_alloc()) {
_enable = true;
setup_jvmti_thread_state();
_post_callback = JvmtiExport::post_vm_object_alloc;
}
}
JvmtiVMObjectAllocEventCollector::~JvmtiVMObjectAllocEventCollector() {
if (_enable) {
generate_call_for_allocated();
}
unset_jvmti_thread_state();
}
bool JvmtiSampledObjectAllocEventCollector::object_alloc_is_safe_to_sample() {
Thread* thread = Thread::current();
// Really only sample allocations if this is a JavaThread and not the compiler
// thread.
if (!thread->is_Java_thread() || thread->is_Compiler_thread()) {
return false;
}
if (Compile_lock->owner() == thread ||
MultiArray_lock->owner() == thread) {
return false;
}
return true;
}
// Setup current thread to record sampled allocated objects.
JvmtiSampledObjectAllocEventCollector::JvmtiSampledObjectAllocEventCollector() {
if (JvmtiExport::should_post_sampled_object_alloc()) {
if (!object_alloc_is_safe_to_sample()) {
return;
}
_enable = true;
setup_jvmti_thread_state();
_post_callback = JvmtiExport::post_sampled_object_alloc;
}
}
JvmtiSampledObjectAllocEventCollector::~JvmtiSampledObjectAllocEventCollector() {
if (!_enable) {
return;
}
generate_call_for_allocated();
unset_jvmti_thread_state();
// Unset the sampling collector as present in assertion mode only.
assert(Thread::current()->is_Java_thread(),
"Should always be in a Java thread");
}
JvmtiGCMarker::JvmtiGCMarker() {
// if there aren't any JVMTI environments then nothing to do
if (!JvmtiEnv::environments_might_exist()) {

View File

@ -123,6 +123,7 @@ class JvmtiExport : public AllStatic {
// breakpoint info
JVMTI_SUPPORT_FLAG(should_clean_up_heap_objects)
JVMTI_SUPPORT_FLAG(should_post_vm_object_alloc)
JVMTI_SUPPORT_FLAG(should_post_sampled_object_alloc)
// If flag cannot be implemented, give an error if on=true
static void report_unsupported(bool on);
@ -363,6 +364,18 @@ class JvmtiExport : public AllStatic {
record_vm_internal_object_allocation(object);
}
}
static void record_sampled_internal_object_allocation(oop object) NOT_JVMTI_RETURN;
// Post objects collected by sampled_object_alloc_event_collector.
static void post_sampled_object_alloc(JavaThread *thread, oop object) NOT_JVMTI_RETURN;
// Collects vm internal objects for later event posting.
inline static void sampled_object_alloc_event_collector(oop object) {
if (should_post_sampled_object_alloc()) {
record_sampled_internal_object_allocation(object);
}
}
inline static void post_array_size_exhausted() {
if (should_post_resource_exhausted()) {
post_resource_exhausted(JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR,
@ -422,12 +435,16 @@ class JvmtiCodeBlobDesc : public CHeapObj<mtInternal> {
class JvmtiEventCollector : public StackObj {
private:
JvmtiEventCollector* _prev; // Save previous one to support nested event collector.
bool _unset_jvmti_thread_state;
public:
void setup_jvmti_thread_state(); // Set this collector in current thread.
JvmtiEventCollector() : _prev(NULL), _unset_jvmti_thread_state(false) {}
void setup_jvmti_thread_state(); // Set this collector in current thread, returns if success.
void unset_jvmti_thread_state(); // Reset previous collector in current thread.
virtual bool is_dynamic_code_event() { return false; }
virtual bool is_vm_object_alloc_event(){ return false; }
virtual bool is_sampled_object_alloc_event(){ return false; }
JvmtiEventCollector *get_prev() { return _prev; }
};
@ -462,42 +479,67 @@ class JvmtiDynamicCodeEventCollector : public JvmtiEventCollector {
};
// Used to record vm internally allocated object oops and post
// vm object alloc event for objects visible to java world.
// Constructor enables JvmtiThreadState flag and all vm allocated
// objects are recorded in a growable array. When destructor is
// called the vm object alloc event is posted for each objects
// visible to java world.
// See jvm.cpp file for its usage.
// Used as a base class for object allocation collection and then posting
// the allocations to any event notification callbacks.
//
class JvmtiVMObjectAllocEventCollector : public JvmtiEventCollector {
private:
GrowableArray<oop>* _allocated; // field to record vm internally allocated object oop.
bool _enable; // This flag is enabled in constructor and disabled
// in destructor before posting event. To avoid
class JvmtiObjectAllocEventCollector : public JvmtiEventCollector {
protected:
GrowableArray<oop>* _allocated; // field to record collected allocated object oop.
bool _enable; // This flag is enabled in constructor if set up in the thread state
// and disabled in destructor before posting event. To avoid
// collection of objects allocated while running java code inside
// agent post_vm_object_alloc() event handler.
// agent post_X_object_alloc() event handler.
void (*_post_callback)(JavaThread*, oop); // what callback to use when destroying the collector.
//GC support
void oops_do(OopClosure* f);
friend class JvmtiExport;
// Record vm allocated object oop.
// Record allocated object oop.
inline void record_allocation(oop obj);
//GC support
static void oops_do_for_all_threads(OopClosure* f);
public:
JvmtiVMObjectAllocEventCollector() NOT_JVMTI_RETURN;
~JvmtiVMObjectAllocEventCollector() NOT_JVMTI_RETURN;
bool is_vm_object_alloc_event() { return true; }
JvmtiObjectAllocEventCollector() NOT_JVMTI_RETURN;
void generate_call_for_allocated();
bool is_enabled() { return _enable; }
void set_enabled(bool on) { _enable = on; }
};
// Used to record vm internally allocated object oops and post
// vm object alloc event for objects visible to java world.
// Constructor enables JvmtiThreadState flag and all vm allocated
// objects are recorded in a growable array. When destructor is
// called the vm object alloc event is posted for each object
// visible to java world.
// See jvm.cpp file for its usage.
//
class JvmtiVMObjectAllocEventCollector : public JvmtiObjectAllocEventCollector {
public:
JvmtiVMObjectAllocEventCollector() NOT_JVMTI_RETURN;
~JvmtiVMObjectAllocEventCollector() NOT_JVMTI_RETURN;
virtual bool is_vm_object_alloc_event() { return true; }
};
// Used to record sampled allocated object oops and post
// sampled object alloc event.
// Constructor enables JvmtiThreadState flag and all sampled allocated
// objects are recorded in a growable array. When destructor is
// called the sampled object alloc event is posted for each sampled object.
// See jvm.cpp file for its usage.
//
class JvmtiSampledObjectAllocEventCollector : public JvmtiObjectAllocEventCollector {
public:
JvmtiSampledObjectAllocEventCollector() NOT_JVMTI_RETURN;
~JvmtiSampledObjectAllocEventCollector() NOT_JVMTI_RETURN;
bool is_sampled_object_alloc_event() { return true; }
static bool object_alloc_is_safe_to_sample();
};
// Marker class to disable the posting of VMObjectAlloc events
// within its scope.

View File

@ -130,6 +130,7 @@ jvmtiCapabilities JvmtiManageCapabilities::init_always_solo_capabilities() {
memset(&jc, 0, sizeof(jc));
jc.can_suspend = 1;
jc.can_generate_sampled_object_alloc_events = 1;
return jc;
}
@ -410,6 +411,8 @@ void JvmtiManageCapabilities:: print(const jvmtiCapabilities* cap) {
log_trace(jvmti)("can_generate_frame_pop_events");
if (cap->can_generate_breakpoint_events)
log_trace(jvmti)("can_generate_breakpoint_events");
if (cap->can_generate_sampled_object_alloc_events)
log_trace(jvmti)("can_generate_sampled_object_alloc_events");
if (cap->can_suspend)
log_trace(jvmti)("can_suspend");
if (cap->can_redefine_any_class )

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2018, 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
@ -60,6 +60,7 @@ JvmtiThreadState::JvmtiThreadState(JavaThread* thread)
_head_env_thread_state = NULL;
_dynamic_code_event_collector = NULL;
_vm_object_alloc_event_collector = NULL;
_sampled_object_alloc_event_collector = NULL;
_the_class_for_redefinition_verification = NULL;
_scratch_class_for_redefinition_verification = NULL;
_cur_stack_depth = UNKNOWN_STACK_DEPTH;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2018 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
@ -113,6 +113,8 @@ class JvmtiThreadState : public CHeapObj<mtInternal> {
JvmtiDynamicCodeEventCollector* _dynamic_code_event_collector;
// holds the current vm object alloc event collector, NULL if no event collector in use
JvmtiVMObjectAllocEventCollector* _vm_object_alloc_event_collector;
// holds the current sampled object alloc event collector, NULL if no event collector in use
JvmtiSampledObjectAllocEventCollector* _sampled_object_alloc_event_collector;
// Should only be created by factory methods
JvmtiThreadState(JavaThread *thread);
@ -314,12 +316,18 @@ class JvmtiThreadState : public CHeapObj<mtInternal> {
JvmtiVMObjectAllocEventCollector* get_vm_object_alloc_event_collector() {
return _vm_object_alloc_event_collector;
}
JvmtiSampledObjectAllocEventCollector* get_sampled_object_alloc_event_collector() {
return _sampled_object_alloc_event_collector;
}
void set_dynamic_code_event_collector(JvmtiDynamicCodeEventCollector* collector) {
_dynamic_code_event_collector = collector;
}
void set_vm_object_alloc_event_collector(JvmtiVMObjectAllocEventCollector* collector) {
_vm_object_alloc_event_collector = collector;
}
void set_sampled_object_alloc_event_collector(JvmtiSampledObjectAllocEventCollector* collector) {
_sampled_object_alloc_event_collector = collector;
}
//

View File

@ -131,6 +131,8 @@ Monitor* Service_lock = NULL;
Monitor* PeriodicTask_lock = NULL;
Monitor* RedefineClasses_lock = NULL;
Mutex* ThreadHeapSampler_lock = NULL;
#if INCLUDE_JFR
Mutex* JfrStacktrace_lock = NULL;
Monitor* JfrMsg_lock = NULL;
@ -296,6 +298,9 @@ void mutex_init() {
def(CompileThread_lock , PaddedMonitor, nonleaf+5, false, Monitor::_safepoint_check_always);
def(PeriodicTask_lock , PaddedMonitor, nonleaf+5, true, Monitor::_safepoint_check_sometimes);
def(RedefineClasses_lock , PaddedMonitor, nonleaf+5, true, Monitor::_safepoint_check_always);
def(ThreadHeapSampler_lock , PaddedMutex, nonleaf, false, Monitor::_safepoint_check_never);
if (WhiteBoxAPI) {
def(Compilation_lock , PaddedMonitor, leaf, false, Monitor::_safepoint_check_never);
}

View File

@ -130,6 +130,7 @@ extern Mutex* Management_lock; // a lock used to serialize JVM
extern Monitor* Service_lock; // a lock used for service thread operation
extern Monitor* PeriodicTask_lock; // protects the periodic task structure
extern Monitor* RedefineClasses_lock; // locks classes from parallel redefinition
extern Mutex* ThreadHeapSampler_lock; // protects the static data for initialization.
#if INCLUDE_JFR
extern Mutex* JfrStacktrace_lock; // used to guard access to the JFR stacktrace table

View File

@ -42,6 +42,7 @@
#include "runtime/park.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/stubRoutines.hpp"
#include "runtime/threadHeapSampler.hpp"
#include "runtime/threadLocalStorage.hpp"
#include "runtime/unhandledOops.hpp"
#include "utilities/align.hpp"
@ -338,6 +339,7 @@ class Thread: public ThreadShadow {
ThreadLocalAllocBuffer _tlab; // Thread-local eden
jlong _allocated_bytes; // Cumulative number of bytes allocated on
// the Java heap
ThreadHeapSampler _heap_sampler; // For use when sampling the memory.
JFR_ONLY(DEFINE_THREAD_LOCAL_FIELD_JFR;) // Thread-local data for jfr
@ -517,6 +519,8 @@ class Thread: public ThreadShadow {
void incr_allocated_bytes(jlong size) { _allocated_bytes += size; }
inline jlong cooked_allocated_bytes();
ThreadHeapSampler& heap_sampler() { return _heap_sampler; }
JFR_ONLY(DEFINE_THREAD_LOCAL_ACCESSOR_JFR;)
bool is_trace_suspend() { return (_suspend_flags & _trace_flag) != 0; }