Skip to content

Commit 79d0669

Browse files
committed
WIP
1 parent 3a5bfec commit 79d0669

10 files changed

+173
-48
lines changed

src/hotspot/share/gc/shared/allocTracer.cpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
#endif
3333

3434
void AllocTracer::send_allocation_outside_tlab(Klass* klass, HeapWord* obj, size_t alloc_size, JavaThread* thread) {
35-
JFR_ONLY(JfrAllocationTracer tracer(klass, obj, alloc_size, true, thread);)
3635
EventObjectAllocationOutsideTLAB event;
3736
if (event.should_commit()) {
3837
event.set_objectClass(klass);
@@ -42,7 +41,6 @@ void AllocTracer::send_allocation_outside_tlab(Klass* klass, HeapWord* obj, size
4241
}
4342

4443
void AllocTracer::send_allocation_in_new_tlab(Klass* klass, HeapWord* obj, size_t tlab_size, size_t alloc_size, JavaThread* thread) {
45-
JFR_ONLY(JfrAllocationTracer tracer(klass, obj, alloc_size, false, thread);)
4644
EventObjectAllocationInNewTLAB event;
4745
if (event.should_commit()) {
4846
event.set_objectClass(klass);
@@ -52,6 +50,10 @@ void AllocTracer::send_allocation_in_new_tlab(Klass* klass, HeapWord* obj, size_
5250
}
5351
}
5452

53+
void AllocTracer::send_allocation_sample(Klass* klass, HeapWord* obj, size_t alloc_size, size_t weight, bool large_allocation, JavaThread* thread) {
54+
JFR_ONLY(JfrAllocationTracer tracer(klass, obj, alloc_size, large_allocation, thread);)
55+
}
56+
5557
void AllocTracer::send_allocation_requiring_gc_event(size_t size, uint gcId) {
5658
EventAllocationRequiringGC event;
5759
if (event.should_commit()) {

src/hotspot/share/gc/shared/allocTracer.hpp

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ class AllocTracer : AllStatic {
3232
public:
3333
static void send_allocation_outside_tlab(Klass* klass, HeapWord* obj, size_t alloc_size, JavaThread* thread);
3434
static void send_allocation_in_new_tlab(Klass* klass, HeapWord* obj, size_t tlab_size, size_t alloc_size, JavaThread* thread);
35+
static void send_allocation_sample(Klass* klass, HeapWord* obj, size_t alloc_size, size_t weight, bool large_allocation, JavaThread* thread);
3536
static void send_allocation_requiring_gc_event(size_t size, uint gcId);
3637
};
3738

src/hotspot/share/gc/shared/memAllocator.cpp

+43-10
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "gc/shared/memAllocator.hpp"
3131
#include "gc/shared/threadLocalAllocBuffer.inline.hpp"
3232
#include "gc/shared/tlab_globals.hpp"
33+
#include "jfr/jfrEvents.hpp"
3334
#include "memory/universe.hpp"
3435
#include "oops/arrayOop.hpp"
3536
#include "oops/oop.inline.hpp"
@@ -41,6 +42,7 @@
4142
#include "services/lowMemoryDetector.hpp"
4243
#include "utilities/align.hpp"
4344
#include "utilities/copy.hpp"
45+
#include "utilities/dtrace.hpp"
4446
#include "utilities/globalDefinitions.hpp"
4547

4648
class MemAllocator::Allocation: StackObj {
@@ -171,7 +173,8 @@ void MemAllocator::Allocation::notify_allocation_jvmti_sampler() {
171173
return;
172174
}
173175

174-
if (!_allocated_outside_tlab && _allocated_tlab_size == 0 && !_tlab_end_reset_for_sample) {
176+
bool hit_mark = _allocated_tlab_size != 0 || _tlab_end_reset_for_sample;
177+
if (!_allocated_outside_tlab && !hit_mark) {
175178
// Sample if it's a non-TLAB allocation, or a TLAB allocation that either refills the TLAB
176179
// or expands it due to taking a sampler induced slow path.
177180
return;
@@ -181,23 +184,21 @@ void MemAllocator::Allocation::notify_allocation_jvmti_sampler() {
181184
// before doing the callback. The callback is done in the destructor of
182185
// the JvmtiSampledObjectAllocEventCollector.
183186
size_t bytes_since_last = 0;
184-
187+
size_t bytes_since_allocation = 0;
185188
{
186189
PreserveObj obj_h(_thread, _obj_ptr);
187190
JvmtiSampledObjectAllocEventCollector collector;
188191
size_t size_in_bytes = _allocator._word_size * HeapWordSize;
189-
ThreadLocalAllocBuffer& tlab = _thread->tlab();
190192

191-
if (!_allocated_outside_tlab) {
192-
bytes_since_last = tlab.bytes_since_last_sample_point();
193+
if (_thread->heap_samplers().jvmti().check_for_sampling(&bytes_since_allocation, size_in_bytes, !_allocated_outside_tlab)) {
194+
JvmtiExport::sampled_object_alloc_event_collector(obj_h());
193195
}
194-
195-
_thread->heap_sampler().check_for_sampling(obj_h(), size_in_bytes, bytes_since_last);
196196
}
197197

198-
if (_tlab_end_reset_for_sample || _allocated_tlab_size != 0) {
198+
if (hit_mark) {
199+
ThreadHeapSampler& sampler = _thread->heap_samplers().jvmti();
199200
// Tell tlab to forget bytes_since_last if we passed it to the heap sampler.
200-
_thread->tlab().set_sample_end(bytes_since_last != 0);
201+
sampler.update_bytes(_thread->tlab().set_sample_end(sampler.bytes_until_sample()), !_allocated_outside_tlab);
201202
}
202203
}
203204

@@ -217,6 +218,36 @@ void MemAllocator::Allocation::notify_allocation_jfr_sampler() {
217218
AllocTracer::send_allocation_in_new_tlab(obj()->klass(), mem, _allocated_tlab_size * HeapWordSize,
218219
size_in_bytes, _thread);
219220
}
221+
222+
EventObjectAllocationSample event;
223+
if (!event.should_commit()) {
224+
return;
225+
}
226+
227+
bool hit_mark = _allocated_tlab_size != 0 || _tlab_end_reset_for_sample;
228+
if (!_allocated_outside_tlab && !hit_mark) {
229+
// Sample if it's a non-TLAB allocation, or a TLAB allocation that either refills the TLAB
230+
// or expands it due to taking a sampler induced slow path.
231+
return;
232+
}
233+
234+
ThreadHeapSampler& sampler = _thread->heap_samplers().jfr();
235+
if (sampler.bytes_until_sample() == static_cast<size_t>(-1)) {
236+
return;
237+
}
238+
239+
size_t bytes_since_allocation = 0;
240+
if (sampler.check_for_sampling(&bytes_since_allocation, size_in_bytes, !_allocated_outside_tlab)) {
241+
size_t weight = bytes_since_allocation == 0 ? size_in_bytes : bytes_since_allocation;
242+
AllocTracer::send_allocation_sample(obj()->klass(), mem, size_in_bytes, weight, _allocated_outside_tlab, _thread);
243+
HOTSPOT_GC_ALLOCOBJECT_SAMPLE(obj()->klass()->name()->as_C_string(), size_in_bytes, weight);
244+
}
245+
246+
if (hit_mark) {
247+
// Tell tlab to forget bytes_since_last if we passed it to the heap sampler.
248+
size_t bytes_inc = _thread->tlab().set_sample_end(sampler.bytes_until_sample());
249+
sampler.update_bytes(bytes_inc, !_allocated_outside_tlab);
250+
}
220251
}
221252

222253
void MemAllocator::Allocation::notify_allocation_dtrace_sampler() {
@@ -258,7 +289,9 @@ HeapWord* MemAllocator::mem_allocate_inside_tlab_slow(Allocation& allocation) co
258289
HeapWord* mem = nullptr;
259290
ThreadLocalAllocBuffer& tlab = _thread->tlab();
260291

261-
if (JvmtiExport::should_post_sampled_object_alloc()) {
292+
EventObjectAllocationSample event;
293+
294+
if (JvmtiExport::should_post_sampled_object_alloc() || event.should_commit()) {
262295
tlab.set_back_allocation_end();
263296
mem = tlab.allocate(_word_size);
264297

src/hotspot/share/gc/shared/threadLocalAllocBuffer.cpp

+15-9
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ ThreadLocalAllocBuffer::ThreadLocalAllocBuffer() :
5050
_desired_size(0),
5151
_refill_waste_limit(0),
5252
_allocated_before_last_gc(0),
53-
_bytes_since_last_sample_point(0),
5453
_number_of_refills(0),
5554
_refill_waste(0),
5655
_gc_waste(0),
@@ -203,6 +202,7 @@ void ThreadLocalAllocBuffer::initialize(HeapWord* start,
203202
set_end(end);
204203
set_allocation_end(end);
205204
invariants();
205+
_end_backup = nullptr;
206206
}
207207

208208
void ThreadLocalAllocBuffer::initialize() {
@@ -313,21 +313,27 @@ void ThreadLocalAllocBuffer::print_stats(const char* tag) {
313313
_refill_waste * HeapWordSize);
314314
}
315315

316-
void ThreadLocalAllocBuffer::set_sample_end(bool reset_byte_accumulation) {
316+
size_t ThreadLocalAllocBuffer::set_sample_end(size_t bytes_until_sample) {
317317
size_t heap_words_remaining = pointer_delta(_end, _top);
318-
size_t bytes_until_sample = thread()->heap_sampler().bytes_until_sample();
319318
size_t words_until_sample = bytes_until_sample / HeapWordSize;
320319

321-
if (reset_byte_accumulation) {
322-
_bytes_since_last_sample_point = 0;
323-
}
324-
325320
if (heap_words_remaining > words_until_sample) {
326321
HeapWord* new_end = _top + words_until_sample;
322+
if (_end_backup != nullptr) {
323+
if (new_end > _end_backup) {
324+
HeapWord* tmp = new_end;
325+
new_end = _end_backup;
326+
_end_backup = tmp;
327+
}
328+
}
327329
set_end(new_end);
328-
_bytes_since_last_sample_point += bytes_until_sample;
330+
return bytes_until_sample;
329331
} else {
330-
_bytes_since_last_sample_point += heap_words_remaining * HeapWordSize;
332+
if (_end_backup != nullptr) {
333+
set_end(_end_backup);
334+
_end_backup = nullptr;
335+
}
336+
return heap_words_remaining * HeapWordSize;
331337
}
332338
}
333339

src/hotspot/share/gc/shared/threadLocalAllocBuffer.hpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ class ThreadLocalAllocBuffer: public CHeapObj<mtThread> {
5151
HeapWord* _top; // address after last allocation
5252
HeapWord* _pf_top; // allocation prefetch watermark
5353
HeapWord* _end; // allocation end (can be the sampling end point or _allocation_end)
54+
HeapWord* _end_backup; // backup of _end for interleaved jvmti and jfr sampling
5455
HeapWord* _allocation_end; // end for allocations (actual TLAB end, excluding alignment_reserve)
5556

5657
size_t _desired_size; // desired size (including alignment_reserve)
5758
size_t _refill_waste_limit; // hold onto tlab if free() is larger than this
5859
size_t _allocated_before_last_gc; // total bytes allocated up until the last gc
59-
size_t _bytes_since_last_sample_point; // bytes since last sample point.
6060

6161
static size_t _max_size; // maximum size of any TLAB
6262
static int _reserve_for_allocation_prefetch; // Reserve at the end of the TLAB
@@ -124,7 +124,6 @@ class ThreadLocalAllocBuffer: public CHeapObj<mtThread> {
124124
size_t free() const { return pointer_delta(end(), top()); }
125125
// Don't discard tlab if remaining space is larger than this.
126126
size_t refill_waste_limit() const { return _refill_waste_limit; }
127-
size_t bytes_since_last_sample_point() const { return _bytes_since_last_sample_point; }
128127

129128
// For external inspection.
130129
const HeapWord* start_relaxed() const;
@@ -168,7 +167,7 @@ class ThreadLocalAllocBuffer: public CHeapObj<mtThread> {
168167
void initialize();
169168

170169
void set_back_allocation_end();
171-
void set_sample_end(bool reset_byte_accumulation);
170+
size_t set_sample_end(size_t bytes_until_sample);
172171

173172
static size_t refill_waste_limit_increment();
174173

src/hotspot/share/jfr/recorder/service/jfrEventThrottler.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "jfr/recorder/service/jfrEventThrottler.hpp"
2828
#include "jfr/utilities/jfrSpinlockHelper.hpp"
2929
#include "logging/log.hpp"
30+
#include "runtime/threadHeapSampler.hpp"
3031

3132
constexpr static const JfrSamplerParams _disabled_params = {
3233
0, // sample points per window
@@ -72,6 +73,9 @@ void JfrEventThrottler::configure(JfrEventId event_id, int64_t sample_size, int6
7273
}
7374
assert(_throttler != nullptr, "JfrEventThrottler has not been properly initialized");
7475
_throttler->configure(sample_size, period_ms);
76+
77+
// TODO: Hack to get the allocation sampler going
78+
ThreadHeapSamplers::set_jfr_sampling_interval(512 * 1024);
7579
}
7680

7781
/*

src/hotspot/share/prims/jvmtiEnv.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -3760,7 +3760,7 @@ JvmtiEnv::SetHeapSamplingInterval(jint sampling_interval) {
37603760
if (sampling_interval < 0) {
37613761
return JVMTI_ERROR_ILLEGAL_ARGUMENT;
37623762
}
3763-
ThreadHeapSampler::set_sampling_interval(sampling_interval);
3763+
ThreadHeapSamplers::set_jvmti_sampling_interval(sampling_interval);
37643764
return JVMTI_ERROR_NONE;
37653765
} /* end SetHeapSamplingInterval */
37663766

src/hotspot/share/runtime/thread.hpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ class Thread: public ThreadShadow {
267267
ThreadLocalAllocBuffer _tlab; // Thread-local eden
268268
jlong _allocated_bytes; // Cumulative number of bytes allocated on
269269
// the Java heap
270-
ThreadHeapSampler _heap_sampler; // For use when sampling the memory.
270+
ThreadHeapSamplers _heap_samplers; // Used by the JVMTI allocation sampler
271271

272272
ThreadStatisticalInfo _statistical_info; // Statistics about the thread
273273

@@ -418,7 +418,7 @@ class Thread: public ThreadShadow {
418418
void incr_allocated_bytes(jlong size) { _allocated_bytes += size; }
419419
inline jlong cooked_allocated_bytes();
420420

421-
ThreadHeapSampler& heap_sampler() { return _heap_sampler; }
421+
ThreadHeapSamplers& heap_samplers() { return _heap_samplers; }
422422

423423
ThreadStatisticalInfo& statistical_info() { return _statistical_info; }
424424

src/hotspot/share/runtime/threadHeapSampler.cpp

+44-15
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,8 @@
3636
#include "oops/oop.inline.hpp"
3737
#include "utilities/dtrace.hpp"
3838

39-
// Cheap random number generator.
40-
uint64_t ThreadHeapSampler::_rnd;
41-
// Default is 512kb.
42-
volatile int ThreadHeapSampler::_sampling_interval = 512 * 1024;
39+
volatile int ThreadHeapSamplers::_jvmti_sampling_interval = 512 * 1024; // 512KiB default
40+
volatile int ThreadHeapSamplers::_jfr_sampling_interval = -1; // disabled
4341

4442
// Statics for the fast log
4543
static const int FastLogNumBits = 10;
@@ -397,7 +395,7 @@ void ThreadHeapSampler::pick_next_geometric_sample() {
397395
// negative answer.
398396
double log_val = (fast_log2(q) - 26);
399397
double result =
400-
(0.0 < log_val ? 0.0 : log_val) * (-log(2.0) * (get_sampling_interval())) + 1;
398+
(0.0 < log_val ? 0.0 : log_val) * (-log(2.0) * (get_interval())) + 1;
401399
assert(result > 0 && result < static_cast<double>(SIZE_MAX), "Result is not in an acceptable range.");
402400
size_t interval = static_cast<size_t>(result);
403401
_bytes_until_sample = interval;
@@ -411,35 +409,66 @@ void ThreadHeapSampler::pick_next_sample(size_t overflowed_bytes) {
411409
#endif
412410
// Explicitly test if the sampling interval is 0, return 0 to sample every
413411
// allocation.
414-
if (get_sampling_interval() == 0) {
412+
int interval = get_interval();
413+
if (interval == 0) {
415414
_bytes_until_sample = 0;
416415
return;
416+
} else if (interval < 0) {
417+
_bytes_until_sample = static_cast<size_t>(-1);
418+
return;
417419
}
418420

419421
pick_next_geometric_sample();
420422
}
421423

422-
void ThreadHeapSampler::check_for_sampling(oop obj, size_t allocation_size, size_t bytes_since_allocation) {
423-
size_t total_allocated_bytes = bytes_since_allocation + allocation_size;
424+
bool ThreadHeapSampler::check_for_sampling(size_t* bytes_since_allocation, size_t allocation_size, bool in_tlab) {
425+
size_t processed_bytes = in_tlab ? _bytes_since_last_sample_point : 0;
426+
427+
size_t total_allocated_bytes = processed_bytes + allocation_size;
424428

429+
*bytes_since_allocation = processed_bytes;
425430
// If not yet time for a sample, skip it.
426431
if (total_allocated_bytes < _bytes_until_sample) {
427432
_bytes_until_sample -= total_allocated_bytes;
428-
return;
433+
return false;
429434
}
430435

431-
HOTSPOT_GC_ALLOCOBJECT_SAMPLE(obj->klass()->name()->as_C_string(), allocation_size, bytes_since_allocation);
432-
433-
JvmtiExport::sampled_object_alloc_event_collector(obj);
434-
435436
size_t overflow_bytes = total_allocated_bytes - _bytes_until_sample;
436437
pick_next_sample(overflow_bytes);
438+
return true;
437439
}
438440

439441
int ThreadHeapSampler::get_sampling_interval() {
440-
return Atomic::load_acquire(&_sampling_interval);
442+
return ThreadHeapSamplers::get_jvmti_sampling_interval();
441443
}
442444

443445
void ThreadHeapSampler::set_sampling_interval(int sampling_interval) {
444-
Atomic::release_store(&_sampling_interval, sampling_interval);
446+
ThreadHeapSamplers::set_jvmti_sampling_interval(sampling_interval);
447+
}
448+
449+
void ThreadHeapSampler::update_bytes(size_t bytes, bool reset) {
450+
if (reset) {
451+
_bytes_since_last_sample_point = 0;
452+
}
453+
_bytes_since_last_sample_point += bytes;
454+
}
455+
456+
void ThreadHeapSamplers::set_jvmti_sampling_interval(int interval) {
457+
Atomic::release_store(&_jvmti_sampling_interval, interval);
458+
}
459+
460+
int ThreadHeapSamplers::get_jvmti_sampling_interval() {
461+
return Atomic::load_acquire(&_jvmti_sampling_interval);
462+
}
463+
464+
void ThreadHeapSamplers::set_jfr_sampling_interval(int interval) {
465+
Atomic::release_store(&_jfr_sampling_interval, interval);
466+
}
467+
468+
int ThreadHeapSamplers::get_jfr_sampling_interval() {
469+
return Atomic::load_acquire(&_jfr_sampling_interval);
470+
}
471+
472+
int ThreadHeapSampler::get_interval() {
473+
return Atomic::load_acquire(_sampling_interval_ref);
445474
}

0 commit comments

Comments
 (0)