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/. */
6
7 /*
8 * [SMDOC] GC Scheduling
9 *
10 * GC Scheduling Overview
11 * ======================
12 *
13 * Scheduling GC's in SpiderMonkey/Firefox is tremendously complicated because
14 * of the large number of subtle, cross-cutting, and widely dispersed factors
15 * that must be taken into account. A summary of some of the more important
16 * factors follows.
17 *
18 * Cost factors:
19 *
20 * * GC too soon and we'll revisit an object graph almost identical to the
21 * one we just visited; since we are unlikely to find new garbage, the
22 * traversal will be largely overhead. We rely heavily on external factors
23 * to signal us that we are likely to find lots of garbage: e.g. "a tab
24 * just got closed".
25 *
26 * * GC too late and we'll run out of memory to allocate (e.g. Out-Of-Memory,
27 * hereafter simply abbreviated to OOM). If this happens inside
28 * SpiderMonkey we may be able to recover, but most embedder allocations
29 * will simply crash on OOM, even if the GC has plenty of free memory it
30 * could surrender.
31 *
32 * * Memory fragmentation: if we fill the process with GC allocations, a
33 * request for a large block of contiguous memory may fail because no
34 * contiguous block is free, despite having enough memory available to
35 * service the request.
36 *
37 * * Management overhead: if our GC heap becomes large, we create extra
38 * overhead when managing the GC's structures, even if the allocations are
39 * mostly unused.
40 *
41 * Heap Management Factors:
42 *
43 * * GC memory: The GC has its own allocator that it uses to make fixed size
44 * allocations for GC managed things. In cases where the GC thing requires
45 * larger or variable sized memory to implement itself, it is responsible
46 * for using the system heap.
47 *
48 * * C Heap Memory: Rather than allowing for large or variable allocations,
49 * the SpiderMonkey GC allows GC things to hold pointers to C heap memory.
50 * It is the responsibility of the thing to free this memory with a custom
51 * finalizer (with the sole exception of NativeObject, which knows about
52 * slots and elements for performance reasons). C heap memory has different
53 * performance and overhead tradeoffs than GC internal memory, which need
54 * to be considered with scheduling a GC.
55 *
56 * Application Factors:
57 *
58 * * Most applications allocate heavily at startup, then enter a processing
59 * stage where memory utilization remains roughly fixed with a slower
60 * allocation rate. This is not always the case, however, so while we may
61 * optimize for this pattern, we must be able to handle arbitrary
62 * allocation patterns.
63 *
64 * Other factors:
65 *
66 * * Other memory: This is memory allocated outside the purview of the GC.
67 * Data mapped by the system for code libraries, data allocated by those
68 * libraries, data in the JSRuntime that is used to manage the engine,
69 * memory used by the embedding that is not attached to a GC thing, memory
70 * used by unrelated processes running on the hardware that use space we
71 * could otherwise use for allocation, etc. While we don't have to manage
72 * it, we do have to take it into account when scheduling since it affects
73 * when we will OOM.
74 *
75 * * Physical Reality: All real machines have limits on the number of bits
76 * that they are physically able to store. While modern operating systems
77 * can generally make additional space available with swapping, at some
78 * point there are simply no more bits to allocate. There is also the
79 * factor of address space limitations, particularly on 32bit machines.
80 *
81 * * Platform Factors: Each OS makes use of wildly different memory
82 * management techniques. These differences result in different performance
83 * tradeoffs, different fragmentation patterns, and different hard limits
84 * on the amount of physical and/or virtual memory that we can use before
85 * OOMing.
86 *
87 *
88 * Reasons for scheduling GC
89 * -------------------------
90 *
91 * While code generally takes the above factors into account in only an ad-hoc
92 * fashion, the API forces the user to pick a "reason" for the GC. We have a
93 * bunch of JS::GCReason reasons in GCAPI.h. These fall into a few categories
94 * that generally coincide with one or more of the above factors.
95 *
96 * Embedding reasons:
97 *
98 * 1) Do a GC now because the embedding knows something useful about the
99 * zone's memory retention state. These are GCReasons like LOAD_END,
100 * PAGE_HIDE, SET_NEW_DOCUMENT, DOM_UTILS. Mostly, Gecko uses these to
101 * indicate that a significant fraction of the scheduled zone's memory is
102 * probably reclaimable.
103 *
104 * 2) Do some known amount of GC work now because the embedding knows now is
105 * a good time to do a long, unblockable operation of a known duration.
106 * These are INTER_SLICE_GC and REFRESH_FRAME.
107 *
108 * Correctness reasons:
109 *
110 * 3) Do a GC now because correctness depends on some GC property. For
111 * example, CC_FORCED is where the embedding requires the mark bits to be
112 * set correctly. Also, EVICT_NURSERY where we need to work on the tenured
113 * heap.
114 *
115 * 4) Do a GC because we are shutting down: e.g. SHUTDOWN_CC or DESTROY_*.
116 *
117 * 5) Do a GC because a compartment was accessed between GC slices when we
118 * would have otherwise discarded it. We have to do a second GC to clean
119 * it up: e.g. COMPARTMENT_REVIVED.
120 *
121 * Emergency Reasons:
122 *
123 * 6) Do an all-zones, non-incremental GC now because the embedding knows it
124 * cannot wait: e.g. MEM_PRESSURE.
125 *
126 * 7) OOM when fetching a new Chunk results in a LAST_DITCH GC.
127 *
128 * Heap Size Limitation Reasons:
129 *
130 * 8) Do an incremental, zonal GC with reason MAYBEGC when we discover that
131 * the gc's allocated size is approaching the current trigger. This is
132 * called MAYBEGC because we make this check in the MaybeGC function.
133 * MaybeGC gets called at the top of the main event loop. Normally, it is
134 * expected that this callback will keep the heap size limited. It is
135 * relatively inexpensive, because it is invoked with no JS running and
136 * thus few stack roots to scan. For this reason, the GC's "trigger" bytes
137 * is less than the GC's "max" bytes as used by the trigger below.
138 *
139 * 9) Do an incremental, zonal GC with reason MAYBEGC when we go to allocate
140 * a new GC thing and find that the GC heap size has grown beyond the
141 * configured maximum (JSGC_MAX_BYTES). We trigger this GC by returning
142 * nullptr and then calling maybeGC at the top level of the allocator.
143 * This is then guaranteed to fail the "size greater than trigger" check
144 * above, since trigger is always less than max. After performing the GC,
145 * the allocator unconditionally returns nullptr to force an OOM exception
146 * is raised by the script.
147 *
148 * Note that this differs from a LAST_DITCH GC where we actually run out
149 * of memory (i.e., a call to a system allocator fails) when trying to
150 * allocate. Unlike above, LAST_DITCH GC only happens when we are really
151 * out of memory, not just when we cross an arbitrary trigger; despite
152 * this, it may still return an allocation at the end and allow the script
153 * to continue, if the LAST_DITCH GC was able to free up enough memory.
154 *
155 * 10) Do a GC under reason ALLOC_TRIGGER when we are over the GC heap trigger
156 * limit, but in the allocator rather than in a random call to maybeGC.
157 * This occurs if we allocate too much before returning to the event loop
158 * and calling maybeGC; this is extremely common in benchmarks and
159 * long-running Worker computations. Note that this uses a wildly
160 * different mechanism from the above in that it sets the interrupt flag
161 * and does the GC at the next loop head, before the next alloc, or
162 * maybeGC. The reason for this is that this check is made after the
163 * allocation and we cannot GC with an uninitialized thing in the heap.
164 *
165 * 11) Do an incremental, zonal GC with reason TOO_MUCH_MALLOC when the total
166 * amount of malloced memory is greater than the malloc trigger limit for the
167 * zone.
168 *
169 *
170 * Size Limitation Triggers Explanation
171 * ------------------------------------
172 *
173 * The GC internally is entirely unaware of the context of the execution of
174 * the mutator. It sees only:
175 *
176 * A) Allocated size: this is the amount of memory currently requested by the
177 * mutator. This quantity is monotonically increasing: i.e. the allocation
178 * rate is always >= 0. It is also easy for the system to track.
179 *
180 * B) Retained size: this is the amount of memory that the mutator can
181 * currently reach. Said another way, it is the size of the heap
182 * immediately after a GC (modulo background sweeping). This size is very
183 * costly to know exactly and also extremely hard to estimate with any
184 * fidelity.
185 *
186 * For reference, a common allocated vs. retained graph might look like:
187 *
188 * | ** **
189 * | ** ** * **
190 * | ** * ** * **
191 * | * ** * ** * **
192 * | ** ** * ** * **
193 * s| * * ** ** + + **
194 * i| * * * + + + + +
195 * z| * * * + + + + +
196 * e| * **+
197 * | * +
198 * | * +
199 * | * +
200 * | * +
201 * | * +
202 * |*+
203 * +--------------------------------------------------
204 * time
205 * *** = allocated
206 * +++ = retained
207 *
208 * Note that this is a bit of a simplification
209 * because in reality we track malloc and GC heap
210 * sizes separately and have a different level of
211 * granularity and accuracy on each heap.
212 *
213 * This presents some obvious implications for Mark-and-Sweep collectors.
214 * Namely:
215 * -> t[marking] ~= size[retained]
216 * -> t[sweeping] ~= size[allocated] - size[retained]
217 *
218 * In a non-incremental collector, maintaining low latency and high
219 * responsiveness requires that total GC times be as low as possible. Thus,
220 * in order to stay responsive when we did not have a fully incremental
221 * collector, our GC triggers were focused on minimizing collection time.
222 * Furthermore, since size[retained] is not under control of the GC, all the
223 * GC could do to control collection times was reduce sweep times by
224 * minimizing size[allocated], per the equation above.
225 *
226 * The result of the above is GC triggers that focus on size[allocated] to
227 * the exclusion of other important factors and default heuristics that are
228 * not optimal for a fully incremental collector. On the other hand, this is
229 * not all bad: minimizing size[allocated] also minimizes the chance of OOM
230 * and sweeping remains one of the hardest areas to further incrementalize.
231 *
232 * EAGER_ALLOC_TRIGGER
233 * -------------------
234 * Occurs when we return to the event loop and find our heap is getting
235 * largish, but before t[marking] OR t[sweeping] is too large for a
236 * responsive non-incremental GC. This is intended to be the common case
237 * in normal web applications: e.g. we just finished an event handler and
238 * the few objects we allocated when computing the new whatzitz have
239 * pushed us slightly over the limit. After this GC we rescale the new
240 * EAGER_ALLOC_TRIGGER trigger to 150% of size[retained] so that our
241 * non-incremental GC times will always be proportional to this size
242 * rather than being dominated by sweeping.
243 *
244 * As a concession to mutators that allocate heavily during their startup
245 * phase, we have a highFrequencyGCMode that ups the growth rate to 300%
246 * of the current size[retained] so that we'll do fewer longer GCs at the
247 * end of the mutator startup rather than more, smaller GCs.
248 *
249 * Assumptions:
250 * -> Responsiveness is proportional to t[marking] + t[sweeping].
251 * -> size[retained] is proportional only to GC allocations.
252 *
253 * ALLOC_TRIGGER (non-incremental)
254 * -------------------------------
255 * If we do not return to the event loop before getting all the way to our
256 * gc trigger bytes then MAYBEGC will never fire. To avoid OOMing, we
257 * succeed the current allocation and set the script interrupt so that we
258 * will (hopefully) do a GC before we overflow our max and have to raise
259 * an OOM exception for the script.
260 *
261 * Assumptions:
262 * -> Common web scripts will return to the event loop before using
263 * 10% of the current triggerBytes worth of GC memory.
264 *
265 * ALLOC_TRIGGER (incremental)
266 * ---------------------------
267 * In practice the above trigger is rough: if a website is just on the
268 * cusp, sometimes it will trigger a non-incremental GC moments before
269 * returning to the event loop, where it could have done an incremental
270 * GC. Thus, we recently added an incremental version of the above with a
271 * substantially lower threshold, so that we have a soft limit here. If
272 * IGC can collect faster than the allocator generates garbage, even if
273 * the allocator does not return to the event loop frequently, we should
274 * not have to fall back to a non-incremental GC.
275 *
276 * INCREMENTAL_TOO_SLOW
277 * --------------------
278 * Do a full, non-incremental GC if we overflow ALLOC_TRIGGER during an
279 * incremental GC. When in the middle of an incremental GC, we suppress
280 * our other triggers, so we need a way to backstop the IGC if the
281 * mutator allocates faster than the IGC can clean things up.
282 *
283 * TOO_MUCH_MALLOC
284 * ---------------
285 * Performs a GC before size[allocated] - size[retained] gets too large
286 * for non-incremental sweeping to be fast in the case that we have
287 * significantly more malloc allocation than GC allocation. This is meant
288 * to complement MAYBEGC triggers. We track this by counting malloced
289 * bytes; the counter gets reset at every GC since we do not always have a
290 * size at the time we call free. Because of this, the malloc heuristic
291 * is, unfortunately, not usefully able to augment our other GC heap
292 * triggers and is limited to this singular heuristic.
293 *
294 * Assumptions:
295 * -> EITHER size[allocated_by_malloc] ~= size[allocated_by_GC]
296 * OR time[sweeping] ~= size[allocated_by_malloc]
297 * -> size[retained] @ t0 ~= size[retained] @ t1
298 * i.e. That the mutator is in steady-state operation.
299 *
300 * LAST_DITCH_GC
301 * -------------
302 * Does a GC because we are out of memory.
303 *
304 * Assumptions:
305 * -> size[retained] < size[available_memory]
306 */
307
308 #ifndef gc_Scheduling_h
309 #define gc_Scheduling_h
310
311 #include "mozilla/Atomics.h"
312 #include "mozilla/DebugOnly.h"
313
314 #include "gc/GCEnum.h"
315 #include "js/AllocPolicy.h"
316 #include "js/GCAPI.h"
317 #include "js/HashTable.h"
318 #include "js/HeapAPI.h"
319 #include "js/SliceBudget.h"
320 #include "threading/ProtectedData.h"
321 #include "util/DifferentialTesting.h"
322
323 namespace js {
324
325 class AutoLockGC;
326 class ZoneAllocator;
327 class ZoneAllocPolicy;
328
329 namespace gc {
330
331 struct Cell;
332
333 /*
334 * Default settings for tuning the GC. Some of these can be set at runtime,
335 * This list is not complete, some tuning parameters are not listed here.
336 *
337 * If you change the values here, please also consider changing them in
338 * modules/libpref/init/all.js where they are duplicated for the Firefox
339 * preferences.
340 */
341 namespace TuningDefaults {
342
343 /* JSGC_ALLOCATION_THRESHOLD */
344 static const size_t GCZoneAllocThresholdBase = 27 * 1024 * 1024;
345
346 /*
347 * JSGC_MIN_NURSERY_BYTES
348 *
349 * With some testing (Bug 1532838) we increased this to 256K from 192K
350 * which improves performance. We should try to reduce this for background
351 * tabs.
352 */
353 static const size_t GCMinNurseryBytes = 256 * 1024;
354
355 /*
356 * JSGC_SMALL_HEAP_INCREMENTAL_LIMIT
357 *
358 * This must be greater than 1.3 to maintain performance on splay-latency.
359 */
360 static const double SmallHeapIncrementalLimit = 1.40;
361
362 /* JSGC_LARGE_HEAP_INCREMENTAL_LIMIT */
363 static const double LargeHeapIncrementalLimit = 1.10;
364
365 /* JSGC_ZONE_ALLOC_DELAY_KB */
366 static const size_t ZoneAllocDelayBytes = 1024 * 1024;
367
368 /* JSGC_HIGH_FREQUENCY_TIME_LIMIT */
369 static const auto HighFrequencyThreshold = 1; // in seconds
370
371 /* JSGC_SMALL_HEAP_SIZE_MAX */
372 static const size_t SmallHeapSizeMaxBytes = 100 * 1024 * 1024;
373
374 /* JSGC_LARGE_HEAP_SIZE_MIN */
375 static const size_t LargeHeapSizeMinBytes = 500 * 1024 * 1024;
376
377 /* JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH */
378 static const double HighFrequencySmallHeapGrowth = 3.0;
379
380 /* JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH */
381 static const double HighFrequencyLargeHeapGrowth = 1.5;
382
383 /* JSGC_LOW_FREQUENCY_HEAP_GROWTH */
384 static const double LowFrequencyHeapGrowth = 1.5;
385
386 /* JSGC_MIN_EMPTY_CHUNK_COUNT */
387 static const uint32_t MinEmptyChunkCount = 1;
388
389 /* JSGC_MAX_EMPTY_CHUNK_COUNT */
390 static const uint32_t MaxEmptyChunkCount = 30;
391
392 /* JSGC_SLICE_TIME_BUDGET_MS */
393 static const int64_t DefaultTimeBudgetMS = 0; // Unlimited by default.
394
395 /* JSGC_INCREMENTAL_ENABLED */
396 static const bool IncrementalGCEnabled = false;
397
398 /* JSGC_PER_ZONE_GC_ENABLED */
399 static const bool PerZoneGCEnabled = false;
400
401 /* JSGC_COMPACTING_ENABLED */
402 static const bool CompactingEnabled = true;
403
404 /* JSGC_INCREMENTAL_WEAKMAP_ENABLED */
405 static const bool IncrementalWeakMapMarkingEnabled = true;
406
407 /* JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION */
408 static const uint32_t NurseryFreeThresholdForIdleCollection = ChunkSize / 4;
409
410 /* JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_PERCENT */
411 static const double NurseryFreeThresholdForIdleCollectionFraction = 0.25;
412
413 /* JSGC_NURSERY_TIMEOUT_FOR_IDLE_COLLECTION_MS */
414 static const uint32_t NurseryTimeoutForIdleCollectionMS = 5000;
415
416 /* JSGC_PRETENURE_THRESHOLD */
417 static const double PretenureThreshold = 0.6;
418
419 /* JSGC_PRETENURE_GROUP_THRESHOLD */
420 static const double PretenureGroupThreshold = 3000;
421
422 /* JSGC_PRETENURE_STRING_THRESHOLD */
423 static const double PretenureStringThreshold = 0.55;
424
425 /* JSGC_STOP_PRETENURE_STRING_THRESHOLD */
426 static const double StopPretenureStringThreshold = 0.9;
427
428 /* JSGC_MIN_LAST_DITCH_GC_PERIOD */
429 static const auto MinLastDitchGCPeriod = 60; // in seconds
430
431 /* JSGC_MALLOC_THRESHOLD_BASE */
432 static const size_t MallocThresholdBase = 38 * 1024 * 1024;
433
434 /* JSGC_MALLOC_GROWTH_FACTOR */
435 static const double MallocGrowthFactor = 1.5;
436
437 /* JSGC_HELPER_THREAD_RATIO */
438 static const double HelperThreadRatio = 0.5;
439
440 /* JSGC_MAX_HELPER_THREADS */
441 static const size_t MaxHelperThreads = 8;
442
443 } // namespace TuningDefaults
444
445 /*
446 * Encapsulates all of the GC tunables. These are effectively constant and
447 * should only be modified by setParameter.
448 */
449 class GCSchedulingTunables {
450 /*
451 * JSGC_MAX_BYTES
452 *
453 * Maximum nominal heap before last ditch GC.
454 */
455 UnprotectedData<size_t> gcMaxBytes_;
456
457 /*
458 * JSGC_MIN_NURSERY_BYTES
459 * JSGC_MAX_NURSERY_BYTES
460 *
461 * Minimum and maximum nursery size for each runtime.
462 */
463 MainThreadData<size_t> gcMinNurseryBytes_;
464 MainThreadData<size_t> gcMaxNurseryBytes_;
465
466 /*
467 * JSGC_ALLOCATION_THRESHOLD
468 *
469 * The base value used to compute zone->threshold.bytes(). When
470 * gcHeapSize.bytes() exceeds threshold.bytes() for a zone, the zone may be
471 * scheduled for a GC, depending on the exact circumstances.
472 */
473 MainThreadOrGCTaskData<size_t> gcZoneAllocThresholdBase_;
474
475 /*
476 * JSGC_SMALL_HEAP_INCREMENTAL_LIMIT
477 *
478 * Multiple of threshold.bytes() which triggers a non-incremental GC.
479 */
480 UnprotectedData<double> smallHeapIncrementalLimit_;
481
482 /*
483 * JSGC_LARGE_HEAP_INCREMENTAL_LIMIT
484 *
485 * Multiple of threshold.bytes() which triggers a non-incremental GC.
486 */
487 UnprotectedData<double> largeHeapIncrementalLimit_;
488
489 /*
490 * Number of bytes to allocate between incremental slices in GCs triggered by
491 * the zone allocation threshold.
492 *
493 * This value does not have a JSGCParamKey parameter yet.
494 */
495 UnprotectedData<size_t> zoneAllocDelayBytes_;
496
497 /*
498 * JSGC_HIGH_FREQUENCY_TIME_LIMIT
499 *
500 * We enter high-frequency mode if we GC a twice within this many
501 * microseconds.
502 */
503 MainThreadOrGCTaskData<mozilla::TimeDuration> highFrequencyThreshold_;
504
505 /*
506 * JSGC_SMALL_HEAP_SIZE_MAX
507 * JSGC_LARGE_HEAP_SIZE_MIN
508 * JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH
509 * JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH
510 *
511 * When in the |highFrequencyGC| mode, these parameterize the per-zone
512 * "HeapGrowthFactor" computation.
513 */
514 MainThreadOrGCTaskData<size_t> smallHeapSizeMaxBytes_;
515 MainThreadOrGCTaskData<size_t> largeHeapSizeMinBytes_;
516 MainThreadOrGCTaskData<double> highFrequencySmallHeapGrowth_;
517 MainThreadOrGCTaskData<double> highFrequencyLargeHeapGrowth_;
518
519 /*
520 * JSGC_LOW_FREQUENCY_HEAP_GROWTH
521 *
522 * When not in |highFrequencyGC| mode, this is the global (stored per-zone)
523 * "HeapGrowthFactor".
524 */
525 MainThreadOrGCTaskData<double> lowFrequencyHeapGrowth_;
526
527 /*
528 * JSGC_MIN_EMPTY_CHUNK_COUNT
529 * JSGC_MAX_EMPTY_CHUNK_COUNT
530 *
531 * Controls the number of empty chunks reserved for future allocation.
532 */
533 UnprotectedData<uint32_t> minEmptyChunkCount_;
534 UnprotectedData<uint32_t> maxEmptyChunkCount_;
535
536 /*
537 * JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION
538 * JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_FRACTION
539 *
540 * Attempt to run a minor GC in the idle time if the free space falls
541 * below this threshold. The absolute threshold is used when the nursery is
542 * large and the percentage when it is small. See Nursery::shouldCollect()
543 */
544 UnprotectedData<uint32_t> nurseryFreeThresholdForIdleCollection_;
545 UnprotectedData<double> nurseryFreeThresholdForIdleCollectionFraction_;
546
547 /* See JSGC_NURSERY_TIMEOUT_FOR_IDLE_COLLECTION_MS. */
548 MainThreadData<mozilla::TimeDuration> nurseryTimeoutForIdleCollection_;
549
550 /*
551 * JSGC_PRETENURE_THRESHOLD
552 *
553 * Fraction of objects tenured to trigger pretenuring (between 0 and 1). If
554 * this fraction is met, the GC proceeds to calculate which objects will be
555 * tenured. If this is 1.0f (actually if it is not < 1.0f) then pretenuring
556 * is disabled.
557 */
558 UnprotectedData<double> pretenureThreshold_;
559
560 /*
561 * JSGC_PRETENURE_GROUP_THRESHOLD
562 *
563 * During a single nursery collection, if this many objects from the same
564 * object group are tenured, then that group will be pretenured.
565 */
566 UnprotectedData<uint32_t> pretenureGroupThreshold_;
567
568 /*
569 * JSGC_PRETENURE_STRING_THRESHOLD
570 *
571 * If the percentage of the tenured strings exceeds this threshold, string
572 * will be allocated in tenured heap instead. (Default is allocated in
573 * nursery.)
574 */
575 MainThreadData<double> pretenureStringThreshold_;
576
577 /*
578 * JSGC_STOP_PRETENURE_STRING_THRESHOLD
579 *
580 * If the finalization rate of the tenured strings exceeds this threshold,
581 * string will be allocated in nursery.
582 */
583 MainThreadData<double> stopPretenureStringThreshold_;
584
585 /*
586 * JSGC_MIN_LAST_DITCH_GC_PERIOD
587 *
588 * Last ditch GC is skipped if allocation failure occurs less than this many
589 * seconds from the previous one.
590 */
591 MainThreadData<mozilla::TimeDuration> minLastDitchGCPeriod_;
592
593 /*
594 * JSGC_MALLOC_THRESHOLD_BASE
595 *
596 * The base value used to compute the GC trigger for malloc allocated memory.
597 */
598 MainThreadOrGCTaskData<size_t> mallocThresholdBase_;
599
600 /*
601 * JSGC_MALLOC_GROWTH_FACTOR
602 *
603 * Malloc memory growth factor.
604 */
605 MainThreadOrGCTaskData<double> mallocGrowthFactor_;
606
607 public:
608 GCSchedulingTunables();
609
gcMaxBytes()610 size_t gcMaxBytes() const { return gcMaxBytes_; }
gcMinNurseryBytes()611 size_t gcMinNurseryBytes() const { return gcMinNurseryBytes_; }
gcMaxNurseryBytes()612 size_t gcMaxNurseryBytes() const { return gcMaxNurseryBytes_; }
gcZoneAllocThresholdBase()613 size_t gcZoneAllocThresholdBase() const { return gcZoneAllocThresholdBase_; }
smallHeapIncrementalLimit()614 double smallHeapIncrementalLimit() const {
615 return smallHeapIncrementalLimit_;
616 }
largeHeapIncrementalLimit()617 double largeHeapIncrementalLimit() const {
618 return largeHeapIncrementalLimit_;
619 }
zoneAllocDelayBytes()620 size_t zoneAllocDelayBytes() const { return zoneAllocDelayBytes_; }
highFrequencyThreshold()621 const mozilla::TimeDuration& highFrequencyThreshold() const {
622 return highFrequencyThreshold_;
623 }
smallHeapSizeMaxBytes()624 size_t smallHeapSizeMaxBytes() const { return smallHeapSizeMaxBytes_; }
largeHeapSizeMinBytes()625 size_t largeHeapSizeMinBytes() const { return largeHeapSizeMinBytes_; }
highFrequencySmallHeapGrowth()626 double highFrequencySmallHeapGrowth() const {
627 return highFrequencySmallHeapGrowth_;
628 }
highFrequencyLargeHeapGrowth()629 double highFrequencyLargeHeapGrowth() const {
630 return highFrequencyLargeHeapGrowth_;
631 }
lowFrequencyHeapGrowth()632 double lowFrequencyHeapGrowth() const { return lowFrequencyHeapGrowth_; }
minEmptyChunkCount(const AutoLockGC &)633 unsigned minEmptyChunkCount(const AutoLockGC&) const {
634 return minEmptyChunkCount_;
635 }
maxEmptyChunkCount()636 unsigned maxEmptyChunkCount() const { return maxEmptyChunkCount_; }
nurseryFreeThresholdForIdleCollection()637 uint32_t nurseryFreeThresholdForIdleCollection() const {
638 return nurseryFreeThresholdForIdleCollection_;
639 }
nurseryFreeThresholdForIdleCollectionFraction()640 double nurseryFreeThresholdForIdleCollectionFraction() const {
641 return nurseryFreeThresholdForIdleCollectionFraction_;
642 }
nurseryTimeoutForIdleCollection()643 mozilla::TimeDuration nurseryTimeoutForIdleCollection() const {
644 return nurseryTimeoutForIdleCollection_;
645 }
646
attemptPretenuring()647 bool attemptPretenuring() const { return pretenureThreshold_ < 1.0; }
pretenureThreshold()648 double pretenureThreshold() const { return pretenureThreshold_; }
pretenureGroupThreshold()649 uint32_t pretenureGroupThreshold() const { return pretenureGroupThreshold_; }
pretenureStringThreshold()650 double pretenureStringThreshold() const { return pretenureStringThreshold_; }
stopPretenureStringThreshold()651 double stopPretenureStringThreshold() const {
652 return stopPretenureStringThreshold_;
653 }
654
minLastDitchGCPeriod()655 mozilla::TimeDuration minLastDitchGCPeriod() const {
656 return minLastDitchGCPeriod_;
657 }
658
mallocThresholdBase()659 size_t mallocThresholdBase() const { return mallocThresholdBase_; }
mallocGrowthFactor()660 double mallocGrowthFactor() const { return mallocGrowthFactor_; }
661
662 [[nodiscard]] bool setParameter(JSGCParamKey key, uint32_t value,
663 const AutoLockGC& lock);
664 void resetParameter(JSGCParamKey key, const AutoLockGC& lock);
665
666 private:
667 void setSmallHeapSizeMaxBytes(size_t value);
668 void setLargeHeapSizeMinBytes(size_t value);
669 void setHighFrequencySmallHeapGrowth(double value);
670 void setHighFrequencyLargeHeapGrowth(double value);
671 void setLowFrequencyHeapGrowth(double value);
672 void setMinEmptyChunkCount(uint32_t value);
673 void setMaxEmptyChunkCount(uint32_t value);
674 };
675
676 class GCSchedulingState {
677 /*
678 * Influences how we schedule and run GC's in several subtle ways. The most
679 * important factor is in how it controls the "HeapGrowthFactor". The
680 * growth factor is a measure of how large (as a percentage of the last GC)
681 * the heap is allowed to grow before we try to schedule another GC.
682 */
683 MainThreadOrGCTaskData<bool> inHighFrequencyGCMode_;
684
685 public:
686 /*
687 * Influences the GC thresholds for the atoms zone to discourage collection of
688 * this zone during page load.
689 */
690 MainThreadOrGCTaskData<bool> inPageLoad;
691
GCSchedulingState()692 GCSchedulingState() : inHighFrequencyGCMode_(false) {}
693
inHighFrequencyGCMode()694 bool inHighFrequencyGCMode() const { return inHighFrequencyGCMode_; }
695
updateHighFrequencyMode(const mozilla::TimeStamp & lastGCTime,const mozilla::TimeStamp & currentTime,const GCSchedulingTunables & tunables)696 void updateHighFrequencyMode(const mozilla::TimeStamp& lastGCTime,
697 const mozilla::TimeStamp& currentTime,
698 const GCSchedulingTunables& tunables) {
699 if (js::SupportDifferentialTesting()) {
700 return;
701 }
702
703 inHighFrequencyGCMode_ =
704 !lastGCTime.IsNull() &&
705 lastGCTime + tunables.highFrequencyThreshold() > currentTime;
706 }
707 };
708
709 struct TriggerResult {
710 bool shouldTrigger;
711 size_t usedBytes;
712 size_t thresholdBytes;
713 };
714
715 using AtomicByteCount = mozilla::Atomic<size_t, mozilla::ReleaseAcquire>;
716
717 /*
718 * Tracks the size of allocated data. This is used for both GC and malloc data.
719 * It automatically maintains the memory usage relationship between parent and
720 * child instances, i.e. between those in a GCRuntime and its Zones.
721 */
722 class HeapSize {
723 /*
724 * An instance that contains our parent's heap usage, or null if this is the
725 * top-level usage container.
726 */
727 HeapSize* const parent_;
728
729 /*
730 * The number of bytes in use. For GC heaps this is approximate to the nearest
731 * ArenaSize. It is atomic because it is updated by both the active and GC
732 * helper threads.
733 */
734 AtomicByteCount bytes_;
735
736 /*
737 * The number of bytes retained after the last collection. This is updated
738 * dynamically during incremental GC. It does not include allocations that
739 * happen during a GC.
740 */
741 AtomicByteCount retainedBytes_;
742
743 public:
HeapSize(HeapSize * parent)744 explicit HeapSize(HeapSize* parent) : parent_(parent), bytes_(0) {}
745
bytes()746 size_t bytes() const { return bytes_; }
retainedBytes()747 size_t retainedBytes() const { return retainedBytes_; }
748
updateOnGCStart()749 void updateOnGCStart() { retainedBytes_ = size_t(bytes_); }
750
addGCArena()751 void addGCArena() { addBytes(ArenaSize); }
removeGCArena()752 void removeGCArena() {
753 MOZ_ASSERT(retainedBytes_ >= ArenaSize);
754 removeBytes(ArenaSize, true /* only sweeping removes arenas */);
755 }
756
addBytes(size_t nbytes)757 void addBytes(size_t nbytes) {
758 mozilla::DebugOnly<size_t> initialBytes(bytes_);
759 MOZ_ASSERT(initialBytes + nbytes > initialBytes);
760 bytes_ += nbytes;
761 if (parent_) {
762 parent_->addBytes(nbytes);
763 }
764 }
removeBytes(size_t nbytes,bool wasSwept)765 void removeBytes(size_t nbytes, bool wasSwept) {
766 if (wasSwept) {
767 // TODO: We would like to assert that retainedBytes_ >= nbytes is here but
768 // we can't do that yet, so clamp the result to zero.
769 retainedBytes_ = nbytes <= retainedBytes_ ? retainedBytes_ - nbytes : 0;
770 }
771 MOZ_ASSERT(bytes_ >= nbytes);
772 bytes_ -= nbytes;
773 if (parent_) {
774 parent_->removeBytes(nbytes, wasSwept);
775 }
776 }
777
778 /* Pair to adoptArenas. Adopts the attendant usage statistics. */
adopt(HeapSize & source)779 void adopt(HeapSize& source) {
780 // Skip retainedBytes_: we never adopt zones that are currently being
781 // collected.
782 bytes_ += source.bytes_;
783 source.retainedBytes_ = 0;
784 source.bytes_ = 0;
785 }
786 };
787
788 // Heap size thresholds used to trigger GC. This is an abstract base class for
789 // GC heap and malloc thresholds defined below.
790 class HeapThreshold {
791 protected:
HeapThreshold()792 HeapThreshold()
793 : startBytes_(SIZE_MAX),
794 incrementalLimitBytes_(SIZE_MAX),
795 sliceBytes_(SIZE_MAX) {}
796
797 // The threshold at which to start a new incremental collection.
798 //
799 // TODO: This is currently read off-thread during parsing, but at some point
800 // we should be able to make this MainThreadData<>.
801 AtomicByteCount startBytes_;
802
803 // The threshold at which start a new non-incremental collection or finish an
804 // ongoing collection non-incrementally.
805 size_t incrementalLimitBytes_;
806
807 // The threshold at which to trigger a slice during an ongoing incremental
808 // collection.
809 size_t sliceBytes_;
810
811 public:
startBytes()812 size_t startBytes() const { return startBytes_; }
sliceBytes()813 size_t sliceBytes() const { return sliceBytes_; }
incrementalLimitBytes()814 size_t incrementalLimitBytes() const { return incrementalLimitBytes_; }
815 double eagerAllocTrigger(bool highFrequencyGC) const;
816
817 void setSliceThreshold(ZoneAllocator* zone, const HeapSize& heapSize,
818 const GCSchedulingTunables& tunables);
clearSliceThreshold()819 void clearSliceThreshold() { sliceBytes_ = SIZE_MAX; }
hasSliceThreshold()820 bool hasSliceThreshold() const { return sliceBytes_ != SIZE_MAX; }
821
822 protected:
823 void setIncrementalLimitFromStartBytes(size_t retainedBytes,
824 const GCSchedulingTunables& tunables);
825 };
826
827 // A heap threshold that is based on a multiple of the retained size after the
828 // last collection adjusted based on collection frequency and retained
829 // size. This is used to determine when to do a zone GC based on GC heap size.
830 class GCHeapThreshold : public HeapThreshold {
831 public:
832 void updateStartThreshold(size_t lastBytes, JS::GCOptions options,
833 const GCSchedulingTunables& tunables,
834 const GCSchedulingState& state, bool isAtomsZone,
835 const AutoLockGC& lock);
836
837 private:
838 static double computeZoneHeapGrowthFactorForHeapSize(
839 size_t lastBytes, const GCSchedulingTunables& tunables,
840 const GCSchedulingState& state);
841 static size_t computeZoneTriggerBytes(double growthFactor, size_t lastBytes,
842 JS::GCOptions options,
843 const GCSchedulingTunables& tunables,
844 const AutoLockGC& lock);
845 };
846
847 // A heap threshold that is calculated as a constant multiple of the retained
848 // size after the last collection. This is used to determines when to do a zone
849 // GC based on malloc data.
850 class MallocHeapThreshold : public HeapThreshold {
851 public:
852 void updateStartThreshold(size_t lastBytes,
853 const GCSchedulingTunables& tunables,
854 const AutoLockGC& lock);
855
856 private:
857 static size_t computeZoneTriggerBytes(double growthFactor, size_t lastBytes,
858 size_t baseBytes,
859 const AutoLockGC& lock);
860 };
861
862 // A fixed threshold that's used to determine when we need to do a zone GC based
863 // on allocated JIT code.
864 class JitHeapThreshold : public HeapThreshold {
865 public:
JitHeapThreshold(size_t bytes)866 explicit JitHeapThreshold(size_t bytes) { startBytes_ = bytes; }
867 };
868
869 struct SharedMemoryUse {
SharedMemoryUseSharedMemoryUse870 explicit SharedMemoryUse(MemoryUse use) : count(0), nbytes(0) {
871 #ifdef DEBUG
872 this->use = use;
873 #endif
874 }
875
876 size_t count;
877 size_t nbytes;
878 #ifdef DEBUG
879 MemoryUse use;
880 #endif
881 };
882
883 // A map which tracks shared memory uses (shared in the sense that an allocation
884 // can be referenced by more than one GC thing in a zone). This allows us to
885 // only account for the memory once.
886 using SharedMemoryMap =
887 HashMap<void*, SharedMemoryUse, DefaultHasher<void*>, SystemAllocPolicy>;
888
889 #ifdef DEBUG
890
891 // Counts memory associated with GC things in a zone.
892 //
893 // This records details of the cell (or non-cell pointer) the memory allocation
894 // is associated with to check the correctness of the information provided. This
895 // is not present in opt builds.
896 class MemoryTracker {
897 public:
898 MemoryTracker();
899 void fixupAfterMovingGC();
900 void checkEmptyOnDestroy();
901
902 void adopt(MemoryTracker& other);
903
904 // Track memory by associated GC thing pointer.
905 void trackGCMemory(Cell* cell, size_t nbytes, MemoryUse use);
906 void untrackGCMemory(Cell* cell, size_t nbytes, MemoryUse use);
907 void swapGCMemory(Cell* a, Cell* b, MemoryUse use);
908
909 // Track memory by associated non-GC thing pointer.
910 void registerNonGCMemory(void* ptr, MemoryUse use);
911 void unregisterNonGCMemory(void* ptr, MemoryUse use);
912 void moveNonGCMemory(void* dst, void* src, MemoryUse use);
913 void incNonGCMemory(void* ptr, size_t nbytes, MemoryUse use);
914 void decNonGCMemory(void* ptr, size_t nbytes, MemoryUse use);
915
916 private:
917 template <typename Ptr>
918 struct Key {
919 Key(Ptr* ptr, MemoryUse use);
920 Ptr* ptr() const;
921 MemoryUse use() const;
922
923 private:
924 # ifdef JS_64BIT
925 // Pack this into a single word on 64 bit platforms.
926 uintptr_t ptr_ : 56;
927 uintptr_t use_ : 8;
928 # else
929 uintptr_t ptr_ : 32;
930 uintptr_t use_ : 8;
931 # endif
932 };
933
934 template <typename Ptr>
935 struct Hasher {
936 using KeyT = Key<Ptr>;
937 using Lookup = KeyT;
938 static HashNumber hash(const Lookup& l);
939 static bool match(const KeyT& key, const Lookup& l);
940 static void rekey(KeyT& k, const KeyT& newKey);
941 };
942
943 template <typename Ptr>
944 using Map = HashMap<Key<Ptr>, size_t, Hasher<Ptr>, SystemAllocPolicy>;
945 using GCMap = Map<Cell>;
946 using NonGCMap = Map<void>;
947
948 static bool isGCMemoryUse(MemoryUse use);
949 static bool isNonGCMemoryUse(MemoryUse use);
950 static bool allowMultipleAssociations(MemoryUse use);
951
952 size_t getAndRemoveEntry(const Key<Cell>& key, LockGuard<Mutex>& lock);
953
954 Mutex mutex;
955
956 // Map containing the allocated size associated with (cell, use) pairs.
957 GCMap gcMap;
958
959 // Map containing the allocated size associated (non-cell pointer, use) pairs.
960 NonGCMap nonGCMap;
961 };
962
963 #endif // DEBUG
964
LinearInterpolate(double x,double x0,double y0,double x1,double y1)965 static inline double LinearInterpolate(double x, double x0, double y0,
966 double x1, double y1) {
967 MOZ_ASSERT(x0 < x1);
968
969 if (x < x0) {
970 return y0;
971 }
972
973 if (x < x1) {
974 return y0 + (y1 - y0) * ((x - x0) / (x1 - x0));
975 }
976
977 return y1;
978 }
979
980 } // namespace gc
981 } // namespace js
982
983 #endif // gc_Scheduling_h
984