| 1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 | // for details. All rights reserved. Use of this source code is governed by a |
| 3 | // BSD-style license that can be found in the LICENSE file. |
| 4 | |
| 5 | #ifndef RUNTIME_VM_HEAP_HEAP_H_ |
| 6 | #define RUNTIME_VM_HEAP_HEAP_H_ |
| 7 | |
| 8 | #if defined(SHOULD_NOT_INCLUDE_RUNTIME) |
| 9 | #error "Should not include runtime" |
| 10 | #endif |
| 11 | |
| 12 | #include "platform/assert.h" |
| 13 | #include "vm/allocation.h" |
| 14 | #include "vm/flags.h" |
| 15 | #include "vm/globals.h" |
| 16 | #include "vm/heap/pages.h" |
| 17 | #include "vm/heap/scavenger.h" |
| 18 | #include "vm/heap/spaces.h" |
| 19 | #include "vm/heap/weak_table.h" |
| 20 | #include "vm/isolate.h" |
| 21 | |
| 22 | namespace dart { |
| 23 | |
| 24 | // Forward declarations. |
| 25 | class Isolate; |
| 26 | class IsolateGroup; |
| 27 | class ObjectPointerVisitor; |
| 28 | class ObjectSet; |
| 29 | class ServiceEvent; |
| 30 | class TimelineEventScope; |
| 31 | class VirtualMemory; |
| 32 | |
| 33 | class Heap { |
| 34 | public: |
| 35 | enum Space { |
| 36 | kNew, |
| 37 | kOld, |
| 38 | kCode, |
| 39 | }; |
| 40 | |
| 41 | enum WeakSelector { |
| 42 | kPeers = 0, |
| 43 | #if !defined(HASH_IN_OBJECT_HEADER) |
| 44 | kIdentityHashes, |
| 45 | #endif |
| 46 | kCanonicalHashes, |
| 47 | kObjectIds, |
| 48 | kLoadingUnits, |
| 49 | kNumWeakSelectors |
| 50 | }; |
| 51 | |
| 52 | enum GCType { |
| 53 | kScavenge, |
| 54 | kMarkSweep, |
| 55 | kMarkCompact, |
| 56 | }; |
| 57 | |
| 58 | enum GCReason { |
| 59 | kNewSpace, // New space is full. |
| 60 | kPromotion, // Old space limit crossed after a scavenge. |
| 61 | kOldSpace, // Old space limit crossed. |
| 62 | kFinalize, // Concurrent marking finished. |
| 63 | kFull, // Heap::CollectAllGarbage |
| 64 | kExternal, // Dart_NewFinalizableHandle Dart_NewWeakPersistentHandle |
| 65 | kIdle, // Dart_NotifyIdle |
| 66 | kLowMemory, // Dart_NotifyLowMemory |
| 67 | kDebugging, // service request, etc. |
| 68 | kSendAndExit, // SendPort.sendAndExit |
| 69 | }; |
| 70 | |
| 71 | // Pattern for unused new space and swept old space. |
| 72 | static const uint8_t kZapByte = 0xf3; |
| 73 | |
| 74 | ~Heap(); |
| 75 | |
| 76 | Scavenger* new_space() { return &new_space_; } |
| 77 | PageSpace* old_space() { return &old_space_; } |
| 78 | |
| 79 | uword Allocate(intptr_t size, Space space) { |
| 80 | ASSERT(!read_only_); |
| 81 | switch (space) { |
| 82 | case kNew: |
| 83 | // Do not attempt to allocate very large objects in new space. |
| 84 | if (!IsAllocatableInNewSpace(size)) { |
| 85 | return AllocateOld(size, OldPage::kData); |
| 86 | } |
| 87 | return AllocateNew(size); |
| 88 | case kOld: |
| 89 | return AllocateOld(size, OldPage::kData); |
| 90 | case kCode: |
| 91 | return AllocateOld(size, OldPage::kExecutable); |
| 92 | default: |
| 93 | UNREACHABLE(); |
| 94 | } |
| 95 | return 0; |
| 96 | } |
| 97 | |
| 98 | // Track external data. |
| 99 | void AllocatedExternal(intptr_t size, Space space); |
| 100 | void FreedExternal(intptr_t size, Space space); |
| 101 | // Move external size from new to old space. Does not by itself trigger GC. |
| 102 | void PromotedExternal(intptr_t size); |
| 103 | |
| 104 | // Heap contains the specified address. |
| 105 | bool Contains(uword addr) const; |
| 106 | bool NewContains(uword addr) const; |
| 107 | bool OldContains(uword addr) const; |
| 108 | bool CodeContains(uword addr) const; |
| 109 | bool DataContains(uword addr) const; |
| 110 | |
| 111 | // Find an object by visiting all pointers in the specified heap space, |
| 112 | // the 'visitor' is used to determine if an object is found or not. |
| 113 | // The 'visitor' function should be set up to return true if the |
| 114 | // object is found, traversal through the heap space stops at that |
| 115 | // point. |
| 116 | // The 'visitor' function should return false if the object is not found, |
| 117 | // traversal through the heap space continues. |
| 118 | // Returns null object if nothing is found. |
| 119 | InstructionsPtr FindObjectInCodeSpace(FindObjectVisitor* visitor) const; |
| 120 | ObjectPtr FindOldObject(FindObjectVisitor* visitor) const; |
| 121 | ObjectPtr FindNewObject(FindObjectVisitor* visitor); |
| 122 | ObjectPtr FindObject(FindObjectVisitor* visitor); |
| 123 | |
| 124 | void HintFreed(intptr_t size); |
| 125 | void NotifyIdle(int64_t deadline); |
| 126 | void NotifyLowMemory(); |
| 127 | |
| 128 | // Collect a single generation. |
| 129 | void CollectGarbage(Space space); |
| 130 | void CollectGarbage(GCType type, GCReason reason); |
| 131 | |
| 132 | // Collect both generations by performing a scavenge followed by a |
| 133 | // mark-sweep. This function may not collect all unreachable objects. Because |
| 134 | // mark-sweep treats new space as roots, a cycle between unreachable old and |
| 135 | // new objects will not be collected until the new objects are promoted. |
| 136 | // Verification based on heap iteration should instead use CollectAllGarbage. |
| 137 | void CollectMostGarbage(GCReason reason = kFull); |
| 138 | |
| 139 | // Collect both generations by performing an evacuation followed by a |
| 140 | // mark-sweep. If incremental marking was in progress, perform another |
| 141 | // mark-sweep. This function will collect all unreachable objects, including |
| 142 | // those in inter-generational cycles or stored during incremental marking. |
| 143 | void CollectAllGarbage(GCReason reason = kFull); |
| 144 | |
| 145 | void CheckStartConcurrentMarking(Thread* thread, GCReason reason); |
| 146 | void StartConcurrentMarking(Thread* thread); |
| 147 | void CheckFinishConcurrentMarking(Thread* thread); |
| 148 | void WaitForMarkerTasks(Thread* thread); |
| 149 | void WaitForSweeperTasks(Thread* thread); |
| 150 | void WaitForSweeperTasksAtSafepoint(Thread* thread); |
| 151 | |
| 152 | // Enables growth control on the page space heaps. This should be |
| 153 | // called before any user code is executed. |
| 154 | void InitGrowthControl(); |
| 155 | void DisableGrowthControl() { SetGrowthControlState(false); } |
| 156 | void SetGrowthControlState(bool state); |
| 157 | bool GrowthControlState(); |
| 158 | |
| 159 | // Protect access to the heap. Note: Code pages are made |
| 160 | // executable/non-executable when 'read_only' is true/false, respectively. |
| 161 | void WriteProtect(bool read_only); |
| 162 | void WriteProtectCode(bool read_only) { |
| 163 | old_space_.WriteProtectCode(read_only); |
| 164 | } |
| 165 | |
| 166 | // Initialize the heap and register it with the isolate. |
| 167 | static void Init(IsolateGroup* isolate_group, |
| 168 | intptr_t max_new_gen_words, |
| 169 | intptr_t max_old_gen_words); |
| 170 | |
| 171 | // Returns a suitable name for a VM region in the heap. |
| 172 | static const char* RegionName(Space space); |
| 173 | |
| 174 | // Verify that all pointers in the heap point to the heap. |
| 175 | bool Verify(MarkExpectation mark_expectation = kForbidMarked); |
| 176 | |
| 177 | // Print heap sizes. |
| 178 | void PrintSizes() const; |
| 179 | |
| 180 | // Return amount of memory used and capacity in a space, excluding external. |
| 181 | int64_t UsedInWords(Space space) const; |
| 182 | int64_t CapacityInWords(Space space) const; |
| 183 | int64_t ExternalInWords(Space space) const; |
| 184 | |
| 185 | int64_t TotalUsedInWords() const; |
| 186 | int64_t TotalCapacityInWords() const; |
| 187 | int64_t TotalExternalInWords() const; |
| 188 | // Return the amount of GCing in microseconds. |
| 189 | int64_t GCTimeInMicros(Space space) const; |
| 190 | |
| 191 | intptr_t Collections(Space space) const; |
| 192 | |
| 193 | ObjectSet* CreateAllocatedObjectSet(Zone* zone, |
| 194 | MarkExpectation mark_expectation); |
| 195 | |
| 196 | static const char* GCTypeToString(GCType type); |
| 197 | static const char* GCReasonToString(GCReason reason); |
| 198 | |
| 199 | // Associate a peer with an object. A non-existent peer is equal to NULL. |
| 200 | void SetPeer(ObjectPtr raw_obj, void* peer) { |
| 201 | SetWeakEntry(raw_obj, kPeers, reinterpret_cast<intptr_t>(peer)); |
| 202 | } |
| 203 | void* GetPeer(ObjectPtr raw_obj) const { |
| 204 | return reinterpret_cast<void*>(GetWeakEntry(raw_obj, kPeers)); |
| 205 | } |
| 206 | int64_t PeerCount() const; |
| 207 | |
| 208 | #if !defined(HASH_IN_OBJECT_HEADER) |
| 209 | // Associate an identity hashCode with an object. An non-existent hashCode |
| 210 | // is equal to 0. |
| 211 | void SetHash(ObjectPtr raw_obj, intptr_t hash) { |
| 212 | SetWeakEntry(raw_obj, kIdentityHashes, hash); |
| 213 | } |
| 214 | intptr_t GetHash(ObjectPtr raw_obj) const { |
| 215 | return GetWeakEntry(raw_obj, kIdentityHashes); |
| 216 | } |
| 217 | #endif |
| 218 | |
| 219 | void SetCanonicalHash(ObjectPtr raw_obj, intptr_t hash) { |
| 220 | SetWeakEntry(raw_obj, kCanonicalHashes, hash); |
| 221 | } |
| 222 | intptr_t GetCanonicalHash(ObjectPtr raw_obj) const { |
| 223 | return GetWeakEntry(raw_obj, kCanonicalHashes); |
| 224 | } |
| 225 | void ResetCanonicalHashTable(); |
| 226 | |
| 227 | // Associate an id with an object (used when serializing an object). |
| 228 | // A non-existant id is equal to 0. |
| 229 | void SetObjectId(ObjectPtr raw_obj, intptr_t object_id) { |
| 230 | ASSERT(Thread::Current()->IsMutatorThread()); |
| 231 | SetWeakEntry(raw_obj, kObjectIds, object_id); |
| 232 | } |
| 233 | intptr_t GetObjectId(ObjectPtr raw_obj) const { |
| 234 | ASSERT(Thread::Current()->IsMutatorThread()); |
| 235 | return GetWeakEntry(raw_obj, kObjectIds); |
| 236 | } |
| 237 | void ResetObjectIdTable(); |
| 238 | |
| 239 | void SetLoadingUnit(ObjectPtr raw_obj, intptr_t object_id) { |
| 240 | ASSERT(Thread::Current()->IsMutatorThread()); |
| 241 | SetWeakEntry(raw_obj, kLoadingUnits, object_id); |
| 242 | } |
| 243 | intptr_t GetLoadingUnit(ObjectPtr raw_obj) const { |
| 244 | ASSERT(Thread::Current()->IsMutatorThread()); |
| 245 | return GetWeakEntry(raw_obj, kLoadingUnits); |
| 246 | } |
| 247 | |
| 248 | // Used by the GC algorithms to propagate weak entries. |
| 249 | intptr_t GetWeakEntry(ObjectPtr raw_obj, WeakSelector sel) const; |
| 250 | void SetWeakEntry(ObjectPtr raw_obj, WeakSelector sel, intptr_t val); |
| 251 | |
| 252 | WeakTable* GetWeakTable(Space space, WeakSelector selector) const { |
| 253 | if (space == kNew) { |
| 254 | return new_weak_tables_[selector]; |
| 255 | } |
| 256 | ASSERT(space == kOld); |
| 257 | return old_weak_tables_[selector]; |
| 258 | } |
| 259 | void SetWeakTable(Space space, WeakSelector selector, WeakTable* value) { |
| 260 | if (space == kNew) { |
| 261 | new_weak_tables_[selector] = value; |
| 262 | } else { |
| 263 | ASSERT(space == kOld); |
| 264 | old_weak_tables_[selector] = value; |
| 265 | } |
| 266 | } |
| 267 | |
| 268 | void ForwardWeakEntries(ObjectPtr before_object, ObjectPtr after_object); |
| 269 | void ForwardWeakTables(ObjectPointerVisitor* visitor); |
| 270 | |
| 271 | // Stats collection. |
| 272 | void RecordTime(int id, int64_t micros) { |
| 273 | ASSERT((id >= 0) && (id < GCStats::kTimeEntries)); |
| 274 | stats_.times_[id] = micros; |
| 275 | } |
| 276 | |
| 277 | void RecordData(int id, intptr_t value) { |
| 278 | ASSERT((id >= 0) && (id < GCStats::kDataEntries)); |
| 279 | stats_.data_[id] = value; |
| 280 | } |
| 281 | |
| 282 | void UpdateGlobalMaxUsed(); |
| 283 | |
| 284 | static bool IsAllocatableInNewSpace(intptr_t size) { |
| 285 | return size <= kNewAllocatableSize; |
| 286 | } |
| 287 | static bool IsAllocatableViaFreeLists(intptr_t size) { |
| 288 | return size < kAllocatablePageSize; |
| 289 | } |
| 290 | |
| 291 | #ifndef PRODUCT |
| 292 | void PrintToJSONObject(Space space, JSONObject* object) const; |
| 293 | |
| 294 | // Returns a JSON object with total memory usage statistics for both new and |
| 295 | // old space combined. |
| 296 | void PrintMemoryUsageJSON(JSONStream* stream) const; |
| 297 | void PrintMemoryUsageJSON(JSONObject* jsobj) const; |
| 298 | |
| 299 | // The heap map contains the sizes and class ids for the objects in each page. |
| 300 | void PrintHeapMapToJSONStream(Isolate* isolate, JSONStream* stream) { |
| 301 | old_space_.PrintHeapMapToJSONStream(isolate, stream); |
| 302 | } |
| 303 | #endif // PRODUCT |
| 304 | |
| 305 | IsolateGroup* isolate_group() const { return isolate_group_; } |
| 306 | |
| 307 | Monitor* barrier() const { return &barrier_; } |
| 308 | Monitor* barrier_done() const { return &barrier_done_; } |
| 309 | |
| 310 | void SetupImagePage(void* pointer, uword size, bool is_executable) { |
| 311 | old_space_.SetupImagePage(pointer, size, is_executable); |
| 312 | } |
| 313 | |
| 314 | static const intptr_t kNewAllocatableSize = 256 * KB; |
| 315 | static const intptr_t kAllocatablePageSize = 64 * KB; |
| 316 | |
| 317 | Space SpaceForExternal(intptr_t size) const; |
| 318 | |
| 319 | void CollectOnNthAllocation(intptr_t num_allocations); |
| 320 | |
| 321 | void MergeFrom(Heap* donor); |
| 322 | |
| 323 | private: |
| 324 | class GCStats : public ValueObject { |
| 325 | public: |
| 326 | GCStats() {} |
| 327 | intptr_t num_; |
| 328 | Heap::GCType type_; |
| 329 | Heap::GCReason reason_; |
| 330 | |
| 331 | class Data : public ValueObject { |
| 332 | public: |
| 333 | Data() {} |
| 334 | int64_t micros_; |
| 335 | SpaceUsage new_; |
| 336 | SpaceUsage old_; |
| 337 | |
| 338 | private: |
| 339 | DISALLOW_COPY_AND_ASSIGN(Data); |
| 340 | }; |
| 341 | |
| 342 | enum { kTimeEntries = 6 }; |
| 343 | enum { kDataEntries = 4 }; |
| 344 | |
| 345 | Data before_; |
| 346 | Data after_; |
| 347 | int64_t times_[kTimeEntries]; |
| 348 | intptr_t data_[kDataEntries]; |
| 349 | |
| 350 | private: |
| 351 | DISALLOW_COPY_AND_ASSIGN(GCStats); |
| 352 | }; |
| 353 | |
| 354 | Heap(IsolateGroup* isolate_group, |
| 355 | intptr_t max_new_gen_semi_words, // Max capacity of new semi-space. |
| 356 | intptr_t max_old_gen_words); |
| 357 | |
| 358 | uword AllocateNew(intptr_t size); |
| 359 | uword AllocateOld(intptr_t size, OldPage::PageType type); |
| 360 | |
| 361 | // Visit all pointers. Caller must ensure concurrent sweeper is not running, |
| 362 | // and the visitor must not allocate. |
| 363 | void VisitObjectPointers(ObjectPointerVisitor* visitor); |
| 364 | |
| 365 | // Visit all objects, including FreeListElement "objects". Caller must ensure |
| 366 | // concurrent sweeper is not running, and the visitor must not allocate. |
| 367 | void VisitObjects(ObjectVisitor* visitor); |
| 368 | void VisitObjectsNoImagePages(ObjectVisitor* visitor); |
| 369 | void VisitObjectsImagePages(ObjectVisitor* visitor) const; |
| 370 | |
| 371 | // Like Verify, but does not wait for concurrent sweeper, so caller must |
| 372 | // ensure thread-safety. |
| 373 | bool VerifyGC(MarkExpectation mark_expectation = kForbidMarked); |
| 374 | |
| 375 | // Helper functions for garbage collection. |
| 376 | void CollectNewSpaceGarbage(Thread* thread, GCReason reason); |
| 377 | void CollectOldSpaceGarbage(Thread* thread, GCType type, GCReason reason); |
| 378 | void EvacuateNewSpace(Thread* thread, GCReason reason); |
| 379 | |
| 380 | // GC stats collection. |
| 381 | void RecordBeforeGC(GCType type, GCReason reason); |
| 382 | void RecordAfterGC(GCType type); |
| 383 | void PrintStats(); |
| 384 | void PrintStatsToTimeline(TimelineEventScope* event, GCReason reason); |
| 385 | |
| 386 | // Updates gc in progress flags. |
| 387 | bool BeginNewSpaceGC(Thread* thread); |
| 388 | void EndNewSpaceGC(); |
| 389 | bool BeginOldSpaceGC(Thread* thread); |
| 390 | void EndOldSpaceGC(); |
| 391 | |
| 392 | void AddRegionsToObjectSet(ObjectSet* set) const; |
| 393 | |
| 394 | // Trigger major GC if 'gc_on_nth_allocation_' is set. |
| 395 | void CollectForDebugging(); |
| 396 | |
| 397 | IsolateGroup* isolate_group_; |
| 398 | |
| 399 | // The different spaces used for allocation. |
| 400 | Scavenger new_space_; |
| 401 | PageSpace old_space_; |
| 402 | |
| 403 | WeakTable* new_weak_tables_[kNumWeakSelectors]; |
| 404 | WeakTable* old_weak_tables_[kNumWeakSelectors]; |
| 405 | |
| 406 | mutable Monitor barrier_; |
| 407 | mutable Monitor barrier_done_; |
| 408 | |
| 409 | // GC stats collection. |
| 410 | GCStats stats_; |
| 411 | |
| 412 | // This heap is in read-only mode: No allocation is allowed. |
| 413 | bool read_only_; |
| 414 | |
| 415 | // GC on the heap is in progress. |
| 416 | Monitor gc_in_progress_monitor_; |
| 417 | bool gc_new_space_in_progress_; |
| 418 | bool gc_old_space_in_progress_; |
| 419 | bool last_gc_was_old_space_; |
| 420 | bool assume_scavenge_will_fail_; |
| 421 | |
| 422 | static const intptr_t kNoForcedGarbageCollection = -1; |
| 423 | |
| 424 | // Whether the next heap allocation (new or old) should trigger |
| 425 | // CollectAllGarbage. Used within unit tests for testing GC on certain |
| 426 | // sensitive codepaths. |
| 427 | intptr_t gc_on_nth_allocation_; |
| 428 | |
| 429 | friend class Become; // VisitObjectPointers |
| 430 | friend class GCCompactor; // VisitObjectPointers |
| 431 | friend class Precompiler; // VisitObjects |
| 432 | friend class Unmarker; // VisitObjects |
| 433 | friend class ServiceEvent; |
| 434 | friend class Scavenger; // VerifyGC |
| 435 | friend class PageSpace; // VerifyGC |
| 436 | friend class IsolateReloadContext; // VisitObjects |
| 437 | friend class ClassFinalizer; // VisitObjects |
| 438 | friend class HeapIterationScope; // VisitObjects |
| 439 | friend class ProgramVisitor; // VisitObjectsImagePages |
| 440 | friend class Serializer; // VisitObjectsImagePages |
| 441 | friend class HeapTestHelper; |
| 442 | |
| 443 | DISALLOW_COPY_AND_ASSIGN(Heap); |
| 444 | }; |
| 445 | |
| 446 | class HeapIterationScope : public ThreadStackResource { |
| 447 | public: |
| 448 | explicit HeapIterationScope(Thread* thread, bool writable = false); |
| 449 | ~HeapIterationScope(); |
| 450 | |
| 451 | void IterateObjects(ObjectVisitor* visitor) const; |
| 452 | void IterateObjectsNoImagePages(ObjectVisitor* visitor) const; |
| 453 | void IterateOldObjects(ObjectVisitor* visitor) const; |
| 454 | void IterateOldObjectsNoImagePages(ObjectVisitor* visitor) const; |
| 455 | |
| 456 | void IterateVMIsolateObjects(ObjectVisitor* visitor) const; |
| 457 | |
| 458 | void IterateObjectPointers(ObjectPointerVisitor* visitor, |
| 459 | ValidationPolicy validate_frames); |
| 460 | void IterateStackPointers(ObjectPointerVisitor* visitor, |
| 461 | ValidationPolicy validate_frames); |
| 462 | |
| 463 | private: |
| 464 | Heap* heap_; |
| 465 | PageSpace* old_space_; |
| 466 | bool writable_; |
| 467 | |
| 468 | DISALLOW_COPY_AND_ASSIGN(HeapIterationScope); |
| 469 | }; |
| 470 | |
| 471 | class NoHeapGrowthControlScope : public ThreadStackResource { |
| 472 | public: |
| 473 | NoHeapGrowthControlScope(); |
| 474 | ~NoHeapGrowthControlScope(); |
| 475 | |
| 476 | private: |
| 477 | bool current_growth_controller_state_; |
| 478 | DISALLOW_COPY_AND_ASSIGN(NoHeapGrowthControlScope); |
| 479 | }; |
| 480 | |
| 481 | // Note: During this scope all pages are writable and the code pages are |
| 482 | // non-executable. |
| 483 | class WritableVMIsolateScope : ThreadStackResource { |
| 484 | public: |
| 485 | explicit WritableVMIsolateScope(Thread* thread); |
| 486 | ~WritableVMIsolateScope(); |
| 487 | }; |
| 488 | |
| 489 | class WritableCodePages : StackResource { |
| 490 | public: |
| 491 | explicit WritableCodePages(Thread* thread, Isolate* isolate); |
| 492 | ~WritableCodePages(); |
| 493 | |
| 494 | private: |
| 495 | Isolate* isolate_; |
| 496 | }; |
| 497 | |
| 498 | #if defined(TESTING) |
| 499 | class GCTestHelper : public AllStatic { |
| 500 | public: |
| 501 | // Collect new gen without triggering any side effects. The normal call to |
| 502 | // CollectGarbage(Heap::kNew) could potentially trigger an old gen collection |
| 503 | // if there is enough promotion, and this can perturb some tests. |
| 504 | static void CollectNewSpace() { |
| 505 | Thread* thread = Thread::Current(); |
| 506 | ASSERT(thread->execution_state() == Thread::kThreadInVM); |
| 507 | thread->heap()->new_space()->Scavenge(); |
| 508 | } |
| 509 | |
| 510 | // Fully collect old gen and wait for the sweeper to finish. The normal call |
| 511 | // to CollectGarbage(Heap::kOld) may leave so-called "floating garbage", |
| 512 | // objects that were seen by the incremental barrier but later made |
| 513 | // unreachable, and this can perturb some tests. |
| 514 | static void CollectOldSpace() { |
| 515 | Thread* thread = Thread::Current(); |
| 516 | ASSERT(thread->execution_state() == Thread::kThreadInVM); |
| 517 | if (thread->is_marking()) { |
| 518 | thread->heap()->CollectGarbage(Heap::kMarkSweep, Heap::kDebugging); |
| 519 | } |
| 520 | thread->heap()->CollectGarbage(Heap::kMarkSweep, Heap::kDebugging); |
| 521 | WaitForGCTasks(); |
| 522 | } |
| 523 | |
| 524 | static void CollectAllGarbage() { |
| 525 | Thread* thread = Thread::Current(); |
| 526 | ASSERT(thread->execution_state() == Thread::kThreadInVM); |
| 527 | thread->heap()->CollectAllGarbage(Heap::kDebugging); |
| 528 | } |
| 529 | |
| 530 | static void WaitForGCTasks() { |
| 531 | Thread* thread = Thread::Current(); |
| 532 | ASSERT(thread->execution_state() == Thread::kThreadInVM); |
| 533 | thread->heap()->WaitForMarkerTasks(thread); |
| 534 | thread->heap()->WaitForSweeperTasks(thread); |
| 535 | } |
| 536 | }; |
| 537 | #endif // TESTING |
| 538 | |
| 539 | } // namespace dart |
| 540 | |
| 541 | #endif // RUNTIME_VM_HEAP_HEAP_H_ |
| 542 | |