1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * Public header for allocating memory associated with GC things.
11 #ifndef gc_ZoneAllocator_h
12 #define gc_ZoneAllocator_h
14 #include "mozilla/Maybe.h"
16 #include "jsfriendapi.h"
19 #include "gc/Scheduling.h"
21 #include "js/shadow/Zone.h" // JS::shadow::Zone
22 #include "vm/MallocProvider.h"
25 class JS_PUBLIC_API Zone
;
33 bool CurrentThreadIsGCFinalizing();
37 void MaybeMallocTriggerZoneGC(JSRuntime
* rt
, ZoneAllocator
* zoneAlloc
,
39 const HeapThreshold
& threshold
,
43 // Base class of JS::Zone that provides malloc memory allocation and accounting.
44 class ZoneAllocator
: public JS::shadow::Zone
,
45 public js::MallocProvider
<JS::Zone
> {
47 explicit ZoneAllocator(JSRuntime
* rt
, Kind kind
);
49 void fixupAfterMovingGC();
52 static ZoneAllocator
* from(JS::Zone
* zone
) {
53 // This is a safe upcast, but the compiler hasn't seen the definition yet.
54 return reinterpret_cast<ZoneAllocator
*>(zone
);
57 [[nodiscard
]] void* onOutOfMemory(js::AllocFunction allocFunc
,
58 arena_id_t arena
, size_t nbytes
,
59 void* reallocPtr
= nullptr);
60 void reportAllocationOverflow() const;
62 void updateSchedulingStateOnGCStart();
63 void updateGCStartThresholds(gc::GCRuntime
& gc
);
64 void setGCSliceThresholds(gc::GCRuntime
& gc
, bool waitingOnBGTask
);
65 void clearGCSliceThresholds();
67 // Memory accounting APIs for malloc memory owned by GC cells.
69 void addCellMemory(js::gc::Cell
* cell
, size_t nbytes
, js::MemoryUse use
) {
73 mallocHeapSize
.addBytes(nbytes
);
76 mallocTracker
.trackGCMemory(cell
, nbytes
, use
);
79 maybeTriggerGCOnMalloc();
82 void removeCellMemory(js::gc::Cell
* cell
, size_t nbytes
, js::MemoryUse use
,
83 bool updateRetainedSize
= false) {
86 MOZ_ASSERT_IF(CurrentThreadIsGCFinalizing(), updateRetainedSize
);
88 mallocHeapSize
.removeBytes(nbytes
, updateRetainedSize
);
91 mallocTracker
.untrackGCMemory(cell
, nbytes
, use
);
95 void swapCellMemory(js::gc::Cell
* a
, js::gc::Cell
* b
, js::MemoryUse use
) {
97 mallocTracker
.swapGCMemory(a
, b
, use
);
101 void registerNonGCMemory(void* mem
, MemoryUse use
) {
103 return mallocTracker
.registerNonGCMemory(mem
, use
);
106 void unregisterNonGCMemory(void* mem
, MemoryUse use
) {
108 return mallocTracker
.unregisterNonGCMemory(mem
, use
);
111 void moveOtherMemory(void* dst
, void* src
, MemoryUse use
) {
113 return mallocTracker
.moveNonGCMemory(dst
, src
, use
);
117 void incNonGCMemory(void* mem
, size_t nbytes
, MemoryUse use
) {
119 mallocHeapSize
.addBytes(nbytes
);
122 mallocTracker
.incNonGCMemory(mem
, nbytes
, use
);
125 maybeTriggerGCOnMalloc();
127 void decNonGCMemory(void* mem
, size_t nbytes
, MemoryUse use
,
128 bool updateRetainedSize
) {
131 mallocHeapSize
.removeBytes(nbytes
, updateRetainedSize
);
134 mallocTracker
.decNonGCMemory(mem
, nbytes
, use
);
138 // Account for allocations that may be referenced by more than one GC thing.
139 bool addSharedMemory(void* mem
, size_t nbytes
, MemoryUse use
);
140 void removeSharedMemory(void* mem
, size_t nbytes
, MemoryUse use
);
142 void incJitMemory(size_t nbytes
) {
144 jitHeapSize
.addBytes(nbytes
);
145 maybeTriggerZoneGC(jitHeapSize
, jitHeapThreshold
,
146 JS::GCReason::TOO_MUCH_JIT_CODE
);
148 void decJitMemory(size_t nbytes
) {
150 jitHeapSize
.removeBytes(nbytes
, true);
153 // Check malloc allocation threshold and trigger a zone GC if necessary.
154 void maybeTriggerGCOnMalloc() {
155 maybeTriggerZoneGC(mallocHeapSize
, mallocHeapThreshold
,
156 JS::GCReason::TOO_MUCH_MALLOC
);
160 void maybeTriggerZoneGC(const js::gc::HeapSize
& heap
,
161 const js::gc::HeapThreshold
& threshold
,
162 JS::GCReason reason
) {
163 if (heap
.bytes() >= threshold
.startBytes()) {
164 gc::MaybeMallocTriggerZoneGC(runtimeFromAnyThread(), this, heap
,
169 void updateCollectionRate(mozilla::TimeDuration mainThreadGCTime
,
170 size_t initialBytesForAllZones
);
172 void updateAllocationRate(mozilla::TimeDuration mutatorTime
);
175 // The size of allocated GC arenas in this zone.
176 gc::PerZoneGCHeapSize gcHeapSize
;
178 // Threshold used to trigger GC based on GC heap size.
179 gc::GCHeapThreshold gcHeapThreshold
;
181 // Amount of malloc data owned by tenured GC things in this zone, including
182 // external allocations supplied by JS::AddAssociatedMemory.
183 gc::HeapSize mallocHeapSize
;
185 // Threshold used to trigger GC based on malloc allocations.
186 gc::MallocHeapThreshold mallocHeapThreshold
;
188 // Amount of exectuable JIT code owned by GC things in this zone.
189 gc::HeapSize jitHeapSize
;
191 // Threshold used to trigger GC based on JIT allocations.
192 gc::JitHeapThreshold jitHeapThreshold
;
194 // Use counts for memory that can be referenced by more than one GC thing.
195 // Memory recorded here is also recorded in mallocHeapSize. This structure
196 // is used to avoid over-counting in mallocHeapSize.
197 gc::SharedMemoryMap sharedMemoryUseCounts
;
199 // Collection rate estimate for this zone in MB/s, and state used to calculate
200 // it. Updated every time this zone is collected.
201 MainThreadData
<mozilla::Maybe
<double>> smoothedCollectionRate
;
202 MainThreadOrGCTaskData
<mozilla::TimeDuration
> perZoneGCTime
;
204 // Allocation rate estimate in MB/s of mutator time, and state used to
206 MainThreadData
<mozilla::Maybe
<double>> smoothedAllocationRate
;
207 MainThreadData
<size_t> prevGCHeapSize
;
211 // In debug builds, malloc allocations can be tracked to make debugging easier
212 // (possible?) if allocation and free sizes don't balance.
213 gc::MemoryTracker mallocTracker
;
216 friend class gc::GCRuntime
;
219 // Whether memory is associated with a single cell or whether it is associated
220 // with the zone as a whole (for memory used by the system).
221 enum class TrackingKind
{ Cell
, Zone
};
224 * Allocation policy that performs memory tracking for malloced memory. This
225 * should be used for all containers associated with a GC thing or a zone.
227 * Since it doesn't hold a JSContext (those may not live long enough), it can't
228 * report out-of-memory conditions itself; the caller must check for OOM and
229 * take the appropriate action.
231 * FIXME bug 647103 - replace these *AllocPolicy names.
233 template <TrackingKind kind
>
234 class TrackedAllocPolicy
: public MallocProvider
<TrackedAllocPolicy
<kind
>> {
235 ZoneAllocator
* zone_
;
238 friend class js::gc::MemoryTracker
; // Can clear |zone_| on merge.
242 MOZ_IMPLICIT
TrackedAllocPolicy(ZoneAllocator
* z
) : zone_(z
) {
243 zone()->registerNonGCMemory(this, MemoryUse::TrackedAllocPolicy
);
245 MOZ_IMPLICIT
TrackedAllocPolicy(JS::Zone
* z
)
246 : TrackedAllocPolicy(ZoneAllocator::from(z
)) {}
247 TrackedAllocPolicy(TrackedAllocPolicy
& other
)
248 : TrackedAllocPolicy(other
.zone_
) {}
249 TrackedAllocPolicy(TrackedAllocPolicy
&& other
) : zone_(other
.zone_
) {
250 zone()->moveOtherMemory(this, &other
, MemoryUse::TrackedAllocPolicy
);
251 other
.zone_
= nullptr;
253 ~TrackedAllocPolicy() {
255 zone_
->unregisterNonGCMemory(this, MemoryUse::TrackedAllocPolicy
);
259 TrackedAllocPolicy
& operator=(const TrackedAllocPolicy
& other
) {
260 zone()->unregisterNonGCMemory(this, MemoryUse::TrackedAllocPolicy
);
261 zone_
= other
.zone();
262 zone()->registerNonGCMemory(this, MemoryUse::TrackedAllocPolicy
);
265 TrackedAllocPolicy
& operator=(TrackedAllocPolicy
&& other
) {
266 MOZ_ASSERT(this != &other
);
267 zone()->unregisterNonGCMemory(this, MemoryUse::TrackedAllocPolicy
);
268 zone_
= other
.zone();
269 zone()->moveOtherMemory(this, &other
, MemoryUse::TrackedAllocPolicy
);
270 other
.zone_
= nullptr;
274 // Public methods required to fulfill the AllocPolicy interface.
276 template <typename T
>
277 void free_(T
* p
, size_t numElems
) {
279 decMemory(numElems
* sizeof(T
));
284 [[nodiscard
]] bool checkSimulatedOOM() const {
285 return !js::oom::ShouldFailWithOOM();
288 void reportAllocOverflow() const { reportAllocationOverflow(); }
290 // Internal methods called by the MallocProvider implementation.
292 [[nodiscard
]] void* onOutOfMemory(js::AllocFunction allocFunc
,
293 arena_id_t arena
, size_t nbytes
,
294 void* reallocPtr
= nullptr) {
295 return zone()->onOutOfMemory(allocFunc
, arena
, nbytes
, reallocPtr
);
297 void reportAllocationOverflow() const { zone()->reportAllocationOverflow(); }
298 void updateMallocCounter(size_t nbytes
) {
299 zone()->incNonGCMemory(this, nbytes
, MemoryUse::TrackedAllocPolicy
);
303 ZoneAllocator
* zone() const {
307 void decMemory(size_t nbytes
);
310 using ZoneAllocPolicy
= TrackedAllocPolicy
<TrackingKind::Zone
>;
311 using CellAllocPolicy
= TrackedAllocPolicy
<TrackingKind::Cell
>;
313 // Functions for memory accounting on the zone.
315 // Associate malloc memory with a GC thing. This call should be matched by a
316 // following call to RemoveCellMemory with the same size and use. The total
317 // amount of malloc memory associated with a zone is used to trigger GC.
319 // You should use InitReservedSlot / InitObjectPrivate in preference to this
322 inline void AddCellMemory(gc::TenuredCell
* cell
, size_t nbytes
, MemoryUse use
) {
324 ZoneAllocator::from(cell
->zone())->addCellMemory(cell
, nbytes
, use
);
327 inline void AddCellMemory(gc::Cell
* cell
, size_t nbytes
, MemoryUse use
) {
328 if (cell
->isTenured()) {
329 AddCellMemory(&cell
->asTenured(), nbytes
, use
);
333 // Remove association between malloc memory and a GC thing. This call should
334 // follow a call to AddCellMemory with the same size and use.
336 inline void RemoveCellMemory(gc::TenuredCell
* cell
, size_t nbytes
,
338 MOZ_ASSERT(!CurrentThreadIsGCFinalizing(),
339 "Use GCContext methods to remove associated memory in finalizers");
342 auto zoneBase
= ZoneAllocator::from(cell
->zoneFromAnyThread());
343 zoneBase
->removeCellMemory(cell
, nbytes
, use
, false);
346 inline void RemoveCellMemory(gc::Cell
* cell
, size_t nbytes
, MemoryUse use
) {
347 if (cell
->isTenured()) {
348 RemoveCellMemory(&cell
->asTenured(), nbytes
, use
);
354 #endif // gc_ZoneAllocator_h