-
Notifications
You must be signed in to change notification settings - Fork 54
/
ZoneAllocator.h
336 lines (277 loc) · 10.5 KB
/
ZoneAllocator.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* Public header for allocating memory associated with GC things.
*/
#ifndef gc_ZoneAllocator_h
#define gc_ZoneAllocator_h
#include "gc/Scheduling.h"
#include "vm/Runtime.h" // For JSRuntime::gc.
namespace JS {
class Zone;
} // namespace JS
namespace js {
namespace gc {
void MaybeMallocTriggerZoneGC(JSRuntime* rt, ZoneAllocator* zoneAlloc);
}
// Base class of JS::Zone that provides malloc memory allocation and accounting.
class ZoneAllocator : public JS::shadow::Zone,
public js::MallocProvider<JS::Zone> {
protected:
explicit ZoneAllocator(JSRuntime* rt);
~ZoneAllocator();
void fixupAfterMovingGC();
public:
static ZoneAllocator* from(JS::Zone* zone) {
// This is a safe downcast, but the compiler hasn't seen the definition yet.
return reinterpret_cast<ZoneAllocator*>(zone);
}
MOZ_MUST_USE void* onOutOfMemory(js::AllocFunction allocFunc,
arena_id_t arena, size_t nbytes,
void* reallocPtr = nullptr);
void reportAllocationOverflow() const;
void setGCMaxMallocBytes(size_t value, const js::AutoLockGC& lock) {
gcMallocCounter.setMax(value, lock);
}
void updateMallocCounter(size_t nbytes) {
updateMemoryCounter(gcMallocCounter, nbytes);
}
void adoptMallocBytes(ZoneAllocator* other) {
gcMallocCounter.adopt(other->gcMallocCounter);
gcMallocBytes.adopt(other->gcMallocBytes);
#ifdef DEBUG
gcMallocTracker.adopt(other->gcMallocTracker);
#endif
}
size_t GCMaxMallocBytes() const { return gcMallocCounter.maxBytes(); }
size_t GCMallocBytes() const { return gcMallocCounter.bytes(); }
void updateJitCodeMallocBytes(size_t nbytes) {
updateMemoryCounter(jitCodeCounter, nbytes);
}
void updateAllGCMallocCountersOnGCStart();
void updateAllGCMallocCountersOnGCEnd(const js::AutoLockGC& lock);
void updateAllGCThresholds(gc::GCRuntime& gc,
JSGCInvocationKind invocationKind,
const js::AutoLockGC& lock);
js::gc::TriggerKind shouldTriggerGCForTooMuchMalloc();
// Memory accounting APIs for malloc memory owned by GC cells.
void addCellMemory(js::gc::Cell* cell, size_t nbytes, js::MemoryUse use) {
MOZ_ASSERT(cell);
MOZ_ASSERT(nbytes);
gcMallocBytes.addBytes(nbytes);
// We don't currently check GC triggers here.
#ifdef DEBUG
gcMallocTracker.trackMemory(cell, nbytes, use);
#endif
}
void removeCellMemory(js::gc::Cell* cell, size_t nbytes, js::MemoryUse use) {
MOZ_ASSERT(cell);
MOZ_ASSERT(nbytes);
gcMallocBytes.removeBytes(nbytes);
#ifdef DEBUG
gcMallocTracker.untrackMemory(cell, nbytes, use);
#endif
}
void swapCellMemory(js::gc::Cell* a, js::gc::Cell* b, js::MemoryUse use) {
#ifdef DEBUG
gcMallocTracker.swapMemory(a, b, use);
#endif
}
#ifdef DEBUG
void registerPolicy(js::ZoneAllocPolicy* policy) {
return gcMallocTracker.registerPolicy(policy);
}
void unregisterPolicy(js::ZoneAllocPolicy* policy) {
return gcMallocTracker.unregisterPolicy(policy);
}
#endif
void incPolicyMemory(js::ZoneAllocPolicy* policy, size_t nbytes) {
MOZ_ASSERT(nbytes);
gcMallocBytes.addBytes(nbytes);
#ifdef DEBUG
gcMallocTracker.incPolicyMemory(policy, nbytes);
#endif
maybeMallocTriggerZoneGC();
}
void decPolicyMemory(js::ZoneAllocPolicy* policy, size_t nbytes) {
MOZ_ASSERT(nbytes);
gcMallocBytes.removeBytes(nbytes);
#ifdef DEBUG
gcMallocTracker.decPolicyMemory(policy, nbytes);
#endif
}
// Check malloc allocation threshold and trigger a zone GC if necessary.
void maybeMallocTriggerZoneGC() {
JSRuntime* rt = runtimeFromAnyThread();
if (gcMallocBytes.gcBytes() >= gcMallocThreshold.gcTriggerBytes() &&
rt->heapState() == JS::HeapState::Idle) {
gc::MaybeMallocTriggerZoneGC(rt, this);
}
}
private:
void updateMemoryCounter(js::gc::MemoryCounter& counter, size_t nbytes) {
JSRuntime* rt = runtimeFromAnyThread();
counter.update(nbytes);
auto trigger = counter.shouldTriggerGC(rt->gc.tunables);
if (MOZ_LIKELY(trigger == js::gc::NoTrigger) ||
trigger <= counter.triggered()) {
return;
}
maybeTriggerGCForTooMuchMalloc(counter, trigger);
}
void maybeTriggerGCForTooMuchMalloc(js::gc::MemoryCounter& counter,
js::gc::TriggerKind trigger);
public:
// Track GC heap size under this Zone.
js::gc::HeapSize zoneSize;
// Thresholds used to trigger GC based on heap size.
js::gc::ZoneHeapThreshold threshold;
// Amount of data to allocate before triggering a new incremental slice for
// the current GC.
js::MainThreadData<size_t> gcDelayBytes;
private:
// Malloc counter to measure memory pressure for GC scheduling. This counter
// is used for allocations where the size of the allocation is not known on
// free. Currently this is used for all internal malloc allocations.
js::gc::MemoryCounter gcMallocCounter;
public:
// Malloc counter used for allocations where size information is
// available. Used for some internal and all tracked external allocations.
js::gc::HeapSize gcMallocBytes;
// Thresholds used to trigger GC based on malloc allocations.
js::gc::ZoneMallocThreshold gcMallocThreshold;
private:
#ifdef DEBUG
// In debug builds, malloc allocations can be tracked to make debugging easier
// (possible?) if allocation and free sizes don't balance.
js::gc::MemoryTracker gcMallocTracker;
#endif
// Counter of JIT code executable memory for GC scheduling. Also imprecise,
// since wasm can generate code that outlives a zone.
js::gc::MemoryCounter jitCodeCounter;
friend class js::gc::GCRuntime;
};
/*
* Allocation policy that performs precise memory tracking on the zone. This
* should be used for all containers associated with a GC thing or a zone.
*
* Since it doesn't hold a JSContext (those may not live long enough), it can't
* report out-of-memory conditions itself; the caller must check for OOM and
* take the appropriate action.
*
* FIXME bug 647103 - replace these *AllocPolicy names.
*/
class ZoneAllocPolicy : public MallocProvider<ZoneAllocPolicy> {
ZoneAllocator* zone_;
#ifdef DEBUG
friend class js::gc::MemoryTracker; // Can clear |zone_| on merge.
#endif
public:
MOZ_IMPLICIT ZoneAllocPolicy(ZoneAllocator* z) : zone_(z) {
#ifdef DEBUG
zone()->registerPolicy(this);
#endif
}
ZoneAllocPolicy(ZoneAllocPolicy& other) : ZoneAllocPolicy(other.zone_) {}
ZoneAllocPolicy(ZoneAllocPolicy&& other) : ZoneAllocPolicy(other.zone_) {}
~ZoneAllocPolicy() {
#ifdef DEBUG
if (zone_) {
zone_->unregisterPolicy(this);
}
#endif
}
// Public methods required to fulfill the AllocPolicy interface.
template <typename T>
void free_(T* p, size_t numElems) {
if (p) {
decMemory(numElems * sizeof(T));
js_free(p);
}
}
MOZ_MUST_USE bool checkSimulatedOOM() const {
return !js::oom::ShouldFailWithOOM();
}
void reportAllocOverflow() const { reportAllocationOverflow(); }
// Internal methods called by the MallocProvider implementation.
MOZ_MUST_USE void* onOutOfMemory(js::AllocFunction allocFunc,
arena_id_t arena, size_t nbytes,
void* reallocPtr = nullptr) {
return zone()->onOutOfMemory(allocFunc, arena, nbytes, reallocPtr);
}
void reportAllocationOverflow() const { zone()->reportAllocationOverflow(); }
void updateMallocCounter(size_t nbytes) {
zone()->incPolicyMemory(this, nbytes);
}
private:
ZoneAllocator* zone() const {
MOZ_ASSERT(zone_);
return zone_;
}
void decMemory(size_t nbytes) { zone_->decPolicyMemory(this, nbytes); }
};
// Functions for memory accounting on the zone.
// Associate malloc memory with a GC thing. This call should be matched by a
// following call to RemoveCellMemory with the same size and use. The total
// amount of malloc memory associated with a zone is used to trigger GC.
//
// You should use InitReservedSlot / InitObjectPrivate in preference to this
// where possible.
inline void AddCellMemory(gc::TenuredCell* cell, size_t nbytes, MemoryUse use) {
if (nbytes) {
ZoneAllocator::from(cell->zone())->addCellMemory(cell, nbytes, use);
}
}
inline void AddCellMemory(gc::Cell* cell, size_t nbytes, MemoryUse use) {
if (cell->isTenured()) {
AddCellMemory(&cell->asTenured(), nbytes, use);
}
}
// Remove association between malloc memory and a GC thing. This call should
// follow a call to AddCellMemory with the same size and use.
inline void RemoveCellMemory(gc::TenuredCell* cell, size_t nbytes,
MemoryUse use) {
if (nbytes) {
auto zoneBase = ZoneAllocator::from(cell->zoneFromAnyThread());
zoneBase->removeCellMemory(cell, nbytes, use);
}
}
inline void RemoveCellMemory(gc::Cell* cell, size_t nbytes, MemoryUse use) {
if (cell->isTenured()) {
RemoveCellMemory(&cell->asTenured(), nbytes, use);
}
}
// Initialize an object's reserved slot with a private value pointing to
// malloc-allocated memory and associate the memory with the object.
//
// This call should be matched with a call to FreeOp::free_/delete_ in the
// object's finalizer to free the memory and update the memory accounting.
inline void InitReservedSlot(NativeObject* obj, uint32_t slot, void* ptr,
size_t nbytes, MemoryUse use) {
AddCellMemory(obj, nbytes, use);
obj->initReservedSlot(slot, PrivateValue(ptr));
}
template <typename T>
inline void InitReservedSlot(NativeObject* obj, uint32_t slot, T* ptr,
MemoryUse use) {
InitReservedSlot(obj, slot, ptr, sizeof(T), use);
}
// Initialize an object's private slot with a pointer to malloc-allocated memory
// and associate the memory with the object.
//
// This call should be matched with a call to FreeOp::free_/delete_ in the
// object's finalizer to free the memory and update the memory accounting.
inline void InitObjectPrivate(NativeObject* obj, void* ptr, size_t nbytes,
MemoryUse use) {
AddCellMemory(obj, nbytes, use);
obj->initPrivate(ptr);
}
template <typename T>
inline void InitObjectPrivate(NativeObject* obj, T* ptr, MemoryUse use) {
InitObjectPrivate(obj, ptr, sizeof(T), use);
}
} // namespace js
#endif // gc_ZoneAllocator_h