Import Upstream version 20180207
[hcoop/debian/mlton.git] / runtime / gc / garbage-collection.c
CommitLineData
7f918cf1
CE
1/* Copyright (C) 2009-2010,2012,2016 Matthew Fluet.
2 * Copyright (C) 1999-2008 Henry Cejtin, Matthew Fluet, Suresh
3 * Jagannathan, and Stephen Weeks.
4 * Copyright (C) 1997-2000 NEC Research Institute.
5 *
6 * MLton is released under a BSD-style license.
7 * See the file MLton-LICENSE for details.
8 */
9
10void minorGC (GC_state s) {
11 minorCheneyCopyGC (s);
12}
13
14void majorGC (GC_state s, size_t bytesRequested, bool mayResize) {
15 uintmax_t numGCs;
16 size_t desiredSize;
17
18 s->lastMajorStatistics.numMinorGCs = 0;
19 numGCs =
20 s->cumulativeStatistics.numCopyingGCs
21 + s->cumulativeStatistics.numMarkCompactGCs;
22 if (0 < numGCs
23 and ((float)(s->cumulativeStatistics.numHashConsGCs) / (float)(numGCs)
24 < s->controls.ratios.hashCons))
25 s->hashConsDuringGC = TRUE;
26 desiredSize =
27 sizeofHeapDesired (s, s->lastMajorStatistics.bytesLive + bytesRequested, 0);
28 if (not FORCE_MARK_COMPACT
29 and not s->hashConsDuringGC // only markCompact can hash cons
30 and s->heap.withMapsSize < s->sysvals.ram
31 and (not isHeapInit (&s->secondaryHeap)
32 or createHeapSecondary (s, desiredSize)))
33 majorCheneyCopyGC (s);
34 else
35 majorMarkCompactGC (s);
36 s->hashConsDuringGC = FALSE;
37 s->lastMajorStatistics.bytesLive = s->heap.oldGenSize;
38 if (s->lastMajorStatistics.bytesLive > s->cumulativeStatistics.maxBytesLive)
39 s->cumulativeStatistics.maxBytesLive = s->lastMajorStatistics.bytesLive;
40 /* Notice that the s->lastMajorStatistics.bytesLive below is
41 * different than the s->lastMajorStatistics.bytesLive used as an
42 * argument to createHeapSecondary above. Above, it was an
43 * estimate. Here, it is exactly how much was live after the GC.
44 */
45 if (mayResize) {
46 resizeHeap (s, s->lastMajorStatistics.bytesLive + bytesRequested);
47 }
48 setCardMapAndCrossMap (s);
49 resizeHeapSecondary (s);
50 assert (s->heap.oldGenSize + bytesRequested <= s->heap.size);
51}
52
53void growStackCurrent (GC_state s) {
54 size_t reserved;
55 GC_stack stack;
56
57 reserved = sizeofStackGrowReserved (s, getStackCurrent(s));
58 if (DEBUG_STACKS or s->controls.messages)
59 fprintf (stderr,
60 "[GC: Growing stack of size %s bytes to size %s bytes, using %s bytes.]\n",
61 uintmaxToCommaString(getStackCurrent(s)->reserved),
62 uintmaxToCommaString(reserved),
63 uintmaxToCommaString(getStackCurrent(s)->used));
64 assert (hasHeapBytesFree (s, sizeofStackWithMetaData (s, reserved), 0));
65 stack = newStack (s, reserved, TRUE);
66 copyStack (s, getStackCurrent(s), stack);
67 getThreadCurrent(s)->stack = pointerToObjptr ((pointer)stack, s->heap.start);
68 markCard (s, objptrToPointer (getThreadCurrentObjptr(s), s->heap.start));
69}
70
71void enterGC (GC_state s) {
72 if (s->profiling.isOn) {
73 /* We don't need to profileEnter for count profiling because it
74 * has already bumped the counter. If we did allow the bump, then
75 * the count would look like function(s) had run an extra time.
76 */
77 if (s->profiling.stack
78 and not (PROFILE_COUNT == s->profiling.kind))
79 GC_profileEnter (s);
80 }
81 s->amInGC = TRUE;
82}
83
84void leaveGC (GC_state s) {
85 if (s->profiling.isOn) {
86 if (s->profiling.stack
87 and not (PROFILE_COUNT == s->profiling.kind))
88 GC_profileLeave (s);
89 }
90 s->amInGC = FALSE;
91}
92
93void performGC (GC_state s,
94 size_t oldGenBytesRequested,
95 size_t nurseryBytesRequested,
96 bool forceMajor,
97 bool mayResize) {
98 uintmax_t gcTime;
99 bool stackTopOk;
100 size_t stackBytesRequested;
101 struct rusage ru_start;
102 size_t totalBytesRequested;
103
104 enterGC (s);
105 s->cumulativeStatistics.numGCs++;
106 if (DEBUG or s->controls.messages) {
107 size_t nurserySize = s->heap.size - ((size_t)(s->heap.nursery - s->heap.start));
108 size_t nurseryUsed = (size_t)(s->frontier - s->heap.nursery);
109 fprintf (stderr,
110 "[GC: Starting gc #%s; requesting %s nursery bytes and %s old-gen bytes,]\n",
111 uintmaxToCommaString(s->cumulativeStatistics.numGCs),
112 uintmaxToCommaString(nurseryBytesRequested),
113 uintmaxToCommaString(oldGenBytesRequested));
114 fprintf (stderr,
115 "[GC:\theap at "FMTPTR" of size %s bytes (+ %s bytes card/cross map),]\n",
116 (uintptr_t)(s->heap.start),
117 uintmaxToCommaString(s->heap.size),
118 uintmaxToCommaString(s->heap.withMapsSize - s->heap.size));
119 fprintf (stderr,
120 "[GC:\twith old-gen of size %s bytes (%.1f%% of heap),]\n",
121 uintmaxToCommaString(s->heap.oldGenSize),
122 100.0 * ((double)(s->heap.oldGenSize) / (double)(s->heap.size)));
123 fprintf (stderr,
124 "[GC:\tand nursery of size %s bytes (%.1f%% of heap),]\n",
125 uintmaxToCommaString(nurserySize),
126 100.0 * ((double)(nurserySize) / (double)(s->heap.size)));
127 fprintf (stderr,
128 "[GC:\tand nursery using %s bytes (%.1f%% of heap, %.1f%% of nursery).]\n",
129 uintmaxToCommaString(nurseryUsed),
130 100.0 * ((double)(nurseryUsed) / (double)(s->heap.size)),
131 100.0 * ((double)(nurseryUsed) / (double)(nurserySize)));
132 }
133 assert (invariantForGC (s));
134 if (needGCTime (s))
135 startTiming (&ru_start);
136 minorGC (s);
137 stackTopOk = invariantForMutatorStack (s);
138 stackBytesRequested =
139 stackTopOk
140 ? 0
141 : sizeofStackWithMetaData (s, sizeofStackGrowReserved (s, getStackCurrent (s)));
142 totalBytesRequested =
143 oldGenBytesRequested
144 + nurseryBytesRequested
145 + stackBytesRequested;
146 if (forceMajor
147 or totalBytesRequested > s->heap.size - s->heap.oldGenSize)
148 majorGC (s, totalBytesRequested, mayResize);
149 setGCStateCurrentHeap (s, oldGenBytesRequested + stackBytesRequested,
150 nurseryBytesRequested);
151 assert (hasHeapBytesFree (s, oldGenBytesRequested + stackBytesRequested,
152 nurseryBytesRequested));
153 unless (stackTopOk)
154 growStackCurrent (s);
155 setGCStateCurrentThreadAndStack (s);
156 if (needGCTime (s)) {
157 gcTime = stopTiming (&ru_start, &s->cumulativeStatistics.ru_gc);
158 s->cumulativeStatistics.maxPauseTime =
159 max (s->cumulativeStatistics.maxPauseTime, gcTime);
160 } else
161 gcTime = 0; /* Assign gcTime to quell gcc warning. */
162 if (DEBUG or s->controls.messages) {
163 size_t nurserySize = s->heap.size - (size_t)(s->heap.nursery - s->heap.start);
164 fprintf (stderr,
165 "[GC: Finished gc #%s; time %s ms,]\n",
166 uintmaxToCommaString(s->cumulativeStatistics.numGCs),
167 uintmaxToCommaString(gcTime));
168 fprintf (stderr,
169 "[GC:\theap at "FMTPTR" of size %s bytes (+ %s bytes card/cross map),]\n",
170 (uintptr_t)(s->heap.start),
171 uintmaxToCommaString(s->heap.size),
172 uintmaxToCommaString(s->heap.withMapsSize - s->heap.size));
173 fprintf (stderr,
174 "[GC:\twith old-gen of size %s bytes (%.1f%% of heap),]\n",
175 uintmaxToCommaString(s->heap.oldGenSize),
176 100.0 * ((double)(s->heap.oldGenSize) / (double)(s->heap.size)));
177 fprintf (stderr,
178 "[GC:\tand nursery of size %s bytes (%.1f%% of heap).]\n",
179 uintmaxToCommaString(nurserySize),
180 100.0 * ((double)(nurserySize) / (double)(s->heap.size)));
181 }
182 /* Send a GC signal. */
183 if (s->signalsInfo.gcSignalHandled
184 and s->signalHandlerThread != BOGUS_OBJPTR) {
185 if (DEBUG_SIGNALS)
186 fprintf (stderr, "GC Signal pending.\n");
187 s->signalsInfo.gcSignalPending = TRUE;
188 unless (s->signalsInfo.amInSignalHandler)
189 s->signalsInfo.signalIsPending = TRUE;
190 }
191 if (DEBUG)
192 displayGCState (s, stderr);
193 assert (hasHeapBytesFree (s, oldGenBytesRequested, nurseryBytesRequested));
194 assert (invariantForGC (s));
195 leaveGC (s);
196}
197
198void ensureInvariantForMutator (GC_state s, bool force) {
199 if (force
200 or not (invariantForMutatorFrontier(s))
201 or not (invariantForMutatorStack(s))) {
202 /* This GC will grow the stack, if necessary. */
203 performGC (s, 0, getThreadCurrent(s)->bytesNeeded, force, TRUE);
204 }
205 assert (invariantForMutatorFrontier(s));
206 assert (invariantForMutatorStack(s));
207}
208
209/* ensureHasHeapBytesFree (s, oldGen, nursery)
210 */
211void ensureHasHeapBytesFree (GC_state s,
212 size_t oldGenBytesRequested,
213 size_t nurseryBytesRequested) {
214 assert (s->heap.nursery <= s->limitPlusSlop);
215 assert (s->frontier <= s->limitPlusSlop);
216 if (not hasHeapBytesFree (s, oldGenBytesRequested, nurseryBytesRequested))
217 performGC (s, oldGenBytesRequested, nurseryBytesRequested, FALSE, TRUE);
218 assert (hasHeapBytesFree (s, oldGenBytesRequested, nurseryBytesRequested));
219}
220
221void GC_collect (GC_state s, size_t bytesRequested, bool force) {
222 enter (s);
223 /* When the mutator requests zero bytes, it may actually need as
224 * much as GC_HEAP_LIMIT_SLOP.
225 */
226 if (0 == bytesRequested)
227 bytesRequested = GC_HEAP_LIMIT_SLOP;
228 getThreadCurrent(s)->bytesNeeded = bytesRequested;
229 switchToSignalHandlerThreadIfNonAtomicAndSignalPending (s);
230 ensureInvariantForMutator (s, force);
231 assert (invariantForMutatorFrontier(s));
232 assert (invariantForMutatorStack(s));
233 leave (s);
234}