-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy pathscavenger.h
331 lines (268 loc) · 9.88 KB
/
scavenger.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
#ifndef RUNTIME_VM_HEAP_SCAVENGER_H_
#define RUNTIME_VM_HEAP_SCAVENGER_H_
#include "platform/assert.h"
#include "platform/utils.h"
#include "vm/dart.h"
#include "vm/flags.h"
#include "vm/globals.h"
#include "vm/heap/page.h"
#include "vm/heap/spaces.h"
#include "vm/isolate.h"
#include "vm/lockers.h"
#include "vm/raw_object.h"
#include "vm/ring_buffer.h"
#include "vm/virtual_memory.h"
#include "vm/visitor.h"
namespace dart {
// Forward declarations.
class Heap;
class Isolate;
class JSONObject;
class ObjectSet;
template <bool parallel>
class ScavengerVisitorBase;
class SemiSpace {
public:
explicit SemiSpace(intptr_t max_capacity_in_words);
~SemiSpace();
Page* TryAllocatePageLocked(bool link);
bool Contains(uword addr) const;
void WriteProtect(bool read_only);
intptr_t used_in_words() const {
intptr_t size = 0;
for (const Page* p = head_; p != nullptr; p = p->next()) {
size += p->used();
}
return size >> kWordSizeLog2;
}
intptr_t capacity_in_words() const { return capacity_in_words_; }
intptr_t max_capacity_in_words() const { return max_capacity_in_words_; }
Page* head() const { return head_; }
void AddList(Page* head, Page* tail);
private:
// Size of NewPages in this semi-space.
intptr_t capacity_in_words_ = 0;
// Size of NewPages before we trigger a scavenge.
intptr_t max_capacity_in_words_;
Page* head_ = nullptr;
Page* tail_ = nullptr;
};
// Statistics for a particular scavenge.
class ScavengeStats {
public:
ScavengeStats() {}
ScavengeStats(int64_t start_micros,
int64_t end_micros,
SpaceUsage before,
SpaceUsage after,
intptr_t promo_candidates_in_words,
intptr_t promoted_in_words,
intptr_t abandoned_in_words)
: start_micros_(start_micros),
end_micros_(end_micros),
before_(before),
after_(after),
promo_candidates_in_words_(promo_candidates_in_words),
promoted_in_words_(promoted_in_words),
abandoned_in_words_(abandoned_in_words) {}
// Of all data before scavenge, what fraction was found to be garbage?
// If this scavenge included growth, assume the extra capacity would become
// garbage to give the scavenger a chance to stabilize at the new capacity.
double ExpectedGarbageFraction() const {
double work =
after_.used_in_words + promoted_in_words_ + abandoned_in_words_;
return 1.0 - (work / after_.capacity_in_words);
}
// Fraction of promotion candidates that survived and was thereby promoted.
// Returns zero if there were no promotion candidates.
double PromoCandidatesSuccessFraction() const {
return promo_candidates_in_words_ > 0
? promoted_in_words_ /
static_cast<double>(promo_candidates_in_words_)
: 0.0;
}
intptr_t UsedBeforeInWords() const { return before_.used_in_words; }
int64_t DurationMicros() const { return end_micros_ - start_micros_; }
private:
int64_t start_micros_;
int64_t end_micros_;
SpaceUsage before_;
SpaceUsage after_;
intptr_t promo_candidates_in_words_;
intptr_t promoted_in_words_;
intptr_t abandoned_in_words_;
};
class Scavenger {
private:
static const intptr_t kTLABSize = 512 * KB;
public:
Scavenger(Heap* heap, intptr_t max_semi_capacity_in_words);
~Scavenger();
// Check whether this Scavenger contains this address.
// During scavenging both the to and from spaces contain "legal" objects.
// During a scavenge this function only returns true for addresses that will
// be part of the surviving objects.
bool Contains(uword addr) const { return to_->Contains(addr); }
ObjectPtr FindObject(FindObjectVisitor* visitor);
uword TryAllocate(Thread* thread, intptr_t size) {
uword addr = TryAllocateFromTLAB(thread, size);
if (LIKELY(addr != 0)) {
return addr;
}
TryAllocateNewTLAB(thread, size, true);
return TryAllocateFromTLAB(thread, size);
}
uword TryAllocateNoSafepoint(Thread* thread, intptr_t size) {
uword addr = TryAllocateFromTLAB(thread, size);
if (LIKELY(addr != 0)) {
return addr;
}
TryAllocateNewTLAB(thread, size, false);
return TryAllocateFromTLAB(thread, size);
}
void AbandonRemainingTLAB(Thread* thread);
void AbandonRemainingTLABForDebugging(Thread* thread);
// Collect the garbage in this scavenger.
void Scavenge(Thread* thread, GCType type, GCReason reason);
int64_t UsedInWords() const {
MutexLocker ml(&space_lock_);
return to_->used_in_words();
}
int64_t CapacityInWords() const { return to_->max_capacity_in_words(); }
int64_t ExternalInWords() const { return external_size_ >> kWordSizeLog2; }
SpaceUsage GetCurrentUsage() const {
SpaceUsage usage;
usage.used_in_words = UsedInWords();
usage.capacity_in_words = CapacityInWords();
usage.external_in_words = ExternalInWords();
return usage;
}
void VisitObjects(ObjectVisitor* visitor) const;
void VisitObjectPointers(ObjectPointerVisitor* visitor) const;
void AddRegionsToObjectSet(ObjectSet* set) const;
void WriteProtect(bool read_only);
bool ShouldPerformIdleScavenge(int64_t deadline);
void AddGCTime(int64_t micros) { gc_time_micros_ += micros; }
int64_t gc_time_micros() const { return gc_time_micros_; }
void IncrementCollections() { collections_++; }
intptr_t collections() const { return collections_; }
#ifndef PRODUCT
void PrintToJSONObject(JSONObject* object) const;
#endif // !PRODUCT
// Tracks an external allocation by incrementing the new space's total
// external size tracker. Returns false without incrementing the tracker if
// this allocation will make it exceed kMaxAddrSpaceInWords.
bool AllocatedExternal(intptr_t size) {
ASSERT(size >= 0);
intptr_t expected = external_size_.load();
intptr_t desired;
do {
intptr_t next_external_size_in_words =
(external_size_ >> kWordSizeLog2) + (size >> kWordSizeLog2);
if (next_external_size_in_words < 0 ||
next_external_size_in_words > kMaxAddrSpaceInWords) {
return false;
}
desired = expected + size;
ASSERT(desired >= 0);
} while (!external_size_.compare_exchange_weak(expected, desired));
return true;
}
void FreedExternal(intptr_t size) {
ASSERT(size >= 0);
external_size_ -= size;
ASSERT(external_size_ >= 0);
}
void MakeNewSpaceIterable();
int64_t FreeSpaceInWords(Isolate* isolate) const;
bool scavenging() const { return scavenging_; }
// The maximum number of Dart mutator threads we allow to execute at the same
// time.
static intptr_t MaxMutatorThreadCount() {
// With a max new-space of 16 MB and 512kb TLABs we would allow up to 8
// mutator threads to run at the same time.
const intptr_t max_parallel_tlab_usage =
(FLAG_new_gen_semi_max_size * MB) / Scavenger::kTLABSize;
const intptr_t max_pool_size = max_parallel_tlab_usage / 4;
return max_pool_size > 0 ? max_pool_size : 1;
}
Page* head() const {
return to_->head();
}
private:
// Ids for time and data records in Heap::GCStats.
enum {
// Time
kDummyScavengeTime = 0,
kSafePoint = 1,
kVisitIsolateRoots = 2,
kIterateStoreBuffers = 3,
kProcessToSpace = 4,
kIterateWeaks = 5,
};
uword TryAllocateFromTLAB(Thread* thread, intptr_t size) {
ASSERT(Utils::IsAligned(size, kObjectAlignment));
ASSERT(heap_ != Dart::vm_isolate_group()->heap());
const uword result = thread->top();
const intptr_t remaining = static_cast<intptr_t>(thread->end()) - result;
ASSERT(remaining >= 0);
if (UNLIKELY(remaining < size)) {
return 0;
}
ASSERT(to_->Contains(result));
ASSERT((result & kObjectAlignmentMask) == kNewObjectAlignmentOffset);
thread->set_top(result + size);
return result;
}
void TryAllocateNewTLAB(Thread* thread, intptr_t size, bool can_safepoint);
SemiSpace* Prologue(GCReason reason);
intptr_t ParallelScavenge(SemiSpace* from);
intptr_t SerialScavenge(SemiSpace* from);
void ReverseScavenge(SemiSpace** from);
void IterateIsolateRoots(ObjectPointerVisitor* visitor);
template <bool parallel>
void IterateStoreBuffers(ScavengerVisitorBase<parallel>* visitor);
template <bool parallel>
void IterateRememberedCards(ScavengerVisitorBase<parallel>* visitor);
void IterateObjectIdTable(ObjectPointerVisitor* visitor);
template <bool parallel>
void IterateRoots(ScavengerVisitorBase<parallel>* visitor);
void MournWeakHandles();
void Epilogue(SemiSpace* from);
void VerifyStoreBuffers();
void UpdateMaxHeapCapacity();
void UpdateMaxHeapUsage();
void MournWeakTables();
intptr_t NewSizeInWords(intptr_t old_size_in_words, GCReason reason) const;
Heap* heap_;
SemiSpace* to_;
PromotionStack promotion_stack_;
intptr_t max_semi_capacity_in_words_;
// Keep track whether a scavenge is currently running.
bool scavenging_;
bool early_tenure_ = false;
RelaxedAtomic<intptr_t> root_slices_started_;
StoreBufferBlock* blocks_ = nullptr;
int64_t gc_time_micros_;
intptr_t collections_;
static const int kStatsHistoryCapacity = 4;
RingBuffer<ScavengeStats, kStatsHistoryCapacity> stats_history_;
intptr_t scavenge_words_per_micro_;
intptr_t idle_scavenge_threshold_in_words_;
// The total size of external data associated with objects in this scavenger.
RelaxedAtomic<intptr_t> external_size_;
RelaxedAtomic<bool> failed_to_promote_;
RelaxedAtomic<bool> abort_;
// Protects new space during the allocation of new TLABs
mutable Mutex space_lock_;
template <bool>
friend class ScavengerVisitorBase;
friend class ScavengerWeakVisitor;
friend class ScavengerFinalizerVisitor;
DISALLOW_COPY_AND_ASSIGN(Scavenger);
};
} // namespace dart
#endif // RUNTIME_VM_HEAP_SCAVENGER_H_