Skip to content

Commit bb96a10

Browse files
Improve heap chunk tracking.
1 parent de4dec1 commit bb96a10

12 files changed

Lines changed: 281 additions & 51 deletions

File tree

src/hotspot/share/memory/arena.cpp

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ class ChunkPool {
8787
// Our four static pools
8888
static constexpr int _num_pools = 4;
8989
static ChunkPool _pools[_num_pools];
90+
volatile static bool _suspend_cleaning;
9091

9192
Chunk* _first;
9293
const size_t _size; // (inner payload) size of the chunks this pool serves
@@ -110,7 +111,6 @@ class ChunkPool {
110111
// Clear this pool of all contained chunks
111112
void prune() {
112113
// Free all chunks with ChunkPoolLocker lock
113-
// so NMT adjustment is stable.
114114
ChunkPoolLocker lock;
115115
Chunk* cur = _first;
116116
Chunk* next = nullptr;
@@ -136,6 +136,7 @@ class ChunkPool {
136136
ChunkPool(size_t size) : _first(nullptr), _size(size) {}
137137

138138
static void clean() {
139+
if (_suspend_cleaning) return;
139140
NativeHeapTrimmer::SuspendMark sm("chunk pool cleaner");
140141
for (int i = 0; i < _num_pools; i++) {
141142
_pools[i].prune();
@@ -145,6 +146,9 @@ class ChunkPool {
145146
// Returns an initialized and null-terminated Chunk of requested size
146147
static Chunk* allocate_chunk(Arena* arena, size_t length, AllocFailType alloc_failmode);
147148
static void deallocate_chunk(Chunk* p);
149+
static void set_suspend_cleaning(bool suspend) {
150+
_suspend_cleaning = suspend;
151+
}
148152
};
149153

150154
static bool on_compiler_thread() {
@@ -194,6 +198,7 @@ Chunk* ChunkPool::allocate_chunk(Arena* arena, size_t length, AllocFailType allo
194198
}
195199
chunk = (Chunk*)p;
196200
}
201+
MemTracker::chunk_assigned_to_arena(chunk, arena->get_mem_tag());
197202
::new(chunk) Chunk(length);
198203
// We rely on arena alignment <= malloc alignment.
199204
assert(is_aligned(chunk, ARENA_AMALLOC_ALIGNMENT), "Chunk start address misaligned.");
@@ -218,18 +223,19 @@ void ChunkPool::deallocate_chunk(Chunk* c) {
218223
c->set_stamp(0);
219224
}
220225

226+
MemTracker::add_chunk_to_pool(c);
227+
221228
// If this is a standard-sized chunk, return it to its pool; otherwise free it.
222229
ChunkPool* pool = ChunkPool::get_pool_for_size(c->length());
223230
if (pool != nullptr) {
224231
pool->return_to_pool(c);
225232
} else {
226-
// Free chunks under a lock so that NMT adjustment is stable.
227-
ChunkPoolLocker lock;
228233
os::free(c);
229234
}
230235
}
231236

232237
ChunkPool ChunkPool::_pools[] = { Chunk::size, Chunk::medium_size, Chunk::init_size, Chunk::tiny_size };
238+
volatile bool ChunkPool::_suspend_cleaning = false;
233239

234240
class ChunkPoolCleaner : public PeriodicTask {
235241
static const int cleaning_interval = 5000; // cleaning interval in ms
@@ -241,6 +247,10 @@ class ChunkPoolCleaner : public PeriodicTask {
241247
}
242248
};
243249

250+
void Arena::suspend_chunk_pool_cleaning(bool suspend) {
251+
ChunkPool::set_suspend_cleaning(suspend);
252+
}
253+
244254
void Arena::start_chunk_pool_cleaner_task() {
245255
#ifdef ASSERT
246256
static bool task_created = false;

src/hotspot/share/memory/arena.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ class Arena : public CHeapObjBase {
159159

160160
public:
161161
static void initialize_chunk_pool();
162+
static void suspend_chunk_pool_cleaning(bool suspend);
162163

163164
// Start the chunk_pool cleaner task
164165
static void start_chunk_pool_cleaner_task();

src/hotspot/share/nmt/mallocHeader.hpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ class MallocHeader {
9999
NONCOPYABLE(MallocHeader);
100100
NOT_LP64(uint32_t _alt_canary);
101101
const size_t _size;
102-
const uint32_t _mst_marker;
103-
const MemTag _mem_tag;
102+
uint32_t _mst_marker;
103+
MemTag _mem_tag;
104104
const uint8_t _unused;
105105
uint16_t _canary;
106106

@@ -140,7 +140,9 @@ class MallocHeader {
140140
inline static void revive_block(void* memblock);
141141
inline size_t size() const { return _size; }
142142
inline MemTag mem_tag() const { return _mem_tag; }
143+
inline void set_mem_tag(MemTag new_tag) { _mem_tag = new_tag; }
143144
inline uint32_t mst_marker() const { return _mst_marker; }
145+
inline void set_mst_marker(uint32_t new_marker) { _mst_marker = new_marker; }
144146

145147
// Return the necessary data to deaccount the block with NMT.
146148
FreeInfo free_info() {

src/hotspot/share/nmt/mallocSiteTable.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ bool MallocSiteTable::walk(MallocSiteWalker* walker) {
117117
*/
118118
MallocSite* MallocSiteTable::lookup_or_add(const NativeCallStack& key, uint32_t* marker, MemTag mem_tag) {
119119
assert(mem_tag != mtNone, "Should have a real memory tag");
120-
const unsigned int hash = key.calculate_hash();
120+
const unsigned int hash = malloc_site_hash(key, mem_tag);
121121
const unsigned int index = hash_to_index(hash);
122122
*marker = 0;
123123

src/hotspot/share/nmt/mallocSiteTable.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@ class MallocSite : public AllocationSite {
5454
const MemoryCounter* counter() const { return &_c; }
5555
};
5656

57+
inline unsigned int malloc_site_hash(const NativeCallStack& stack, MemTag tag) {
58+
return stack.calculate_hash() + NMTUtil::tag_to_index(tag) * 73;
59+
}
60+
5761
// Malloc site hashtable entry
5862
class MallocSiteHashtableEntry : public CHeapObj<mtNMT> {
5963
private:
@@ -64,7 +68,7 @@ class MallocSiteHashtableEntry : public CHeapObj<mtNMT> {
6468
public:
6569

6670
MallocSiteHashtableEntry(NativeCallStack stack, MemTag mem_tag):
67-
_malloc_site(stack, mem_tag), _hash(stack.calculate_hash()), _next(nullptr) {
71+
_malloc_site(stack, mem_tag), _hash(malloc_site_hash(stack, mem_tag)), _next(nullptr) {
6872
assert(mem_tag != mtNone, "Expect a real memory tag");
6973
}
7074

src/hotspot/share/nmt/mallocTracker.cpp

Lines changed: 57 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -62,23 +62,16 @@ void MemoryCounter::update_peak(size_t size, size_t cnt) {
6262
}
6363

6464
void MallocMemorySnapshot::copy_to(MallocMemorySnapshot* s) {
65-
// Use lock to make sure that mtChunks don't get deallocated while the
66-
// copy is going on, because their size is adjusted using this
67-
// buffer in make_adjustment().
68-
ChunkPoolLocker::LockStrategy ls = ChunkPoolLocker::LockStrategy::Lock;
69-
if (VMError::is_error_reported() && VMError::is_error_reported_in_current_thread()) {
70-
ls = ChunkPoolLocker::LockStrategy::Try;
71-
}
72-
ChunkPoolLocker cpl(ls);
7365
s->_all_mallocs = _all_mallocs;
7466
size_t total_size = 0;
7567
size_t total_count = 0;
7668
for (int index = 0; index < mt_number_of_tags; index ++) {
7769
s->_malloc[index] = _malloc[index];
78-
total_size += s->_malloc[index].malloc_size();
70+
total_size += s->_malloc[index].malloc_size() + s->_malloc[index].arena_size();
7971
total_count += s->_malloc[index].malloc_count();
8072
}
8173
// malloc counters may be updated concurrently
74+
// Update total here for consistency with saved values of individual sizes.
8275
s->_all_mallocs.set_size_and_count(total_size, total_count);
8376
}
8477

@@ -91,15 +84,6 @@ size_t MallocMemorySnapshot::total_arena() const {
9184
return amount;
9285
}
9386

94-
// Make adjustment by subtracting chunks used by arenas
95-
// from total chunks to get total free chunk size
96-
void MallocMemorySnapshot::make_adjustment() {
97-
size_t arena_size = total_arena();
98-
int chunk_idx = NMTUtil::tag_to_index(mtChunk);
99-
_malloc[chunk_idx].record_free(arena_size);
100-
_all_mallocs.deallocate(arena_size);
101-
}
102-
10387
void MallocMemorySummary::initialize() {
10488
_snapshot.initialize();
10589
MallocLimitHandler::initialize(MallocLimit);
@@ -203,6 +187,61 @@ void* MallocTracker::record_malloc(void* malloc_base, size_t size, MemTag mem_ta
203187
return memblock;
204188
}
205189

190+
void MallocTracker::chunk_assigned_to_arena(void* memblock, MemTag new_tag) {
191+
MallocHeader* header = (MallocHeader*)memblock - 1;
192+
193+
// Only decrement mtChunk, leave the total malloc amounts unchanged.
194+
assert(header->mem_tag() == mtChunk, "Should only be operating on heap chunks");
195+
MallocMemorySummary::as_snapshot()->by_tag(mtChunk)->record_free(header->size());
196+
197+
uint32_t new_mst_marker = 0;
198+
if (MemTracker::tracking_level() == NMT_detail) {
199+
// retrieve the old stack from MST
200+
NativeCallStack old_stack;
201+
if (!MallocSiteTable::access_stack(old_stack, *header)) {
202+
fatal("NMT is now out of sync.");
203+
}
204+
MallocSiteTable::deallocation_at(header->size(), header->mst_marker());
205+
// update MST with new tag
206+
if (!MallocSiteTable::allocation_at(old_stack, header->size(), &new_mst_marker, new_tag)) {
207+
fatal("NMT is now out of sync.");
208+
}
209+
}
210+
211+
// Arena only accounts for payload. Account for the header size and count. Attribute it to the arena's tag.
212+
MallocMemorySummary::as_snapshot()->by_tag(new_tag)->record_malloc(Chunk::aligned_overhead_size());
213+
214+
header->set_mem_tag(new_tag);
215+
header->set_mst_marker(new_mst_marker);
216+
}
217+
218+
void MallocTracker::add_chunk_to_pool(void* memblock) {
219+
MallocHeader* header = (MallocHeader*)memblock - 1;
220+
assert(header->mem_tag() != mtChunk, "Should only be operating on arena chunks");
221+
222+
// Only increment mtChunk, leave the total malloc amounts unchanged.
223+
MallocMemorySummary::as_snapshot()->by_tag(mtChunk)->record_malloc(header->size());
224+
225+
uint32_t new_mst_marker = 0;
226+
if (MemTracker::tracking_level() == NMT_detail) {
227+
NativeCallStack old_stack;
228+
// retrieve the old stack from MST
229+
if (!MallocSiteTable::access_stack(old_stack, *header)) {
230+
fatal("NMT is now out of sync.");
231+
}
232+
MallocSiteTable::deallocation_at(header->size(), header->mst_marker());
233+
// update MST with new tag
234+
if (!MallocSiteTable::allocation_at(old_stack, header->size(), &new_mst_marker, mtChunk)) {
235+
fatal("NMT is now out of sync.");
236+
}
237+
}
238+
239+
MallocMemorySummary::as_snapshot()->by_tag(header->mem_tag())->record_free(Chunk::aligned_overhead_size());
240+
241+
header->set_mem_tag(mtChunk);
242+
header->set_mst_marker(new_mst_marker);
243+
}
244+
206245
void* MallocTracker::record_free_block(void* memblock) {
207246
assert(MemTracker::enabled(), "Sanity");
208247
assert(memblock != nullptr, "precondition");

src/hotspot/share/nmt/mallocTracker.hpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ class MallocMemorySnapshot {
176176

177177
// Total malloc'd memory amount
178178
size_t total() const {
179-
return _all_mallocs.size() + malloc_overhead() + total_arena();
179+
return _all_mallocs.size() + malloc_overhead();
180180
}
181181

182182
// Total peak malloc
@@ -193,10 +193,6 @@ class MallocMemorySnapshot {
193193
size_t total_arena() const;
194194

195195
void copy_to(MallocMemorySnapshot* s);
196-
197-
// Make adjustment by subtracting chunks used by arenas
198-
// from total chunks to get total free chunk size
199-
void make_adjustment();
200196
};
201197

202198
/*
@@ -243,7 +239,6 @@ class MallocMemorySummary : AllStatic {
243239

244240
static void snapshot(MallocMemorySnapshot* s) {
245241
as_snapshot()->copy_to(s);
246-
s->make_adjustment();
247242
}
248243

249244
// The memory used by malloc tracking headers
@@ -283,6 +278,9 @@ class MallocTracker : AllStatic {
283278
static void* record_malloc(void* malloc_base, size_t size, MemTag mem_tag,
284279
const NativeCallStack& stack);
285280

281+
static void chunk_assigned_to_arena(void* memblock, MemTag new_tag);
282+
static void add_chunk_to_pool(void* memblock);
283+
286284
// Given a block returned by os::malloc() or os::realloc():
287285
// deaccount block from NMT, mark its header as dead and return pointer to header.
288286
static void* record_free_block(void* memblock);

src/hotspot/share/nmt/memReporter.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ void MemReporterBase::print_malloc(const MemoryCounter* c, MemTag mem_tag) const
8888
amount_in_current_scale(amount), scale);
8989
}
9090

91-
// blends out mtChunk count number
9291
if (count > 0) {
9392
out->print(" #%zu", count);
9493
}
@@ -705,7 +704,7 @@ void MemSummaryDiffReporter::diff_summary_of_tag(MemTag mem_tag,
705704
if (amount_in_current_scale(current_malloc_amount) > 0 ||
706705
diff_in_current_scale(current_malloc_amount, early_malloc_amount) != 0) {
707706
out->print("(");
708-
print_malloc_diff(current_malloc_amount, (mem_tag == mtChunk) ? 0 : current_malloc->malloc_count(),
707+
print_malloc_diff(current_malloc_amount, current_malloc->malloc_count(),
709708
early_malloc_amount, early_malloc->malloc_count(), mtNone);
710709
out->print_cr(")");
711710
}

src/hotspot/share/nmt/memTracker.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,22 @@ class MemTracker : AllStatic {
9090
return mem_base;
9191
}
9292

93+
static inline void chunk_assigned_to_arena(void* memblock, MemTag new_tag) {
94+
assert(memblock != nullptr, "caller should handle null");
95+
if (!enabled()) {
96+
return;
97+
}
98+
MallocTracker::chunk_assigned_to_arena(memblock, new_tag);
99+
}
100+
101+
static inline void add_chunk_to_pool(void* memblock) {
102+
assert(memblock != nullptr, "caller should handle null");
103+
if (!enabled()) {
104+
return;
105+
}
106+
MallocTracker::add_chunk_to_pool(memblock);
107+
}
108+
93109
// Record malloc free and return malloc base address
94110
static inline void* record_free(void* memblock) {
95111
// Never turned on

src/hotspot/share/nmt/nmtUsage.cpp

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
*
2323
*/
2424

25-
#include "memory/arena.hpp"
2625
#include "nmt/mallocTracker.hpp"
2726
#include "nmt/memoryFileTracker.hpp"
2827
#include "nmt/memTracker.hpp"
@@ -55,33 +54,17 @@ void NMTUsage::walk_thread_stacks() {
5554
}
5655

5756
void NMTUsage::update_malloc_usage() {
58-
MallocMemorySnapshot* ms;
59-
// Lock needed to keep values in sync, total area size
60-
// is deducted from mtChunk in the end to give correct values.
61-
{
62-
ChunkPoolLocker::LockStrategy ls = ChunkPoolLocker::LockStrategy::Lock;
63-
if (VMError::is_error_reported() && VMError::is_error_reported_in_current_thread()) {
64-
ls = ChunkPoolLocker::LockStrategy::Try;
65-
}
66-
ChunkPoolLocker cpl(ls);
67-
ms = MallocMemorySummary::as_snapshot();
68-
}
57+
MallocMemorySnapshot* ms = MallocMemorySummary::as_snapshot();
6958

70-
size_t total_arena_size = 0;
7159
for (int i = 0; i < mt_number_of_tags; i++) {
7260
MemTag mem_tag = NMTUtil::index_to_tag(i);
7361
const MallocMemory* mm = ms->by_tag(mem_tag);
7462
_malloc_by_type[i] = mm->malloc_size() + mm->arena_size();
75-
total_arena_size += mm->arena_size();
7663
}
7764

7865
// Total malloc size.
7966
_malloc_total = ms->total();
8067

81-
// Adjustment due to mtChunk double counting.
82-
_malloc_by_type[NMTUtil::tag_to_index(mtChunk)] -= total_arena_size;
83-
_malloc_total -= total_arena_size;
84-
8568
// Adjust mtNMT to include malloc overhead.
8669
_malloc_by_type[NMTUtil::tag_to_index(mtNMT)] += ms->malloc_overhead();
8770
}

0 commit comments

Comments
 (0)