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