| 1 | /* |
| 2 | * Copyright (c) 1997, 2019, Oracle and/or its affiliates. All rights reserved. |
| 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
| 4 | * |
| 5 | * This code is free software; you can redistribute it and/or modify it |
| 6 | * under the terms of the GNU General Public License version 2 only, as |
| 7 | * published by the Free Software Foundation. |
| 8 | * |
| 9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| 12 | * version 2 for more details (a copy is included in the LICENSE file that |
| 13 | * accompanied this code). |
| 14 | * |
| 15 | * You should have received a copy of the GNU General Public License version |
| 16 | * 2 along with this work; if not, write to the Free Software Foundation, |
| 17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
| 18 | * |
| 19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
| 20 | * or visit www.oracle.com if you need additional information or have any |
| 21 | * questions. |
| 22 | * |
| 23 | */ |
| 24 | |
| 25 | #ifndef SHARE_MEMORY_RESOURCEAREA_HPP |
| 26 | #define SHARE_MEMORY_RESOURCEAREA_HPP |
| 27 | |
| 28 | #include "memory/allocation.hpp" |
| 29 | #include "runtime/thread.hpp" |
| 30 | |
| 31 | // The resource area holds temporary data structures in the VM. |
| 32 | // The actual allocation areas are thread local. Typical usage: |
| 33 | // |
| 34 | // ... |
| 35 | // { |
| 36 | // ResourceMark rm; |
| 37 | // int foo[] = NEW_RESOURCE_ARRAY(int, 64); |
| 38 | // ... |
| 39 | // } |
| 40 | // ... |
| 41 | |
| 42 | //------------------------------ResourceArea----------------------------------- |
| 43 | // A ResourceArea is an Arena that supports safe usage of ResourceMark. |
| 44 | class ResourceArea: public Arena { |
| 45 | friend class ResourceMark; |
| 46 | friend class DeoptResourceMark; |
| 47 | friend class VMStructs; |
| 48 | debug_only(int _nesting;) // current # of nested ResourceMarks |
| 49 | debug_only(static int _warned;) // to suppress multiple warnings |
| 50 | |
| 51 | public: |
| 52 | ResourceArea(MEMFLAGS flags = mtThread) : Arena(flags) { |
| 53 | debug_only(_nesting = 0;) |
| 54 | } |
| 55 | |
| 56 | ResourceArea(size_t init_size, MEMFLAGS flags = mtThread) : Arena(flags, init_size) { |
| 57 | debug_only(_nesting = 0;); |
| 58 | } |
| 59 | |
| 60 | char* allocate_bytes(size_t size, AllocFailType alloc_failmode = AllocFailStrategy::EXIT_OOM); |
| 61 | |
| 62 | // Bias this resource area to specific memory type |
| 63 | // (by default, ResourceArea is tagged as mtThread, per-thread general purpose storage) |
| 64 | void bias_to(MEMFLAGS flags); |
| 65 | |
| 66 | debug_only(int nesting() const { return _nesting; }) |
| 67 | }; |
| 68 | |
| 69 | |
| 70 | //------------------------------ResourceMark----------------------------------- |
| 71 | // A resource mark releases all resources allocated after it was constructed |
| 72 | // when the destructor is called. Typically used as a local variable. |
| 73 | 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; |
| 79 | #ifdef ASSERT |
| 80 | Thread* _thread; |
| 81 | 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); |
| 96 | #endif // ASSERT |
| 97 | } |
| 98 | public: |
| 99 | |
| 100 | #ifndef ASSERT |
| 101 | ResourceMark(Thread *thread) { |
| 102 | assert(thread == Thread::current(), "not the current thread" ); |
| 103 | initialize(thread); |
| 104 | } |
| 105 | #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; |
| 125 | } |
| 126 | #endif // ASSERT |
| 127 | } |
| 128 | |
| 129 | void reset_to_mark() { |
| 130 | if (UseMallocOnly) free_malloced_objects(); |
| 131 | |
| 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; |
| 144 | |
| 145 | // clear out this chunk (to detect allocation bugs) |
| 146 | if (ZapResourceArea) memset(_hwm, badResourceValue, _max - _hwm); |
| 147 | } |
| 148 | |
| 149 | ~ResourceMark() { |
| 150 | assert( _area->_nesting > 0, "must stack allocate RMs" ); |
| 151 | debug_only(_area->_nesting--;) |
| 152 | reset_to_mark(); |
| 153 | #ifdef ASSERT |
| 154 | if (_thread != NULL) { |
| 155 | _thread->set_current_resource_mark(_previous_resource_mark); |
| 156 | } |
| 157 | #endif // ASSERT |
| 158 | } |
| 159 | |
| 160 | |
| 161 | private: |
| 162 | void free_malloced_objects() PRODUCT_RETURN; |
| 163 | size_t size_in_bytes() { return _size_in_bytes; } |
| 164 | }; |
| 165 | |
| 166 | //------------------------------DeoptResourceMark----------------------------------- |
| 167 | // A deopt resource mark releases all resources allocated after it was constructed |
| 168 | // when the destructor is called. Typically used as a local variable. It differs |
| 169 | // from a typical resource more in that it is C-Heap allocated so that deoptimization |
| 170 | // can use data structures that are arena based but are not amenable to vanilla |
| 171 | // ResourceMarks because deoptimization can not use a stack allocated mark. During |
| 172 | // deoptimization we go thru the following steps: |
| 173 | // |
| 174 | // 0: start in assembly stub and call either uncommon_trap/fetch_unroll_info |
| 175 | // 1: create the vframeArray (contains pointers to Resource allocated structures) |
| 176 | // This allocates the DeoptResourceMark. |
| 177 | // 2: return to assembly stub and remove stub frame and deoptee frame and create |
| 178 | // the new skeletal frames. |
| 179 | // 3: push new stub frame and call unpack_frames |
| 180 | // 4: retrieve information from the vframeArray to populate the skeletal frames |
| 181 | // 5: release the DeoptResourceMark |
| 182 | // 6: return to stub and eventually to interpreter |
| 183 | // |
| 184 | // With old style eager deoptimization the vframeArray was created by the vmThread there |
| 185 | // was no way for the vframeArray to contain resource allocated objects and so |
| 186 | // a complex set of data structures to simulate an array of vframes in CHeap memory |
| 187 | // was used. With new style lazy deoptimization the vframeArray is created in the |
| 188 | // the thread that will use it and we can use a much simpler scheme for the vframeArray |
| 189 | // leveraging existing data structures if we simply create a way to manage this one |
| 190 | // special need for a ResourceMark. If ResourceMark simply inherited from CHeapObj |
| 191 | // then existing ResourceMarks would work fine since no one use new to allocate them |
| 192 | // and they would be stack allocated. This leaves open the possibility of accidental |
| 193 | // misuse so we simple duplicate the ResourceMark functionality here. |
| 194 | |
| 195 | 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 | } |
| 211 | |
| 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 | } |
| 257 | |
| 258 | |
| 259 | private: |
| 260 | void free_malloced_objects() PRODUCT_RETURN; |
| 261 | size_t size_in_bytes() { return _size_in_bytes; }; |
| 262 | }; |
| 263 | |
| 264 | #endif // SHARE_MEMORY_RESOURCEAREA_HPP |
| 265 | |