OLD | NEW |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "vm/scavenger.h" | 5 #include "vm/scavenger.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <map> | 8 #include <map> |
9 #include <utility> | 9 #include <utility> |
10 | 10 |
11 #include "vm/dart.h" | 11 #include "vm/dart.h" |
12 #include "vm/dart_api_state.h" | 12 #include "vm/dart_api_state.h" |
13 #include "vm/isolate.h" | 13 #include "vm/isolate.h" |
14 #include "vm/object.h" | 14 #include "vm/object.h" |
15 #include "vm/stack_frame.h" | 15 #include "vm/stack_frame.h" |
16 #include "vm/store_buffer.h" | 16 #include "vm/store_buffer.h" |
| 17 #include "vm/thread.h" |
17 #include "vm/verifier.h" | 18 #include "vm/verifier.h" |
18 #include "vm/visitor.h" | 19 #include "vm/visitor.h" |
19 #include "vm/weak_table.h" | 20 #include "vm/weak_table.h" |
20 #include "vm/object_id_ring.h" | 21 #include "vm/object_id_ring.h" |
21 | 22 |
22 namespace dart { | 23 namespace dart { |
23 | 24 |
24 DEFINE_FLAG(int, early_tenuring_threshold, 66, "Skip TO space when promoting" | 25 DEFINE_FLAG(int, early_tenuring_threshold, 66, "Skip TO space when promoting" |
25 " above this percentage."); | 26 " above this percentage."); |
26 | 27 |
(...skipping 251 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
278 bool prologue_weak_were_strong_; | 279 bool prologue_weak_were_strong_; |
279 | 280 |
280 DISALLOW_COPY_AND_ASSIGN(ScavengerWeakVisitor); | 281 DISALLOW_COPY_AND_ASSIGN(ScavengerWeakVisitor); |
281 }; | 282 }; |
282 | 283 |
283 | 284 |
284 // Visitor used to verify that all old->new references have been added to the | 285 // Visitor used to verify that all old->new references have been added to the |
285 // StoreBuffers. | 286 // StoreBuffers. |
286 class VerifyStoreBufferPointerVisitor : public ObjectPointerVisitor { | 287 class VerifyStoreBufferPointerVisitor : public ObjectPointerVisitor { |
287 public: | 288 public: |
288 VerifyStoreBufferPointerVisitor(Isolate* isolate, MemoryRegion* to) | 289 VerifyStoreBufferPointerVisitor(Isolate* isolate, |
| 290 const SemiSpace* to) |
289 : ObjectPointerVisitor(isolate), to_(to) {} | 291 : ObjectPointerVisitor(isolate), to_(to) {} |
290 | 292 |
291 void VisitPointers(RawObject** first, RawObject** last) { | 293 void VisitPointers(RawObject** first, RawObject** last) { |
292 for (RawObject** current = first; current <= last; current++) { | 294 for (RawObject** current = first; current <= last; current++) { |
293 RawObject* obj = *current; | 295 RawObject* obj = *current; |
294 if (obj->IsHeapObject() && obj->IsNewObject()) { | 296 if (obj->IsHeapObject() && obj->IsNewObject()) { |
295 ASSERT(to_->Contains(RawObject::ToAddr(obj))); | 297 ASSERT(to_->Contains(RawObject::ToAddr(obj))); |
296 } | 298 } |
297 } | 299 } |
298 } | 300 } |
299 | 301 |
300 private: | 302 private: |
301 MemoryRegion* to_; | 303 const SemiSpace* to_; |
302 | 304 |
303 DISALLOW_COPY_AND_ASSIGN(VerifyStoreBufferPointerVisitor); | 305 DISALLOW_COPY_AND_ASSIGN(VerifyStoreBufferPointerVisitor); |
304 }; | 306 }; |
305 | 307 |
306 | 308 |
| 309 SemiSpace::SemiSpace(VirtualMemory* reserved) |
| 310 : reserved_(reserved), region_(NULL, 0) { |
| 311 if (reserved != NULL) { |
| 312 region_ = MemoryRegion(reserved_->address(), reserved_->size()); |
| 313 } |
| 314 } |
| 315 |
| 316 |
| 317 SemiSpace::~SemiSpace() { |
| 318 if (reserved_ != NULL) { |
| 319 #if defined(DEBUG) |
| 320 memset(reserved_->address(), 0xf3, size()); |
| 321 #endif // defined(DEBUG) |
| 322 delete reserved_; |
| 323 } |
| 324 } |
| 325 |
| 326 |
| 327 Mutex* SemiSpace::mutex_ = NULL; |
| 328 SemiSpace* SemiSpace::cache_ = NULL; |
| 329 |
| 330 |
| 331 void SemiSpace::InitOnce() { |
| 332 ASSERT(mutex_ == NULL); |
| 333 mutex_ = new Mutex(); |
| 334 ASSERT(mutex_ != NULL); |
| 335 } |
| 336 |
| 337 |
| 338 SemiSpace* SemiSpace::New(intptr_t size) { |
| 339 { |
| 340 MutexLocker locker(mutex_); |
| 341 if (cache_ != NULL && cache_->size() == size) { |
| 342 SemiSpace* result = cache_; |
| 343 cache_ = NULL; |
| 344 return result; |
| 345 } |
| 346 } |
| 347 if (size == 0) { |
| 348 return new SemiSpace(NULL); |
| 349 } else { |
| 350 VirtualMemory* reserved = VirtualMemory::Reserve(size); |
| 351 if ((reserved == NULL) || !reserved->Commit(VirtualMemory::kReadWrite)) { |
| 352 // TODO(koda): If cache_ is not empty, we could try to delete it. |
| 353 delete reserved; |
| 354 return NULL; |
| 355 } |
| 356 #if defined(DEBUG) |
| 357 memset(reserved->address(), 0xf3, size); |
| 358 #endif // defined(DEBUG) |
| 359 return new SemiSpace(reserved); |
| 360 } |
| 361 } |
| 362 |
| 363 |
| 364 void SemiSpace::Delete() { |
| 365 SemiSpace* old_cache = NULL; |
| 366 { |
| 367 MutexLocker locker(mutex_); |
| 368 old_cache = cache_; |
| 369 cache_ = this; |
| 370 } |
| 371 delete old_cache; |
| 372 } |
| 373 |
| 374 |
| 375 void SemiSpace::WriteProtect(bool read_only) { |
| 376 if (reserved_ != NULL) { |
| 377 bool success = reserved_->Protect( |
| 378 read_only ? VirtualMemory::kReadOnly : VirtualMemory::kReadWrite); |
| 379 ASSERT(success); |
| 380 } |
| 381 } |
| 382 |
| 383 |
307 Scavenger::Scavenger(Heap* heap, | 384 Scavenger::Scavenger(Heap* heap, |
308 intptr_t max_capacity_in_words, | 385 intptr_t max_capacity_in_words, |
309 uword object_alignment) | 386 uword object_alignment) |
310 : heap_(heap), | 387 : heap_(heap), |
311 object_alignment_(object_alignment), | 388 object_alignment_(object_alignment), |
312 scavenging_(false), | 389 scavenging_(false), |
313 gc_time_micros_(0), | 390 gc_time_micros_(0), |
314 collections_(0), | 391 collections_(0), |
315 external_size_(0) { | 392 external_size_(0) { |
316 // Verify assumptions about the first word in objects which the scavenger is | 393 // Verify assumptions about the first word in objects which the scavenger is |
317 // going to use for forwarding pointers. | 394 // going to use for forwarding pointers. |
318 ASSERT(Object::tags_offset() == 0); | 395 ASSERT(Object::tags_offset() == 0); |
319 | 396 |
320 if (max_capacity_in_words == 0) { | 397 const intptr_t semi_space_size = (max_capacity_in_words / 2) * kWordSize; |
321 space_ = NULL; | 398 to_ = SemiSpace::New(semi_space_size); |
322 to_ = new MemoryRegion(NULL, 0); | 399 if (to_ == NULL) { |
323 from_ = new MemoryRegion(NULL, 0); | 400 FATAL("Out of memory.\n"); |
324 } else { | |
325 // Allocate the virtual memory for this scavenge heap. | |
326 space_ = VirtualMemory::Reserve(max_capacity_in_words << kWordSizeLog2); | |
327 if (space_ == NULL) { | |
328 FATAL("Out of memory.\n"); | |
329 } | |
330 | |
331 // Allocate the entire space at the beginning. | |
332 space_->Commit(false); | |
333 | |
334 // Setup the semi spaces. | |
335 uword semi_space_size = space_->size() / 2; | |
336 ASSERT((semi_space_size & (VirtualMemory::PageSize() - 1)) == 0); | |
337 to_ = new MemoryRegion(space_->address(), semi_space_size); | |
338 uword middle = space_->start() + semi_space_size; | |
339 from_ = new MemoryRegion(reinterpret_cast<void*>(middle), semi_space_size); | |
340 } | 401 } |
341 | 402 from_ = NULL; |
342 // Make sure that the two semi-spaces are aligned properly. | |
343 ASSERT(Utils::IsAligned(to_->start(), kObjectAlignment)); | |
344 ASSERT(Utils::IsAligned(from_->start(), kObjectAlignment)); | |
345 | 403 |
346 // Setup local fields. | 404 // Setup local fields. |
347 top_ = FirstObjectStart(); | 405 top_ = FirstObjectStart(); |
348 resolved_top_ = top_; | 406 resolved_top_ = top_; |
349 end_ = to_->end(); | 407 end_ = to_->end(); |
350 | 408 |
351 survivor_end_ = FirstObjectStart(); | 409 survivor_end_ = FirstObjectStart(); |
352 | |
353 #if defined(DEBUG) | |
354 memset(to_->pointer(), 0xf3, to_->size()); | |
355 memset(from_->pointer(), 0xf3, from_->size()); | |
356 #endif // defined(DEBUG) | |
357 } | 410 } |
358 | 411 |
359 | 412 |
360 Scavenger::~Scavenger() { | 413 Scavenger::~Scavenger() { |
361 delete to_; | 414 ASSERT(!scavenging_); |
362 delete from_; | 415 ASSERT(from_ == NULL); |
363 delete space_; | 416 to_->Delete(); |
364 } | 417 } |
365 | 418 |
366 | 419 |
367 void Scavenger::Prologue(Isolate* isolate, bool invoke_api_callbacks) { | 420 void Scavenger::Prologue(Isolate* isolate, bool invoke_api_callbacks) { |
368 if (invoke_api_callbacks && (isolate->gc_prologue_callback() != NULL)) { | 421 if (invoke_api_callbacks && (isolate->gc_prologue_callback() != NULL)) { |
369 (isolate->gc_prologue_callback())(); | 422 (isolate->gc_prologue_callback())(); |
370 } | 423 } |
371 // Flip the two semi-spaces so that to_ is always the space for allocating | 424 // Flip the two semi-spaces so that to_ is always the space for allocating |
372 // objects. | 425 // objects. |
373 MemoryRegion* temp = from_; | |
374 from_ = to_; | 426 from_ = to_; |
375 to_ = temp; | 427 to_ = SemiSpace::New(from_->size()); |
| 428 if (to_ == NULL) { |
| 429 // TODO(koda): We could try to recover (collect old space, wait for another |
| 430 // isolate to finish scavenge, etc.). |
| 431 FATAL("Out of memory.\n"); |
| 432 } |
376 top_ = FirstObjectStart(); | 433 top_ = FirstObjectStart(); |
377 resolved_top_ = top_; | 434 resolved_top_ = top_; |
378 end_ = to_->end(); | 435 end_ = to_->end(); |
379 } | 436 } |
380 | 437 |
381 | 438 |
382 void Scavenger::Epilogue(Isolate* isolate, | 439 void Scavenger::Epilogue(Isolate* isolate, |
383 ScavengerVisitor* visitor, | 440 ScavengerVisitor* visitor, |
384 bool invoke_api_callbacks) { | 441 bool invoke_api_callbacks) { |
385 // All objects in the to space have been copied from the from space at this | 442 // All objects in the to space have been copied from the from space at this |
386 // moment. | 443 // moment. |
387 int promotion_ratio = static_cast<int>( | 444 int promotion_ratio = static_cast<int>( |
388 (static_cast<double>(visitor->bytes_promoted()) / | 445 (static_cast<double>(visitor->bytes_promoted()) / |
389 static_cast<double>(to_->size())) * 100.0); | 446 static_cast<double>(to_->size())) * 100.0); |
390 if (promotion_ratio < FLAG_early_tenuring_threshold) { | 447 if (promotion_ratio < FLAG_early_tenuring_threshold) { |
391 // Remember the limit to which objects have been copied. | 448 // Remember the limit to which objects have been copied. |
392 survivor_end_ = top_; | 449 survivor_end_ = top_; |
393 } else { | 450 } else { |
394 // Move survivor end to the end of the to_ space, making all surviving | 451 // Move survivor end to the end of the to_ space, making all surviving |
395 // objects candidates for promotion. | 452 // objects candidates for promotion. |
396 survivor_end_ = end_; | 453 survivor_end_ = end_; |
397 } | 454 } |
398 | 455 |
399 #if defined(DEBUG) | 456 #if defined(DEBUG) |
400 VerifyStoreBufferPointerVisitor verify_store_buffer_visitor(isolate, to_); | 457 VerifyStoreBufferPointerVisitor verify_store_buffer_visitor(isolate, to_); |
401 heap_->IterateOldPointers(&verify_store_buffer_visitor); | 458 heap_->IterateOldPointers(&verify_store_buffer_visitor); |
402 | |
403 memset(from_->pointer(), 0xf3, from_->size()); | |
404 #endif // defined(DEBUG) | 459 #endif // defined(DEBUG) |
| 460 from_->Delete(); |
| 461 from_ = NULL; |
405 if (invoke_api_callbacks && (isolate->gc_epilogue_callback() != NULL)) { | 462 if (invoke_api_callbacks && (isolate->gc_epilogue_callback() != NULL)) { |
406 (isolate->gc_epilogue_callback())(); | 463 (isolate->gc_epilogue_callback())(); |
407 } | 464 } |
408 } | 465 } |
409 | 466 |
410 | 467 |
411 void Scavenger::IterateStoreBuffers(Isolate* isolate, | 468 void Scavenger::IterateStoreBuffers(Isolate* isolate, |
412 ScavengerVisitor* visitor) { | 469 ScavengerVisitor* visitor) { |
413 StoreBuffer* buffer = isolate->store_buffer(); | 470 StoreBuffer* buffer = isolate->store_buffer(); |
414 heap_->RecordData(kStoreBufferEntries, buffer->Count()); | 471 heap_->RecordData(kStoreBufferEntries, buffer->Count()); |
(...skipping 317 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
732 OS::PrintErr(" done.\n"); | 789 OS::PrintErr(" done.\n"); |
733 } | 790 } |
734 | 791 |
735 // Done scavenging. Reset the marker. | 792 // Done scavenging. Reset the marker. |
736 ASSERT(scavenging_); | 793 ASSERT(scavenging_); |
737 scavenging_ = false; | 794 scavenging_ = false; |
738 } | 795 } |
739 | 796 |
740 | 797 |
741 void Scavenger::WriteProtect(bool read_only) { | 798 void Scavenger::WriteProtect(bool read_only) { |
742 if (space_ != NULL) { | 799 ASSERT(!scavenging_); |
743 space_->Protect( | 800 ASSERT(from_ == NULL); |
744 read_only ? VirtualMemory::kReadOnly : VirtualMemory::kReadWrite); | 801 to_->WriteProtect(read_only); |
745 } | |
746 } | 802 } |
747 | 803 |
748 | 804 |
749 void Scavenger::PrintToJSONObject(JSONObject* object) { | 805 void Scavenger::PrintToJSONObject(JSONObject* object) { |
750 JSONObject space(object, "new"); | 806 JSONObject space(object, "new"); |
751 space.AddProperty("type", "@Scavenger"); | 807 space.AddProperty("type", "@Scavenger"); |
752 space.AddProperty("id", "heaps/new"); | 808 space.AddProperty("id", "heaps/new"); |
753 space.AddProperty("name", "Scavenger"); | 809 space.AddProperty("name", "Scavenger"); |
754 space.AddProperty("user_name", "new"); | 810 space.AddProperty("user_name", "new"); |
755 space.AddProperty("collections", collections()); | 811 space.AddProperty("collections", collections()); |
(...skipping 10 matching lines...) Expand all Loading... |
766 } | 822 } |
767 | 823 |
768 | 824 |
769 void Scavenger::FreeExternal(intptr_t size) { | 825 void Scavenger::FreeExternal(intptr_t size) { |
770 ASSERT(size >= 0); | 826 ASSERT(size >= 0); |
771 external_size_ -= size; | 827 external_size_ -= size; |
772 ASSERT(external_size_ >= 0); | 828 ASSERT(external_size_ >= 0); |
773 } | 829 } |
774 | 830 |
775 } // namespace dart | 831 } // namespace dart |
OLD | NEW |