Skip to content

Commit 52117c6

Browse files
author
Kim Barrett
committedAug 26, 2020
8251850: Refactor ResourceMark and DeoptResourceMark for better code sharing
Move saved state to ResourceArea, merge most of marks into shared helper. Reviewed-by: stuefe, iklam, tschatzl, xliu, vlivanov
1 parent 0ea0338 commit 52117c6

File tree

6 files changed

+179
-186
lines changed

6 files changed

+179
-186
lines changed
 

‎src/hotspot/share/memory/arena.hpp

-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@ class Chunk: CHeapObj<mtChunk> {
9393
// Fast allocation of memory
9494
class Arena : public CHeapObj<mtNone> {
9595
protected:
96-
friend class ResourceMark;
9796
friend class HandleMark;
9897
friend class NoHandleMark;
9998
friend class VMStructs;

‎src/hotspot/share/memory/resourceArea.cpp

+20-31
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -25,7 +25,7 @@
2525
#include "precompiled.hpp"
2626
#include "memory/allocation.inline.hpp"
2727
#include "memory/resourceArea.inline.hpp"
28-
#include "runtime/mutexLocker.hpp"
28+
#include "runtime/atomic.hpp"
2929
#include "runtime/thread.inline.hpp"
3030
#include "services/memTracker.hpp"
3131

@@ -40,9 +40,25 @@ void ResourceArea::bias_to(MEMFLAGS new_flags) {
4040
}
4141
}
4242

43-
//------------------------------ResourceMark-----------------------------------
44-
debug_only(int ResourceArea::_warned;) // to suppress multiple warnings
43+
#ifdef ASSERT
44+
45+
void ResourceArea::verify_has_resource_mark() {
46+
if (_nesting <= 0) {
47+
// Only report the first occurrence of an allocating thread that
48+
// is missing a ResourceMark, to avoid possible recursive errors
49+
// in error handling.
50+
static volatile bool reported = false;
51+
if (!Atomic::load(&reported)) {
52+
if (!Atomic::cmpxchg(&reported, false, true)) {
53+
fatal("memory leak: allocating without ResourceMark");
54+
}
55+
}
56+
}
57+
}
4558

59+
#endif // ASSERT
60+
61+
//------------------------------ResourceMark-----------------------------------
4662
// The following routines are declared in allocation.hpp and used everywhere:
4763

4864
// Allocation in thread-local resource area
@@ -60,30 +76,3 @@ extern char* resource_reallocate_bytes( char *old, size_t old_size, size_t new_s
6076
extern void resource_free_bytes( char *old, size_t size ) {
6177
Thread::current()->resource_area()->Afree(old, size);
6278
}
63-
64-
#ifdef ASSERT
65-
ResourceMark::ResourceMark(Thread *thread) {
66-
assert(thread == Thread::current(), "not the current thread");
67-
initialize(thread);
68-
}
69-
70-
DeoptResourceMark::DeoptResourceMark(Thread *thread) {
71-
assert(thread == Thread::current(), "not the current thread");
72-
initialize(thread);
73-
}
74-
#endif
75-
76-
77-
//-------------------------------------------------------------------------------
78-
// Non-product code
79-
#ifndef PRODUCT
80-
81-
void ResourceMark::free_malloced_objects() {
82-
Arena::free_malloced_objects(_chunk, _hwm, _max, _area->_hwm);
83-
}
84-
85-
void DeoptResourceMark::free_malloced_objects() {
86-
Arena::free_malloced_objects(_chunk, _hwm, _max, _area->_hwm);
87-
}
88-
89-
#endif
+139-148
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -42,125 +42,173 @@
4242
//------------------------------ResourceArea-----------------------------------
4343
// A ResourceArea is an Arena that supports safe usage of ResourceMark.
4444
class ResourceArea: public Arena {
45-
friend class ResourceMark;
46-
friend class DeoptResourceMark;
4745
friend class VMStructs;
48-
debug_only(int _nesting;) // current # of nested ResourceMarks
49-
debug_only(static int _warned;) // to suppress multiple warnings
46+
47+
#ifdef ASSERT
48+
int _nesting; // current # of nested ResourceMarks
49+
void verify_has_resource_mark();
50+
#endif // ASSERT
5051

5152
public:
52-
ResourceArea(MEMFLAGS flags = mtThread) : Arena(flags) {
53-
debug_only(_nesting = 0;)
54-
}
53+
ResourceArea(MEMFLAGS flags = mtThread) :
54+
Arena(flags) DEBUG_ONLY(COMMA _nesting(0)) {}
5555

56-
ResourceArea(size_t init_size, MEMFLAGS flags = mtThread) : Arena(flags, init_size) {
57-
debug_only(_nesting = 0;);
58-
}
56+
ResourceArea(size_t init_size, MEMFLAGS flags = mtThread) :
57+
Arena(flags, init_size) DEBUG_ONLY(COMMA _nesting(0)) {}
5958

6059
char* allocate_bytes(size_t size, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM);
6160

6261
// Bias this resource area to specific memory type
6362
// (by default, ResourceArea is tagged as mtThread, per-thread general purpose storage)
6463
void bias_to(MEMFLAGS flags);
6564

66-
debug_only(int nesting() const { return _nesting; })
65+
DEBUG_ONLY(int nesting() const { return _nesting; })
66+
67+
// Capture the state of a ResourceArea needed by a ResourceMark for
68+
// rollback to that mark.
69+
class SavedState {
70+
friend class ResourceArea;
71+
Chunk* _chunk;
72+
char* _hwm;
73+
char* _max;
74+
size_t _size_in_bytes;
75+
DEBUG_ONLY(int _nesting;)
76+
77+
public:
78+
SavedState(ResourceArea* area) :
79+
_chunk(area->_chunk),
80+
_hwm(area->_hwm),
81+
_max(area->_max),
82+
_size_in_bytes(area->_size_in_bytes)
83+
DEBUG_ONLY(COMMA _nesting(area->_nesting))
84+
{}
85+
};
86+
87+
// Check and adjust debug-only nesting level.
88+
void activate_state(const SavedState& state) {
89+
assert(_nesting == state._nesting, "precondition");
90+
assert(_nesting >= 0, "precondition");
91+
assert(_nesting < INT_MAX, "nesting overflow");
92+
DEBUG_ONLY(++_nesting;)
93+
}
94+
95+
// Check and adjust debug-only nesting level.
96+
void deactivate_state(const SavedState& state) {
97+
assert(_nesting > state._nesting, "deactivating inactive mark");
98+
assert((_nesting - state._nesting) == 1, "deactivating across another mark");
99+
DEBUG_ONLY(--_nesting;)
100+
}
101+
102+
// Roll back the allocation state to the indicated state values.
103+
// The state must be the current state for this thread.
104+
void rollback_to(const SavedState& state) {
105+
assert(_nesting > state._nesting, "rollback to inactive mark");
106+
assert((_nesting - state._nesting) == 1, "rollback across another mark");
107+
108+
if (UseMallocOnly) {
109+
free_malloced_objects(state._chunk, state._hwm, state._max, _hwm);
110+
}
111+
112+
if (state._chunk->next() != nullptr) { // Delete later chunks.
113+
// Reset size before deleting chunks. Otherwise, the total
114+
// size could exceed the total chunk size.
115+
assert(size_in_bytes() > state._size_in_bytes,
116+
"size: " SIZE_FORMAT ", saved size: " SIZE_FORMAT,
117+
size_in_bytes(), state._size_in_bytes);
118+
set_size_in_bytes(state._size_in_bytes);
119+
state._chunk->next_chop();
120+
} else {
121+
assert(size_in_bytes() == state._size_in_bytes, "Sanity check");
122+
}
123+
_chunk = state._chunk; // Roll back to saved chunk.
124+
_hwm = state._hwm;
125+
_max = state._max;
126+
127+
// Clear out this chunk (to detect allocation bugs)
128+
if (ZapResourceArea) {
129+
memset(state._hwm, badResourceValue, state._max - state._hwm);
130+
}
131+
}
67132
};
68133

69134

70135
//------------------------------ResourceMark-----------------------------------
71136
// A resource mark releases all resources allocated after it was constructed
72137
// when the destructor is called. Typically used as a local variable.
138+
139+
// Shared part of implementation for ResourceMark and DeoptResourceMark.
140+
class ResourceMarkImpl {
141+
ResourceArea* _area; // Resource area to stack allocate
142+
ResourceArea::SavedState _saved_state;
143+
144+
NONCOPYABLE(ResourceMarkImpl);
145+
146+
public:
147+
explicit ResourceMarkImpl(ResourceArea* area) :
148+
_area(area),
149+
_saved_state(area)
150+
{
151+
_area->activate_state(_saved_state);
152+
}
153+
154+
explicit ResourceMarkImpl(Thread* thread)
155+
: ResourceMarkImpl(thread->resource_area()) {}
156+
157+
~ResourceMarkImpl() {
158+
reset_to_mark();
159+
_area->deactivate_state(_saved_state);
160+
}
161+
162+
void reset_to_mark() const {
163+
_area->rollback_to(_saved_state);
164+
}
165+
};
166+
73167
class ResourceMark: public StackObj {
74-
protected:
75-
ResourceArea *_area; // Resource area to stack allocate
76-
Chunk *_chunk; // saved arena chunk
77-
char *_hwm, *_max;
78-
size_t _size_in_bytes;
168+
const ResourceMarkImpl _impl;
79169
#ifdef ASSERT
80170
Thread* _thread;
81171
ResourceMark* _previous_resource_mark;
82-
#endif //ASSERT
83-
84-
void initialize(Thread *thread) {
85-
_area = thread->resource_area();
86-
_chunk = _area->_chunk;
87-
_hwm = _area->_hwm;
88-
_max= _area->_max;
89-
_size_in_bytes = _area->size_in_bytes();
90-
debug_only(_area->_nesting++;)
91-
assert( _area->_nesting > 0, "must stack allocate RMs" );
92-
#ifdef ASSERT
93-
_thread = thread;
94-
_previous_resource_mark = thread->current_resource_mark();
95-
thread->set_current_resource_mark(this);
96172
#endif // ASSERT
97-
}
98-
public:
99173

174+
NONCOPYABLE(ResourceMark);
175+
176+
// Helper providing common constructor implementation.
100177
#ifndef ASSERT
101-
ResourceMark(Thread *thread) {
102-
assert(thread == Thread::current(), "not the current thread");
103-
initialize(thread);
104-
}
178+
ResourceMark(ResourceArea* area, Thread* thread) : _impl(area) {}
105179
#else
106-
ResourceMark(Thread *thread);
107-
#endif // ASSERT
108-
109-
ResourceMark() { initialize(Thread::current()); }
110-
111-
ResourceMark( ResourceArea *r ) :
112-
_area(r), _chunk(r->_chunk), _hwm(r->_hwm), _max(r->_max) {
113-
_size_in_bytes = r->_size_in_bytes;
114-
debug_only(_area->_nesting++;)
115-
assert( _area->_nesting > 0, "must stack allocate RMs" );
116-
#ifdef ASSERT
117-
Thread* thread = Thread::current_or_null();
118-
if (thread != NULL) {
119-
_thread = thread;
120-
_previous_resource_mark = thread->current_resource_mark();
121-
thread->set_current_resource_mark(this);
122-
} else {
123-
_thread = NULL;
124-
_previous_resource_mark = NULL;
180+
ResourceMark(ResourceArea* area, Thread* thread) :
181+
_impl(area),
182+
_thread(thread),
183+
_previous_resource_mark(nullptr)
184+
{
185+
if (_thread != nullptr) {
186+
assert(_thread == Thread::current(), "not the current thread");
187+
_previous_resource_mark = _thread->current_resource_mark();
188+
_thread->set_current_resource_mark(this);
125189
}
126-
#endif // ASSERT
127190
}
191+
#endif // ASSERT
128192

129-
void reset_to_mark() {
130-
if (UseMallocOnly) free_malloced_objects();
193+
public:
131194

132-
if( _chunk->next() ) { // Delete later chunks
133-
// reset arena size before delete chunks. Otherwise, the total
134-
// arena size could exceed total chunk size
135-
assert(_area->size_in_bytes() > size_in_bytes(), "Sanity check");
136-
_area->set_size_in_bytes(size_in_bytes());
137-
_chunk->next_chop();
138-
} else {
139-
assert(_area->size_in_bytes() == size_in_bytes(), "Sanity check");
140-
}
141-
_area->_chunk = _chunk; // Roll back arena to saved chunk
142-
_area->_hwm = _hwm;
143-
_area->_max = _max;
195+
ResourceMark() : ResourceMark(Thread::current()) {}
144196

145-
// clear out this chunk (to detect allocation bugs)
146-
if (ZapResourceArea) memset(_hwm, badResourceValue, _max - _hwm);
147-
}
197+
explicit ResourceMark(Thread* thread)
198+
: ResourceMark(thread->resource_area(), thread) {}
199+
200+
explicit ResourceMark(ResourceArea* area)
201+
: ResourceMark(area, DEBUG_ONLY(Thread::current_or_null()) NOT_DEBUG(nullptr)) {}
148202

149-
~ResourceMark() {
150-
assert( _area->_nesting > 0, "must stack allocate RMs" );
151-
debug_only(_area->_nesting--;)
152-
reset_to_mark();
153203
#ifdef ASSERT
154-
if (_thread != NULL) {
204+
~ResourceMark() {
205+
if (_thread != nullptr) {
155206
_thread->set_current_resource_mark(_previous_resource_mark);
156207
}
157-
#endif // ASSERT
158208
}
209+
#endif // ASSERT
159210

160-
161-
private:
162-
void free_malloced_objects() PRODUCT_RETURN;
163-
size_t size_in_bytes() { return _size_in_bytes; }
211+
void reset_to_mark() { _impl.reset_to_mark(); }
164212
};
165213

166214
//------------------------------DeoptResourceMark-----------------------------------
@@ -190,75 +238,18 @@ class ResourceMark: public StackObj {
190238
// special need for a ResourceMark. If ResourceMark simply inherited from CHeapObj
191239
// then existing ResourceMarks would work fine since no one use new to allocate them
192240
// and they would be stack allocated. This leaves open the possibility of accidental
193-
// misuse so we simple duplicate the ResourceMark functionality here.
241+
// misuse so we duplicate the ResourceMark functionality via a shared implementation
242+
// class.
194243

195244
class DeoptResourceMark: public CHeapObj<mtInternal> {
196-
protected:
197-
ResourceArea *_area; // Resource area to stack allocate
198-
Chunk *_chunk; // saved arena chunk
199-
char *_hwm, *_max;
200-
size_t _size_in_bytes;
201-
202-
void initialize(Thread *thread) {
203-
_area = thread->resource_area();
204-
_chunk = _area->_chunk;
205-
_hwm = _area->_hwm;
206-
_max= _area->_max;
207-
_size_in_bytes = _area->size_in_bytes();
208-
debug_only(_area->_nesting++;)
209-
assert( _area->_nesting > 0, "must stack allocate RMs" );
210-
}
245+
const ResourceMarkImpl _impl;
211246

212-
public:
213-
214-
#ifndef ASSERT
215-
DeoptResourceMark(Thread *thread) {
216-
assert(thread == Thread::current(), "not the current thread");
217-
initialize(thread);
218-
}
219-
#else
220-
DeoptResourceMark(Thread *thread);
221-
#endif // ASSERT
222-
223-
DeoptResourceMark() { initialize(Thread::current()); }
224-
225-
DeoptResourceMark( ResourceArea *r ) :
226-
_area(r), _chunk(r->_chunk), _hwm(r->_hwm), _max(r->_max) {
227-
_size_in_bytes = _area->size_in_bytes();
228-
debug_only(_area->_nesting++;)
229-
assert( _area->_nesting > 0, "must stack allocate RMs" );
230-
}
231-
232-
void reset_to_mark() {
233-
if (UseMallocOnly) free_malloced_objects();
234-
235-
if( _chunk->next() ) { // Delete later chunks
236-
// reset arena size before delete chunks. Otherwise, the total
237-
// arena size could exceed total chunk size
238-
assert(_area->size_in_bytes() > size_in_bytes(), "Sanity check");
239-
_area->set_size_in_bytes(size_in_bytes());
240-
_chunk->next_chop();
241-
} else {
242-
assert(_area->size_in_bytes() == size_in_bytes(), "Sanity check");
243-
}
244-
_area->_chunk = _chunk; // Roll back arena to saved chunk
245-
_area->_hwm = _hwm;
246-
_area->_max = _max;
247-
248-
// clear out this chunk (to detect allocation bugs)
249-
if (ZapResourceArea) memset(_hwm, badResourceValue, _max - _hwm);
250-
}
251-
252-
~DeoptResourceMark() {
253-
assert( _area->_nesting > 0, "must stack allocate RMs" );
254-
debug_only(_area->_nesting--;)
255-
reset_to_mark();
256-
}
247+
NONCOPYABLE(DeoptResourceMark);
257248

249+
public:
250+
explicit DeoptResourceMark(Thread* thread) : _impl(thread) {}
258251

259-
private:
260-
void free_malloced_objects() PRODUCT_RETURN;
261-
size_t size_in_bytes() { return _size_in_bytes; };
252+
void reset_to_mark() { _impl.reset_to_mark(); }
262253
};
263254

264255
#endif // SHARE_MEMORY_RESOURCEAREA_HPP

‎src/hotspot/share/memory/resourceArea.inline.hpp

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 1997, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -29,14 +29,13 @@
2929

3030
inline char* ResourceArea::allocate_bytes(size_t size, AllocFailType alloc_failmode) {
3131
#ifdef ASSERT
32-
if (_nesting < 1 && !_warned++)
33-
fatal("memory leak: allocating without ResourceMark");
32+
verify_has_resource_mark();
3433
if (UseMallocOnly) {
3534
// use malloc, but save pointer in res. area for later freeing
3635
char** save = (char**)internal_malloc_4(sizeof(char*));
3736
return (*save = (char*)os::malloc(size, mtThread, CURRENT_PC));
3837
}
39-
#endif
38+
#endif // ASSERT
4039
return (char*)Amalloc(size, alloc_failmode);
4140
}
4241

‎src/hotspot/share/utilities/vmError.cpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
#include "logging/logConfiguration.hpp"
3333
#include "memory/metaspace.hpp"
3434
#include "memory/metaspaceShared.hpp"
35-
#include "memory/resourceArea.hpp"
35+
#include "memory/resourceArea.inline.hpp"
3636
#include "memory/universe.hpp"
3737
#include "oops/compressedOops.hpp"
3838
#include "prims/whitebox.hpp"
@@ -1867,6 +1867,15 @@ void VMError::controlled_crash(int how) {
18671867
fatal("Force crash with a nested ThreadsListHandle.");
18681868
}
18691869
}
1870+
case 18: {
1871+
// Check for assert when allocating from resource area without a
1872+
// ResourceMark. There must not be a ResourceMark on the
1873+
// current stack when invoking this test case.
1874+
ResourceArea* area = Thread::current()->resource_area();
1875+
assert(area->nesting() == 0, "unexpected ResourceMark");
1876+
area->allocate_bytes(100);
1877+
break;
1878+
}
18701879

18711880
default: tty->print_cr("ERROR: %d: unexpected test_num value.", how);
18721881
}

‎test/hotspot/jtreg/runtime/ErrorHandling/ErrorHandler.java

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -80,5 +80,11 @@ public static void main(String[] args) throws Exception {
8080
for (String p : patterns) {
8181
runTest(i++).shouldMatch(p);
8282
}
83+
84+
// Tests after here could be handled by above iterations, but doing
85+
// so would renumber ErrorHandlerTest cases, requiring updates a
86+
// bunch of other test programs.
87+
88+
runTest(18).shouldContain("memory leak: allocating without ResourceMark");
8389
}
8490
}

0 commit comments

Comments
 (0)