1 // Copyright 2011 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/v8.h" 6 7 #include "src/full-codegen.h" 8 #include "src/macro-assembler.h" 9 #include "src/mark-compact.h" 10 #include "src/msan.h" 11 #include "src/platform.h" 12 13 namespace v8 { 14 namespace internal { 15 16 17 // ---------------------------------------------------------------------------- 18 // HeapObjectIterator 19 20 HeapObjectIterator::HeapObjectIterator(PagedSpace* space) { 21 // You can't actually iterate over the anchor page. It is not a real page, 22 // just an anchor for the double linked page list. Initialize as if we have 23 // reached the end of the anchor page, then the first iteration will move on 24 // to the first page. 25 Initialize(space, 26 NULL, 27 NULL, 28 kAllPagesInSpace, 29 NULL); 30 } 31 32 33 HeapObjectIterator::HeapObjectIterator(PagedSpace* space, 34 HeapObjectCallback size_func) { 35 // You can't actually iterate over the anchor page. It is not a real page, 36 // just an anchor for the double linked page list. Initialize the current 37 // address and end as NULL, then the first iteration will move on 38 // to the first page. 39 Initialize(space, 40 NULL, 41 NULL, 42 kAllPagesInSpace, 43 size_func); 44 } 45 46 47 HeapObjectIterator::HeapObjectIterator(Page* page, 48 HeapObjectCallback size_func) { 49 Space* owner = page->owner(); 50 ASSERT(owner == page->heap()->old_pointer_space() || 51 owner == page->heap()->old_data_space() || 52 owner == page->heap()->map_space() || 53 owner == page->heap()->cell_space() || 54 owner == page->heap()->property_cell_space() || 55 owner == page->heap()->code_space()); 56 Initialize(reinterpret_cast<PagedSpace*>(owner), 57 page->area_start(), 58 page->area_end(), 59 kOnePageOnly, 60 size_func); 61 ASSERT(page->WasSweptPrecisely()); 62 } 63 64 65 void HeapObjectIterator::Initialize(PagedSpace* space, 66 Address cur, Address end, 67 HeapObjectIterator::PageMode mode, 68 HeapObjectCallback size_f) { 69 // Check that we actually can iterate this space. 70 ASSERT(!space->was_swept_conservatively()); 71 72 space_ = space; 73 cur_addr_ = cur; 74 cur_end_ = end; 75 page_mode_ = mode; 76 size_func_ = size_f; 77 } 78 79 80 // We have hit the end of the page and should advance to the next block of 81 // objects. This happens at the end of the page. 82 bool HeapObjectIterator::AdvanceToNextPage() { 83 ASSERT(cur_addr_ == cur_end_); 84 if (page_mode_ == kOnePageOnly) return false; 85 Page* cur_page; 86 if (cur_addr_ == NULL) { 87 cur_page = space_->anchor(); 88 } else { 89 cur_page = Page::FromAddress(cur_addr_ - 1); 90 ASSERT(cur_addr_ == cur_page->area_end()); 91 } 92 cur_page = cur_page->next_page(); 93 if (cur_page == space_->anchor()) return false; 94 cur_addr_ = cur_page->area_start(); 95 cur_end_ = cur_page->area_end(); 96 ASSERT(cur_page->WasSweptPrecisely()); 97 return true; 98 } 99 100 101 // ----------------------------------------------------------------------------- 102 // CodeRange 103 104 105 CodeRange::CodeRange(Isolate* isolate) 106 : isolate_(isolate), 107 code_range_(NULL), 108 free_list_(0), 109 allocation_list_(0), 110 current_allocation_block_index_(0) { 111 } 112 113 114 bool CodeRange::SetUp(size_t requested) { 115 ASSERT(code_range_ == NULL); 116 117 if (requested == 0) { 118 // When a target requires the code range feature, we put all code objects 119 // in a kMaximalCodeRangeSize range of virtual address space, so that 120 // they can call each other with near calls. 121 if (kRequiresCodeRange) { 122 requested = kMaximalCodeRangeSize; 123 } else { 124 return true; 125 } 126 } 127 128 ASSERT(!kRequiresCodeRange || requested <= kMaximalCodeRangeSize); 129 code_range_ = new VirtualMemory(requested); 130 CHECK(code_range_ != NULL); 131 if (!code_range_->IsReserved()) { 132 delete code_range_; 133 code_range_ = NULL; 134 return false; 135 } 136 137 // We are sure that we have mapped a block of requested addresses. 138 ASSERT(code_range_->size() == requested); 139 LOG(isolate_, 140 NewEvent("CodeRange", code_range_->address(), requested)); 141 Address base = reinterpret_cast<Address>(code_range_->address()); 142 Address aligned_base = 143 RoundUp(reinterpret_cast<Address>(code_range_->address()), 144 MemoryChunk::kAlignment); 145 size_t size = code_range_->size() - (aligned_base - base); 146 allocation_list_.Add(FreeBlock(aligned_base, size)); 147 current_allocation_block_index_ = 0; 148 return true; 149 } 150 151 152 int CodeRange::CompareFreeBlockAddress(const FreeBlock* left, 153 const FreeBlock* right) { 154 // The entire point of CodeRange is that the difference between two 155 // addresses in the range can be represented as a signed 32-bit int, 156 // so the cast is semantically correct. 157 return static_cast<int>(left->start - right->start); 158 } 159 160 161 bool CodeRange::GetNextAllocationBlock(size_t requested) { 162 for (current_allocation_block_index_++; 163 current_allocation_block_index_ < allocation_list_.length(); 164 current_allocation_block_index_++) { 165 if (requested <= allocation_list_[current_allocation_block_index_].size) { 166 return true; // Found a large enough allocation block. 167 } 168 } 169 170 // Sort and merge the free blocks on the free list and the allocation list. 171 free_list_.AddAll(allocation_list_); 172 allocation_list_.Clear(); 173 free_list_.Sort(&CompareFreeBlockAddress); 174 for (int i = 0; i < free_list_.length();) { 175 FreeBlock merged = free_list_[i]; 176 i++; 177 // Add adjacent free blocks to the current merged block. 178 while (i < free_list_.length() && 179 free_list_[i].start == merged.start + merged.size) { 180 merged.size += free_list_[i].size; 181 i++; 182 } 183 if (merged.size > 0) { 184 allocation_list_.Add(merged); 185 } 186 } 187 free_list_.Clear(); 188 189 for (current_allocation_block_index_ = 0; 190 current_allocation_block_index_ < allocation_list_.length(); 191 current_allocation_block_index_++) { 192 if (requested <= allocation_list_[current_allocation_block_index_].size) { 193 return true; // Found a large enough allocation block. 194 } 195 } 196 current_allocation_block_index_ = 0; 197 // Code range is full or too fragmented. 198 return false; 199 } 200 201 202 Address CodeRange::AllocateRawMemory(const size_t requested_size, 203 const size_t commit_size, 204 size_t* allocated) { 205 ASSERT(commit_size <= requested_size); 206 ASSERT(current_allocation_block_index_ < allocation_list_.length()); 207 if (requested_size > allocation_list_[current_allocation_block_index_].size) { 208 // Find an allocation block large enough. 209 if (!GetNextAllocationBlock(requested_size)) return NULL; 210 } 211 // Commit the requested memory at the start of the current allocation block. 212 size_t aligned_requested = RoundUp(requested_size, MemoryChunk::kAlignment); 213 FreeBlock current = allocation_list_[current_allocation_block_index_]; 214 if (aligned_requested >= (current.size - Page::kPageSize)) { 215 // Don't leave a small free block, useless for a large object or chunk. 216 *allocated = current.size; 217 } else { 218 *allocated = aligned_requested; 219 } 220 ASSERT(*allocated <= current.size); 221 ASSERT(IsAddressAligned(current.start, MemoryChunk::kAlignment)); 222 if (!isolate_->memory_allocator()->CommitExecutableMemory(code_range_, 223 current.start, 224 commit_size, 225 *allocated)) { 226 *allocated = 0; 227 return NULL; 228 } 229 allocation_list_[current_allocation_block_index_].start += *allocated; 230 allocation_list_[current_allocation_block_index_].size -= *allocated; 231 if (*allocated == current.size) { 232 // This block is used up, get the next one. 233 if (!GetNextAllocationBlock(0)) return NULL; 234 } 235 return current.start; 236 } 237 238 239 bool CodeRange::CommitRawMemory(Address start, size_t length) { 240 return isolate_->memory_allocator()->CommitMemory(start, length, EXECUTABLE); 241 } 242 243 244 bool CodeRange::UncommitRawMemory(Address start, size_t length) { 245 return code_range_->Uncommit(start, length); 246 } 247 248 249 void CodeRange::FreeRawMemory(Address address, size_t length) { 250 ASSERT(IsAddressAligned(address, MemoryChunk::kAlignment)); 251 free_list_.Add(FreeBlock(address, length)); 252 code_range_->Uncommit(address, length); 253 } 254 255 256 void CodeRange::TearDown() { 257 delete code_range_; // Frees all memory in the virtual memory range. 258 code_range_ = NULL; 259 free_list_.Free(); 260 allocation_list_.Free(); 261 } 262 263 264 // ----------------------------------------------------------------------------- 265 // MemoryAllocator 266 // 267 268 MemoryAllocator::MemoryAllocator(Isolate* isolate) 269 : isolate_(isolate), 270 capacity_(0), 271 capacity_executable_(0), 272 size_(0), 273 size_executable_(0), 274 lowest_ever_allocated_(reinterpret_cast<void*>(-1)), 275 highest_ever_allocated_(reinterpret_cast<void*>(0)) { 276 } 277 278 279 bool MemoryAllocator::SetUp(intptr_t capacity, intptr_t capacity_executable) { 280 capacity_ = RoundUp(capacity, Page::kPageSize); 281 capacity_executable_ = RoundUp(capacity_executable, Page::kPageSize); 282 ASSERT_GE(capacity_, capacity_executable_); 283 284 size_ = 0; 285 size_executable_ = 0; 286 287 return true; 288 } 289 290 291 void MemoryAllocator::TearDown() { 292 // Check that spaces were torn down before MemoryAllocator. 293 ASSERT(size_ == 0); 294 // TODO(gc) this will be true again when we fix FreeMemory. 295 // ASSERT(size_executable_ == 0); 296 capacity_ = 0; 297 capacity_executable_ = 0; 298 } 299 300 301 bool MemoryAllocator::CommitMemory(Address base, 302 size_t size, 303 Executability executable) { 304 if (!VirtualMemory::CommitRegion(base, size, executable == EXECUTABLE)) { 305 return false; 306 } 307 UpdateAllocatedSpaceLimits(base, base + size); 308 return true; 309 } 310 311 312 void MemoryAllocator::FreeMemory(VirtualMemory* reservation, 313 Executability executable) { 314 // TODO(gc) make code_range part of memory allocator? 315 ASSERT(reservation->IsReserved()); 316 size_t size = reservation->size(); 317 ASSERT(size_ >= size); 318 size_ -= size; 319 320 isolate_->counters()->memory_allocated()->Decrement(static_cast<int>(size)); 321 322 if (executable == EXECUTABLE) { 323 ASSERT(size_executable_ >= size); 324 size_executable_ -= size; 325 } 326 // Code which is part of the code-range does not have its own VirtualMemory. 327 ASSERT(isolate_->code_range() == NULL || 328 !isolate_->code_range()->contains( 329 static_cast<Address>(reservation->address()))); 330 ASSERT(executable == NOT_EXECUTABLE || 331 isolate_->code_range() == NULL || 332 !isolate_->code_range()->valid()); 333 reservation->Release(); 334 } 335 336 337 void MemoryAllocator::FreeMemory(Address base, 338 size_t size, 339 Executability executable) { 340 // TODO(gc) make code_range part of memory allocator? 341 ASSERT(size_ >= size); 342 size_ -= size; 343 344 isolate_->counters()->memory_allocated()->Decrement(static_cast<int>(size)); 345 346 if (executable == EXECUTABLE) { 347 ASSERT(size_executable_ >= size); 348 size_executable_ -= size; 349 } 350 if (isolate_->code_range() != NULL && 351 isolate_->code_range()->contains(static_cast<Address>(base))) { 352 ASSERT(executable == EXECUTABLE); 353 isolate_->code_range()->FreeRawMemory(base, size); 354 } else { 355 ASSERT(executable == NOT_EXECUTABLE || 356 isolate_->code_range() == NULL || 357 !isolate_->code_range()->valid()); 358 bool result = VirtualMemory::ReleaseRegion(base, size); 359 USE(result); 360 ASSERT(result); 361 } 362 } 363 364 365 Address MemoryAllocator::ReserveAlignedMemory(size_t size, 366 size_t alignment, 367 VirtualMemory* controller) { 368 VirtualMemory reservation(size, alignment); 369 370 if (!reservation.IsReserved()) return NULL; 371 size_ += reservation.size(); 372 Address base = RoundUp(static_cast<Address>(reservation.address()), 373 alignment); 374 controller->TakeControl(&reservation); 375 return base; 376 } 377 378 379 Address MemoryAllocator::AllocateAlignedMemory(size_t reserve_size, 380 size_t commit_size, 381 size_t alignment, 382 Executability executable, 383 VirtualMemory* controller) { 384 ASSERT(commit_size <= reserve_size); 385 VirtualMemory reservation; 386 Address base = ReserveAlignedMemory(reserve_size, alignment, &reservation); 387 if (base == NULL) return NULL; 388 389 if (executable == EXECUTABLE) { 390 if (!CommitExecutableMemory(&reservation, 391 base, 392 commit_size, 393 reserve_size)) { 394 base = NULL; 395 } 396 } else { 397 if (reservation.Commit(base, commit_size, false)) { 398 UpdateAllocatedSpaceLimits(base, base + commit_size); 399 } else { 400 base = NULL; 401 } 402 } 403 404 if (base == NULL) { 405 // Failed to commit the body. Release the mapping and any partially 406 // commited regions inside it. 407 reservation.Release(); 408 return NULL; 409 } 410 411 controller->TakeControl(&reservation); 412 return base; 413 } 414 415 416 void Page::InitializeAsAnchor(PagedSpace* owner) { 417 set_owner(owner); 418 set_prev_page(this); 419 set_next_page(this); 420 } 421 422 423 NewSpacePage* NewSpacePage::Initialize(Heap* heap, 424 Address start, 425 SemiSpace* semi_space) { 426 Address area_start = start + NewSpacePage::kObjectStartOffset; 427 Address area_end = start + Page::kPageSize; 428 429 MemoryChunk* chunk = MemoryChunk::Initialize(heap, 430 start, 431 Page::kPageSize, 432 area_start, 433 area_end, 434 NOT_EXECUTABLE, 435 semi_space); 436 chunk->set_next_chunk(NULL); 437 chunk->set_prev_chunk(NULL); 438 chunk->initialize_scan_on_scavenge(true); 439 bool in_to_space = (semi_space->id() != kFromSpace); 440 chunk->SetFlag(in_to_space ? MemoryChunk::IN_TO_SPACE 441 : MemoryChunk::IN_FROM_SPACE); 442 ASSERT(!chunk->IsFlagSet(in_to_space ? MemoryChunk::IN_FROM_SPACE 443 : MemoryChunk::IN_TO_SPACE)); 444 NewSpacePage* page = static_cast<NewSpacePage*>(chunk); 445 heap->incremental_marking()->SetNewSpacePageFlags(page); 446 return page; 447 } 448 449 450 void NewSpacePage::InitializeAsAnchor(SemiSpace* semi_space) { 451 set_owner(semi_space); 452 set_next_chunk(this); 453 set_prev_chunk(this); 454 // Flags marks this invalid page as not being in new-space. 455 // All real new-space pages will be in new-space. 456 SetFlags(0, ~0); 457 } 458 459 460 MemoryChunk* MemoryChunk::Initialize(Heap* heap, 461 Address base, 462 size_t size, 463 Address area_start, 464 Address area_end, 465 Executability executable, 466 Space* owner) { 467 MemoryChunk* chunk = FromAddress(base); 468 469 ASSERT(base == chunk->address()); 470 471 chunk->heap_ = heap; 472 chunk->size_ = size; 473 chunk->area_start_ = area_start; 474 chunk->area_end_ = area_end; 475 chunk->flags_ = 0; 476 chunk->set_owner(owner); 477 chunk->InitializeReservedMemory(); 478 chunk->slots_buffer_ = NULL; 479 chunk->skip_list_ = NULL; 480 chunk->write_barrier_counter_ = kWriteBarrierCounterGranularity; 481 chunk->progress_bar_ = 0; 482 chunk->high_water_mark_ = static_cast<int>(area_start - base); 483 chunk->set_parallel_sweeping(PARALLEL_SWEEPING_DONE); 484 chunk->available_in_small_free_list_ = 0; 485 chunk->available_in_medium_free_list_ = 0; 486 chunk->available_in_large_free_list_ = 0; 487 chunk->available_in_huge_free_list_ = 0; 488 chunk->non_available_small_blocks_ = 0; 489 chunk->ResetLiveBytes(); 490 Bitmap::Clear(chunk); 491 chunk->initialize_scan_on_scavenge(false); 492 chunk->SetFlag(WAS_SWEPT_PRECISELY); 493 494 ASSERT(OFFSET_OF(MemoryChunk, flags_) == kFlagsOffset); 495 ASSERT(OFFSET_OF(MemoryChunk, live_byte_count_) == kLiveBytesOffset); 496 497 if (executable == EXECUTABLE) { 498 chunk->SetFlag(IS_EXECUTABLE); 499 } 500 501 if (owner == heap->old_data_space()) { 502 chunk->SetFlag(CONTAINS_ONLY_DATA); 503 } 504 505 return chunk; 506 } 507 508 509 // Commit MemoryChunk area to the requested size. 510 bool MemoryChunk::CommitArea(size_t requested) { 511 size_t guard_size = IsFlagSet(IS_EXECUTABLE) ? 512 MemoryAllocator::CodePageGuardSize() : 0; 513 size_t header_size = area_start() - address() - guard_size; 514 size_t commit_size = RoundUp(header_size + requested, OS::CommitPageSize()); 515 size_t committed_size = RoundUp(header_size + (area_end() - area_start()), 516 OS::CommitPageSize()); 517 518 if (commit_size > committed_size) { 519 // Commit size should be less or equal than the reserved size. 520 ASSERT(commit_size <= size() - 2 * guard_size); 521 // Append the committed area. 522 Address start = address() + committed_size + guard_size; 523 size_t length = commit_size - committed_size; 524 if (reservation_.IsReserved()) { 525 Executability executable = IsFlagSet(IS_EXECUTABLE) 526 ? EXECUTABLE : NOT_EXECUTABLE; 527 if (!heap()->isolate()->memory_allocator()->CommitMemory( 528 start, length, executable)) { 529 return false; 530 } 531 } else { 532 CodeRange* code_range = heap_->isolate()->code_range(); 533 ASSERT(code_range != NULL && code_range->valid() && 534 IsFlagSet(IS_EXECUTABLE)); 535 if (!code_range->CommitRawMemory(start, length)) return false; 536 } 537 538 if (Heap::ShouldZapGarbage()) { 539 heap_->isolate()->memory_allocator()->ZapBlock(start, length); 540 } 541 } else if (commit_size < committed_size) { 542 ASSERT(commit_size > 0); 543 // Shrink the committed area. 544 size_t length = committed_size - commit_size; 545 Address start = address() + committed_size + guard_size - length; 546 if (reservation_.IsReserved()) { 547 if (!reservation_.Uncommit(start, length)) return false; 548 } else { 549 CodeRange* code_range = heap_->isolate()->code_range(); 550 ASSERT(code_range != NULL && code_range->valid() && 551 IsFlagSet(IS_EXECUTABLE)); 552 if (!code_range->UncommitRawMemory(start, length)) return false; 553 } 554 } 555 556 area_end_ = area_start_ + requested; 557 return true; 558 } 559 560 561 void MemoryChunk::InsertAfter(MemoryChunk* other) { 562 MemoryChunk* other_next = other->next_chunk(); 563 564 set_next_chunk(other_next); 565 set_prev_chunk(other); 566 other_next->set_prev_chunk(this); 567 other->set_next_chunk(this); 568 } 569 570 571 void MemoryChunk::Unlink() { 572 MemoryChunk* next_element = next_chunk(); 573 MemoryChunk* prev_element = prev_chunk(); 574 next_element->set_prev_chunk(prev_element); 575 prev_element->set_next_chunk(next_element); 576 set_prev_chunk(NULL); 577 set_next_chunk(NULL); 578 } 579 580 581 MemoryChunk* MemoryAllocator::AllocateChunk(intptr_t reserve_area_size, 582 intptr_t commit_area_size, 583 Executability executable, 584 Space* owner) { 585 ASSERT(commit_area_size <= reserve_area_size); 586 587 size_t chunk_size; 588 Heap* heap = isolate_->heap(); 589 Address base = NULL; 590 VirtualMemory reservation; 591 Address area_start = NULL; 592 Address area_end = NULL; 593 594 // 595 // MemoryChunk layout: 596 // 597 // Executable 598 // +----------------------------+<- base aligned with MemoryChunk::kAlignment 599 // | Header | 600 // +----------------------------+<- base + CodePageGuardStartOffset 601 // | Guard | 602 // +----------------------------+<- area_start_ 603 // | Area | 604 // +----------------------------+<- area_end_ (area_start + commit_area_size) 605 // | Committed but not used | 606 // +----------------------------+<- aligned at OS page boundary 607 // | Reserved but not committed | 608 // +----------------------------+<- aligned at OS page boundary 609 // | Guard | 610 // +----------------------------+<- base + chunk_size 611 // 612 // Non-executable 613 // +----------------------------+<- base aligned with MemoryChunk::kAlignment 614 // | Header | 615 // +----------------------------+<- area_start_ (base + kObjectStartOffset) 616 // | Area | 617 // +----------------------------+<- area_end_ (area_start + commit_area_size) 618 // | Committed but not used | 619 // +----------------------------+<- aligned at OS page boundary 620 // | Reserved but not committed | 621 // +----------------------------+<- base + chunk_size 622 // 623 624 if (executable == EXECUTABLE) { 625 chunk_size = RoundUp(CodePageAreaStartOffset() + reserve_area_size, 626 OS::CommitPageSize()) + CodePageGuardSize(); 627 628 // Check executable memory limit. 629 if (size_executable_ + chunk_size > capacity_executable_) { 630 LOG(isolate_, 631 StringEvent("MemoryAllocator::AllocateRawMemory", 632 "V8 Executable Allocation capacity exceeded")); 633 return NULL; 634 } 635 636 // Size of header (not executable) plus area (executable). 637 size_t commit_size = RoundUp(CodePageGuardStartOffset() + commit_area_size, 638 OS::CommitPageSize()); 639 // Allocate executable memory either from code range or from the 640 // OS. 641 if (isolate_->code_range() != NULL && isolate_->code_range()->valid()) { 642 base = isolate_->code_range()->AllocateRawMemory(chunk_size, 643 commit_size, 644 &chunk_size); 645 ASSERT(IsAligned(reinterpret_cast<intptr_t>(base), 646 MemoryChunk::kAlignment)); 647 if (base == NULL) return NULL; 648 size_ += chunk_size; 649 // Update executable memory size. 650 size_executable_ += chunk_size; 651 } else { 652 base = AllocateAlignedMemory(chunk_size, 653 commit_size, 654 MemoryChunk::kAlignment, 655 executable, 656 &reservation); 657 if (base == NULL) return NULL; 658 // Update executable memory size. 659 size_executable_ += reservation.size(); 660 } 661 662 if (Heap::ShouldZapGarbage()) { 663 ZapBlock(base, CodePageGuardStartOffset()); 664 ZapBlock(base + CodePageAreaStartOffset(), commit_area_size); 665 } 666 667 area_start = base + CodePageAreaStartOffset(); 668 area_end = area_start + commit_area_size; 669 } else { 670 chunk_size = RoundUp(MemoryChunk::kObjectStartOffset + reserve_area_size, 671 OS::CommitPageSize()); 672 size_t commit_size = RoundUp(MemoryChunk::kObjectStartOffset + 673 commit_area_size, OS::CommitPageSize()); 674 base = AllocateAlignedMemory(chunk_size, 675 commit_size, 676 MemoryChunk::kAlignment, 677 executable, 678 &reservation); 679 680 if (base == NULL) return NULL; 681 682 if (Heap::ShouldZapGarbage()) { 683 ZapBlock(base, Page::kObjectStartOffset + commit_area_size); 684 } 685 686 area_start = base + Page::kObjectStartOffset; 687 area_end = area_start + commit_area_size; 688 } 689 690 // Use chunk_size for statistics and callbacks because we assume that they 691 // treat reserved but not-yet committed memory regions of chunks as allocated. 692 isolate_->counters()->memory_allocated()-> 693 Increment(static_cast<int>(chunk_size)); 694 695 LOG(isolate_, NewEvent("MemoryChunk", base, chunk_size)); 696 if (owner != NULL) { 697 ObjectSpace space = static_cast<ObjectSpace>(1 << owner->identity()); 698 PerformAllocationCallback(space, kAllocationActionAllocate, chunk_size); 699 } 700 701 MemoryChunk* result = MemoryChunk::Initialize(heap, 702 base, 703 chunk_size, 704 area_start, 705 area_end, 706 executable, 707 owner); 708 result->set_reserved_memory(&reservation); 709 MSAN_MEMORY_IS_INITIALIZED_IN_JIT(base, chunk_size); 710 return result; 711 } 712 713 714 void Page::ResetFreeListStatistics() { 715 non_available_small_blocks_ = 0; 716 available_in_small_free_list_ = 0; 717 available_in_medium_free_list_ = 0; 718 available_in_large_free_list_ = 0; 719 available_in_huge_free_list_ = 0; 720 } 721 722 723 Page* MemoryAllocator::AllocatePage(intptr_t size, 724 PagedSpace* owner, 725 Executability executable) { 726 MemoryChunk* chunk = AllocateChunk(size, size, executable, owner); 727 728 if (chunk == NULL) return NULL; 729 730 return Page::Initialize(isolate_->heap(), chunk, executable, owner); 731 } 732 733 734 LargePage* MemoryAllocator::AllocateLargePage(intptr_t object_size, 735 Space* owner, 736 Executability executable) { 737 MemoryChunk* chunk = AllocateChunk(object_size, 738 object_size, 739 executable, 740 owner); 741 if (chunk == NULL) return NULL; 742 return LargePage::Initialize(isolate_->heap(), chunk); 743 } 744 745 746 void MemoryAllocator::Free(MemoryChunk* chunk) { 747 LOG(isolate_, DeleteEvent("MemoryChunk", chunk)); 748 if (chunk->owner() != NULL) { 749 ObjectSpace space = 750 static_cast<ObjectSpace>(1 << chunk->owner()->identity()); 751 PerformAllocationCallback(space, kAllocationActionFree, chunk->size()); 752 } 753 754 isolate_->heap()->RememberUnmappedPage( 755 reinterpret_cast<Address>(chunk), chunk->IsEvacuationCandidate()); 756 757 delete chunk->slots_buffer(); 758 delete chunk->skip_list(); 759 760 VirtualMemory* reservation = chunk->reserved_memory(); 761 if (reservation->IsReserved()) { 762 FreeMemory(reservation, chunk->executable()); 763 } else { 764 FreeMemory(chunk->address(), 765 chunk->size(), 766 chunk->executable()); 767 } 768 } 769 770 771 bool MemoryAllocator::CommitBlock(Address start, 772 size_t size, 773 Executability executable) { 774 if (!CommitMemory(start, size, executable)) return false; 775 776 if (Heap::ShouldZapGarbage()) { 777 ZapBlock(start, size); 778 } 779 780 isolate_->counters()->memory_allocated()->Increment(static_cast<int>(size)); 781 return true; 782 } 783 784 785 bool MemoryAllocator::UncommitBlock(Address start, size_t size) { 786 if (!VirtualMemory::UncommitRegion(start, size)) return false; 787 isolate_->counters()->memory_allocated()->Decrement(static_cast<int>(size)); 788 return true; 789 } 790 791 792 void MemoryAllocator::ZapBlock(Address start, size_t size) { 793 for (size_t s = 0; s + kPointerSize <= size; s += kPointerSize) { 794 Memory::Address_at(start + s) = kZapValue; 795 } 796 } 797 798 799 void MemoryAllocator::PerformAllocationCallback(ObjectSpace space, 800 AllocationAction action, 801 size_t size) { 802 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { 803 MemoryAllocationCallbackRegistration registration = 804 memory_allocation_callbacks_[i]; 805 if ((registration.space & space) == space && 806 (registration.action & action) == action) 807 registration.callback(space, action, static_cast<int>(size)); 808 } 809 } 810 811 812 bool MemoryAllocator::MemoryAllocationCallbackRegistered( 813 MemoryAllocationCallback callback) { 814 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { 815 if (memory_allocation_callbacks_[i].callback == callback) return true; 816 } 817 return false; 818 } 819 820 821 void MemoryAllocator::AddMemoryAllocationCallback( 822 MemoryAllocationCallback callback, 823 ObjectSpace space, 824 AllocationAction action) { 825 ASSERT(callback != NULL); 826 MemoryAllocationCallbackRegistration registration(callback, space, action); 827 ASSERT(!MemoryAllocator::MemoryAllocationCallbackRegistered(callback)); 828 return memory_allocation_callbacks_.Add(registration); 829 } 830 831 832 void MemoryAllocator::RemoveMemoryAllocationCallback( 833 MemoryAllocationCallback callback) { 834 ASSERT(callback != NULL); 835 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { 836 if (memory_allocation_callbacks_[i].callback == callback) { 837 memory_allocation_callbacks_.Remove(i); 838 return; 839 } 840 } 841 UNREACHABLE(); 842 } 843 844 845 #ifdef DEBUG 846 void MemoryAllocator::ReportStatistics() { 847 float pct = static_cast<float>(capacity_ - size_) / capacity_; 848 PrintF(" capacity: %" V8_PTR_PREFIX "d" 849 ", used: %" V8_PTR_PREFIX "d" 850 ", available: %%%d\n\n", 851 capacity_, size_, static_cast<int>(pct*100)); 852 } 853 #endif 854 855 856 int MemoryAllocator::CodePageGuardStartOffset() { 857 // We are guarding code pages: the first OS page after the header 858 // will be protected as non-writable. 859 return RoundUp(Page::kObjectStartOffset, OS::CommitPageSize()); 860 } 861 862 863 int MemoryAllocator::CodePageGuardSize() { 864 return static_cast<int>(OS::CommitPageSize()); 865 } 866 867 868 int MemoryAllocator::CodePageAreaStartOffset() { 869 // We are guarding code pages: the first OS page after the header 870 // will be protected as non-writable. 871 return CodePageGuardStartOffset() + CodePageGuardSize(); 872 } 873 874 875 int MemoryAllocator::CodePageAreaEndOffset() { 876 // We are guarding code pages: the last OS page will be protected as 877 // non-writable. 878 return Page::kPageSize - static_cast<int>(OS::CommitPageSize()); 879 } 880 881 882 bool MemoryAllocator::CommitExecutableMemory(VirtualMemory* vm, 883 Address start, 884 size_t commit_size, 885 size_t reserved_size) { 886 // Commit page header (not executable). 887 if (!vm->Commit(start, 888 CodePageGuardStartOffset(), 889 false)) { 890 return false; 891 } 892 893 // Create guard page after the header. 894 if (!vm->Guard(start + CodePageGuardStartOffset())) { 895 return false; 896 } 897 898 // Commit page body (executable). 899 if (!vm->Commit(start + CodePageAreaStartOffset(), 900 commit_size - CodePageGuardStartOffset(), 901 true)) { 902 return false; 903 } 904 905 // Create guard page before the end. 906 if (!vm->Guard(start + reserved_size - CodePageGuardSize())) { 907 return false; 908 } 909 910 UpdateAllocatedSpaceLimits(start, 911 start + CodePageAreaStartOffset() + 912 commit_size - CodePageGuardStartOffset()); 913 return true; 914 } 915 916 917 // ----------------------------------------------------------------------------- 918 // MemoryChunk implementation 919 920 void MemoryChunk::IncrementLiveBytesFromMutator(Address address, int by) { 921 MemoryChunk* chunk = MemoryChunk::FromAddress(address); 922 if (!chunk->InNewSpace() && !static_cast<Page*>(chunk)->WasSwept()) { 923 static_cast<PagedSpace*>(chunk->owner())->IncrementUnsweptFreeBytes(-by); 924 } 925 chunk->IncrementLiveBytes(by); 926 } 927 928 929 // ----------------------------------------------------------------------------- 930 // PagedSpace implementation 931 932 PagedSpace::PagedSpace(Heap* heap, 933 intptr_t max_capacity, 934 AllocationSpace id, 935 Executability executable) 936 : Space(heap, id, executable), 937 free_list_(this), 938 was_swept_conservatively_(false), 939 unswept_free_bytes_(0), 940 end_of_unswept_pages_(NULL) { 941 if (id == CODE_SPACE) { 942 area_size_ = heap->isolate()->memory_allocator()-> 943 CodePageAreaSize(); 944 } else { 945 area_size_ = Page::kPageSize - Page::kObjectStartOffset; 946 } 947 max_capacity_ = (RoundDown(max_capacity, Page::kPageSize) / Page::kPageSize) 948 * AreaSize(); 949 accounting_stats_.Clear(); 950 951 allocation_info_.set_top(NULL); 952 allocation_info_.set_limit(NULL); 953 954 anchor_.InitializeAsAnchor(this); 955 } 956 957 958 bool PagedSpace::SetUp() { 959 return true; 960 } 961 962 963 bool PagedSpace::HasBeenSetUp() { 964 return true; 965 } 966 967 968 void PagedSpace::TearDown() { 969 PageIterator iterator(this); 970 while (iterator.has_next()) { 971 heap()->isolate()->memory_allocator()->Free(iterator.next()); 972 } 973 anchor_.set_next_page(&anchor_); 974 anchor_.set_prev_page(&anchor_); 975 accounting_stats_.Clear(); 976 } 977 978 979 size_t PagedSpace::CommittedPhysicalMemory() { 980 if (!VirtualMemory::HasLazyCommits()) return CommittedMemory(); 981 MemoryChunk::UpdateHighWaterMark(allocation_info_.top()); 982 size_t size = 0; 983 PageIterator it(this); 984 while (it.has_next()) { 985 size += it.next()->CommittedPhysicalMemory(); 986 } 987 return size; 988 } 989 990 991 Object* PagedSpace::FindObject(Address addr) { 992 // Note: this function can only be called on precisely swept spaces. 993 ASSERT(!heap()->mark_compact_collector()->in_use()); 994 995 if (!Contains(addr)) return Smi::FromInt(0); // Signaling not found. 996 997 Page* p = Page::FromAddress(addr); 998 HeapObjectIterator it(p, NULL); 999 for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { 1000 Address cur = obj->address(); 1001 Address next = cur + obj->Size(); 1002 if ((cur <= addr) && (addr < next)) return obj; 1003 } 1004 1005 UNREACHABLE(); 1006 return Smi::FromInt(0); 1007 } 1008 1009 1010 bool PagedSpace::CanExpand() { 1011 ASSERT(max_capacity_ % AreaSize() == 0); 1012 1013 if (Capacity() == max_capacity_) return false; 1014 1015 ASSERT(Capacity() < max_capacity_); 1016 1017 // Are we going to exceed capacity for this space? 1018 if ((Capacity() + Page::kPageSize) > max_capacity_) return false; 1019 1020 return true; 1021 } 1022 1023 1024 bool PagedSpace::Expand() { 1025 if (!CanExpand()) return false; 1026 1027 intptr_t size = AreaSize(); 1028 1029 if (anchor_.next_page() == &anchor_) { 1030 size = SizeOfFirstPage(); 1031 } 1032 1033 Page* p = heap()->isolate()->memory_allocator()->AllocatePage( 1034 size, this, executable()); 1035 if (p == NULL) return false; 1036 1037 ASSERT(Capacity() <= max_capacity_); 1038 1039 p->InsertAfter(anchor_.prev_page()); 1040 1041 return true; 1042 } 1043 1044 1045 intptr_t PagedSpace::SizeOfFirstPage() { 1046 int size = 0; 1047 switch (identity()) { 1048 case OLD_POINTER_SPACE: 1049 size = 96 * kPointerSize * KB; 1050 break; 1051 case OLD_DATA_SPACE: 1052 size = 192 * KB; 1053 break; 1054 case MAP_SPACE: 1055 size = 16 * kPointerSize * KB; 1056 break; 1057 case CELL_SPACE: 1058 size = 16 * kPointerSize * KB; 1059 break; 1060 case PROPERTY_CELL_SPACE: 1061 size = 8 * kPointerSize * KB; 1062 break; 1063 case CODE_SPACE: { 1064 CodeRange* code_range = heap()->isolate()->code_range(); 1065 if (code_range != NULL && code_range->valid()) { 1066 // When code range exists, code pages are allocated in a special way 1067 // (from the reserved code range). That part of the code is not yet 1068 // upgraded to handle small pages. 1069 size = AreaSize(); 1070 } else { 1071 size = RoundUp( 1072 480 * KB * FullCodeGenerator::kBootCodeSizeMultiplier / 100, 1073 kPointerSize); 1074 } 1075 break; 1076 } 1077 default: 1078 UNREACHABLE(); 1079 } 1080 return Min(size, AreaSize()); 1081 } 1082 1083 1084 int PagedSpace::CountTotalPages() { 1085 PageIterator it(this); 1086 int count = 0; 1087 while (it.has_next()) { 1088 it.next(); 1089 count++; 1090 } 1091 return count; 1092 } 1093 1094 1095 void PagedSpace::ObtainFreeListStatistics(Page* page, SizeStats* sizes) { 1096 sizes->huge_size_ = page->available_in_huge_free_list(); 1097 sizes->small_size_ = page->available_in_small_free_list(); 1098 sizes->medium_size_ = page->available_in_medium_free_list(); 1099 sizes->large_size_ = page->available_in_large_free_list(); 1100 } 1101 1102 1103 void PagedSpace::ResetFreeListStatistics() { 1104 PageIterator page_iterator(this); 1105 while (page_iterator.has_next()) { 1106 Page* page = page_iterator.next(); 1107 page->ResetFreeListStatistics(); 1108 } 1109 } 1110 1111 1112 void PagedSpace::IncreaseCapacity(int size) { 1113 accounting_stats_.ExpandSpace(size); 1114 } 1115 1116 1117 void PagedSpace::ReleasePage(Page* page) { 1118 ASSERT(page->LiveBytes() == 0); 1119 ASSERT(AreaSize() == page->area_size()); 1120 1121 if (page->WasSwept()) { 1122 intptr_t size = free_list_.EvictFreeListItems(page); 1123 accounting_stats_.AllocateBytes(size); 1124 ASSERT_EQ(AreaSize(), static_cast<int>(size)); 1125 } else { 1126 DecreaseUnsweptFreeBytes(page); 1127 } 1128 1129 if (page->IsFlagSet(MemoryChunk::SCAN_ON_SCAVENGE)) { 1130 heap()->decrement_scan_on_scavenge_pages(); 1131 page->ClearFlag(MemoryChunk::SCAN_ON_SCAVENGE); 1132 } 1133 1134 ASSERT(!free_list_.ContainsPageFreeListItems(page)); 1135 1136 if (Page::FromAllocationTop(allocation_info_.top()) == page) { 1137 allocation_info_.set_top(NULL); 1138 allocation_info_.set_limit(NULL); 1139 } 1140 1141 page->Unlink(); 1142 if (page->IsFlagSet(MemoryChunk::CONTAINS_ONLY_DATA)) { 1143 heap()->isolate()->memory_allocator()->Free(page); 1144 } else { 1145 heap()->QueueMemoryChunkForFree(page); 1146 } 1147 1148 ASSERT(Capacity() > 0); 1149 accounting_stats_.ShrinkSpace(AreaSize()); 1150 } 1151 1152 1153 #ifdef DEBUG 1154 void PagedSpace::Print() { } 1155 #endif 1156 1157 #ifdef VERIFY_HEAP 1158 void PagedSpace::Verify(ObjectVisitor* visitor) { 1159 // We can only iterate over the pages if they were swept precisely. 1160 if (was_swept_conservatively_) return; 1161 1162 bool allocation_pointer_found_in_space = 1163 (allocation_info_.top() == allocation_info_.limit()); 1164 PageIterator page_iterator(this); 1165 while (page_iterator.has_next()) { 1166 Page* page = page_iterator.next(); 1167 CHECK(page->owner() == this); 1168 if (page == Page::FromAllocationTop(allocation_info_.top())) { 1169 allocation_pointer_found_in_space = true; 1170 } 1171 CHECK(page->WasSweptPrecisely()); 1172 HeapObjectIterator it(page, NULL); 1173 Address end_of_previous_object = page->area_start(); 1174 Address top = page->area_end(); 1175 int black_size = 0; 1176 for (HeapObject* object = it.Next(); object != NULL; object = it.Next()) { 1177 CHECK(end_of_previous_object <= object->address()); 1178 1179 // The first word should be a map, and we expect all map pointers to 1180 // be in map space. 1181 Map* map = object->map(); 1182 CHECK(map->IsMap()); 1183 CHECK(heap()->map_space()->Contains(map)); 1184 1185 // Perform space-specific object verification. 1186 VerifyObject(object); 1187 1188 // The object itself should look OK. 1189 object->ObjectVerify(); 1190 1191 // All the interior pointers should be contained in the heap. 1192 int size = object->Size(); 1193 object->IterateBody(map->instance_type(), size, visitor); 1194 if (Marking::IsBlack(Marking::MarkBitFrom(object))) { 1195 black_size += size; 1196 } 1197 1198 CHECK(object->address() + size <= top); 1199 end_of_previous_object = object->address() + size; 1200 } 1201 CHECK_LE(black_size, page->LiveBytes()); 1202 } 1203 CHECK(allocation_pointer_found_in_space); 1204 } 1205 #endif // VERIFY_HEAP 1206 1207 // ----------------------------------------------------------------------------- 1208 // NewSpace implementation 1209 1210 1211 bool NewSpace::SetUp(int reserved_semispace_capacity, 1212 int maximum_semispace_capacity) { 1213 // Set up new space based on the preallocated memory block defined by 1214 // start and size. The provided space is divided into two semi-spaces. 1215 // To support fast containment testing in the new space, the size of 1216 // this chunk must be a power of two and it must be aligned to its size. 1217 int initial_semispace_capacity = heap()->InitialSemiSpaceSize(); 1218 1219 size_t size = 2 * reserved_semispace_capacity; 1220 Address base = 1221 heap()->isolate()->memory_allocator()->ReserveAlignedMemory( 1222 size, size, &reservation_); 1223 if (base == NULL) return false; 1224 1225 chunk_base_ = base; 1226 chunk_size_ = static_cast<uintptr_t>(size); 1227 LOG(heap()->isolate(), NewEvent("InitialChunk", chunk_base_, chunk_size_)); 1228 1229 ASSERT(initial_semispace_capacity <= maximum_semispace_capacity); 1230 ASSERT(IsPowerOf2(maximum_semispace_capacity)); 1231 1232 // Allocate and set up the histogram arrays if necessary. 1233 allocated_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1); 1234 promoted_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1); 1235 1236 #define SET_NAME(name) allocated_histogram_[name].set_name(#name); \ 1237 promoted_histogram_[name].set_name(#name); 1238 INSTANCE_TYPE_LIST(SET_NAME) 1239 #undef SET_NAME 1240 1241 ASSERT(reserved_semispace_capacity == heap()->ReservedSemiSpaceSize()); 1242 ASSERT(static_cast<intptr_t>(chunk_size_) >= 1243 2 * heap()->ReservedSemiSpaceSize()); 1244 ASSERT(IsAddressAligned(chunk_base_, 2 * reserved_semispace_capacity, 0)); 1245 1246 to_space_.SetUp(chunk_base_, 1247 initial_semispace_capacity, 1248 maximum_semispace_capacity); 1249 from_space_.SetUp(chunk_base_ + reserved_semispace_capacity, 1250 initial_semispace_capacity, 1251 maximum_semispace_capacity); 1252 if (!to_space_.Commit()) { 1253 return false; 1254 } 1255 ASSERT(!from_space_.is_committed()); // No need to use memory yet. 1256 1257 start_ = chunk_base_; 1258 address_mask_ = ~(2 * reserved_semispace_capacity - 1); 1259 object_mask_ = address_mask_ | kHeapObjectTagMask; 1260 object_expected_ = reinterpret_cast<uintptr_t>(start_) | kHeapObjectTag; 1261 1262 ResetAllocationInfo(); 1263 1264 return true; 1265 } 1266 1267 1268 void NewSpace::TearDown() { 1269 if (allocated_histogram_) { 1270 DeleteArray(allocated_histogram_); 1271 allocated_histogram_ = NULL; 1272 } 1273 if (promoted_histogram_) { 1274 DeleteArray(promoted_histogram_); 1275 promoted_histogram_ = NULL; 1276 } 1277 1278 start_ = NULL; 1279 allocation_info_.set_top(NULL); 1280 allocation_info_.set_limit(NULL); 1281 1282 to_space_.TearDown(); 1283 from_space_.TearDown(); 1284 1285 LOG(heap()->isolate(), DeleteEvent("InitialChunk", chunk_base_)); 1286 1287 ASSERT(reservation_.IsReserved()); 1288 heap()->isolate()->memory_allocator()->FreeMemory(&reservation_, 1289 NOT_EXECUTABLE); 1290 chunk_base_ = NULL; 1291 chunk_size_ = 0; 1292 } 1293 1294 1295 void NewSpace::Flip() { 1296 SemiSpace::Swap(&from_space_, &to_space_); 1297 } 1298 1299 1300 void NewSpace::Grow() { 1301 // Double the semispace size but only up to maximum capacity. 1302 ASSERT(Capacity() < MaximumCapacity()); 1303 int new_capacity = Min(MaximumCapacity(), 2 * static_cast<int>(Capacity())); 1304 if (to_space_.GrowTo(new_capacity)) { 1305 // Only grow from space if we managed to grow to-space. 1306 if (!from_space_.GrowTo(new_capacity)) { 1307 // If we managed to grow to-space but couldn't grow from-space, 1308 // attempt to shrink to-space. 1309 if (!to_space_.ShrinkTo(from_space_.Capacity())) { 1310 // We are in an inconsistent state because we could not 1311 // commit/uncommit memory from new space. 1312 V8::FatalProcessOutOfMemory("Failed to grow new space."); 1313 } 1314 } 1315 } 1316 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); 1317 } 1318 1319 1320 void NewSpace::Shrink() { 1321 int new_capacity = Max(InitialCapacity(), 2 * SizeAsInt()); 1322 int rounded_new_capacity = RoundUp(new_capacity, Page::kPageSize); 1323 if (rounded_new_capacity < Capacity() && 1324 to_space_.ShrinkTo(rounded_new_capacity)) { 1325 // Only shrink from-space if we managed to shrink to-space. 1326 from_space_.Reset(); 1327 if (!from_space_.ShrinkTo(rounded_new_capacity)) { 1328 // If we managed to shrink to-space but couldn't shrink from 1329 // space, attempt to grow to-space again. 1330 if (!to_space_.GrowTo(from_space_.Capacity())) { 1331 // We are in an inconsistent state because we could not 1332 // commit/uncommit memory from new space. 1333 V8::FatalProcessOutOfMemory("Failed to shrink new space."); 1334 } 1335 } 1336 } 1337 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); 1338 } 1339 1340 1341 void NewSpace::UpdateAllocationInfo() { 1342 MemoryChunk::UpdateHighWaterMark(allocation_info_.top()); 1343 allocation_info_.set_top(to_space_.page_low()); 1344 allocation_info_.set_limit(to_space_.page_high()); 1345 UpdateInlineAllocationLimit(0); 1346 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); 1347 } 1348 1349 1350 void NewSpace::ResetAllocationInfo() { 1351 to_space_.Reset(); 1352 UpdateAllocationInfo(); 1353 pages_used_ = 0; 1354 // Clear all mark-bits in the to-space. 1355 NewSpacePageIterator it(&to_space_); 1356 while (it.has_next()) { 1357 Bitmap::Clear(it.next()); 1358 } 1359 } 1360 1361 1362 void NewSpace::UpdateInlineAllocationLimit(int size_in_bytes) { 1363 if (heap()->inline_allocation_disabled()) { 1364 // Lowest limit when linear allocation was disabled. 1365 Address high = to_space_.page_high(); 1366 Address new_top = allocation_info_.top() + size_in_bytes; 1367 allocation_info_.set_limit(Min(new_top, high)); 1368 } else if (inline_allocation_limit_step() == 0) { 1369 // Normal limit is the end of the current page. 1370 allocation_info_.set_limit(to_space_.page_high()); 1371 } else { 1372 // Lower limit during incremental marking. 1373 Address high = to_space_.page_high(); 1374 Address new_top = allocation_info_.top() + size_in_bytes; 1375 Address new_limit = new_top + inline_allocation_limit_step_; 1376 allocation_info_.set_limit(Min(new_limit, high)); 1377 } 1378 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); 1379 } 1380 1381 1382 bool NewSpace::AddFreshPage() { 1383 Address top = allocation_info_.top(); 1384 if (NewSpacePage::IsAtStart(top)) { 1385 // The current page is already empty. Don't try to make another. 1386 1387 // We should only get here if someone asks to allocate more 1388 // than what can be stored in a single page. 1389 // TODO(gc): Change the limit on new-space allocation to prevent this 1390 // from happening (all such allocations should go directly to LOSpace). 1391 return false; 1392 } 1393 if (!to_space_.AdvancePage()) { 1394 // Failed to get a new page in to-space. 1395 return false; 1396 } 1397 1398 // Clear remainder of current page. 1399 Address limit = NewSpacePage::FromLimit(top)->area_end(); 1400 if (heap()->gc_state() == Heap::SCAVENGE) { 1401 heap()->promotion_queue()->SetNewLimit(limit); 1402 heap()->promotion_queue()->ActivateGuardIfOnTheSamePage(); 1403 } 1404 1405 int remaining_in_page = static_cast<int>(limit - top); 1406 heap()->CreateFillerObjectAt(top, remaining_in_page); 1407 pages_used_++; 1408 UpdateAllocationInfo(); 1409 1410 return true; 1411 } 1412 1413 1414 AllocationResult NewSpace::SlowAllocateRaw(int size_in_bytes) { 1415 Address old_top = allocation_info_.top(); 1416 Address high = to_space_.page_high(); 1417 if (allocation_info_.limit() < high) { 1418 // Either the limit has been lowered because linear allocation was disabled 1419 // or because incremental marking wants to get a chance to do a step. Set 1420 // the new limit accordingly. 1421 Address new_top = old_top + size_in_bytes; 1422 int bytes_allocated = static_cast<int>(new_top - top_on_previous_step_); 1423 heap()->incremental_marking()->Step( 1424 bytes_allocated, IncrementalMarking::GC_VIA_STACK_GUARD); 1425 UpdateInlineAllocationLimit(size_in_bytes); 1426 top_on_previous_step_ = new_top; 1427 return AllocateRaw(size_in_bytes); 1428 } else if (AddFreshPage()) { 1429 // Switched to new page. Try allocating again. 1430 int bytes_allocated = static_cast<int>(old_top - top_on_previous_step_); 1431 heap()->incremental_marking()->Step( 1432 bytes_allocated, IncrementalMarking::GC_VIA_STACK_GUARD); 1433 top_on_previous_step_ = to_space_.page_low(); 1434 return AllocateRaw(size_in_bytes); 1435 } else { 1436 return AllocationResult::Retry(); 1437 } 1438 } 1439 1440 1441 #ifdef VERIFY_HEAP 1442 // We do not use the SemiSpaceIterator because verification doesn't assume 1443 // that it works (it depends on the invariants we are checking). 1444 void NewSpace::Verify() { 1445 // The allocation pointer should be in the space or at the very end. 1446 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); 1447 1448 // There should be objects packed in from the low address up to the 1449 // allocation pointer. 1450 Address current = to_space_.first_page()->area_start(); 1451 CHECK_EQ(current, to_space_.space_start()); 1452 1453 while (current != top()) { 1454 if (!NewSpacePage::IsAtEnd(current)) { 1455 // The allocation pointer should not be in the middle of an object. 1456 CHECK(!NewSpacePage::FromLimit(current)->ContainsLimit(top()) || 1457 current < top()); 1458 1459 HeapObject* object = HeapObject::FromAddress(current); 1460 1461 // The first word should be a map, and we expect all map pointers to 1462 // be in map space. 1463 Map* map = object->map(); 1464 CHECK(map->IsMap()); 1465 CHECK(heap()->map_space()->Contains(map)); 1466 1467 // The object should not be code or a map. 1468 CHECK(!object->IsMap()); 1469 CHECK(!object->IsCode()); 1470 1471 // The object itself should look OK. 1472 object->ObjectVerify(); 1473 1474 // All the interior pointers should be contained in the heap. 1475 VerifyPointersVisitor visitor; 1476 int size = object->Size(); 1477 object->IterateBody(map->instance_type(), size, &visitor); 1478 1479 current += size; 1480 } else { 1481 // At end of page, switch to next page. 1482 NewSpacePage* page = NewSpacePage::FromLimit(current)->next_page(); 1483 // Next page should be valid. 1484 CHECK(!page->is_anchor()); 1485 current = page->area_start(); 1486 } 1487 } 1488 1489 // Check semi-spaces. 1490 CHECK_EQ(from_space_.id(), kFromSpace); 1491 CHECK_EQ(to_space_.id(), kToSpace); 1492 from_space_.Verify(); 1493 to_space_.Verify(); 1494 } 1495 #endif 1496 1497 // ----------------------------------------------------------------------------- 1498 // SemiSpace implementation 1499 1500 void SemiSpace::SetUp(Address start, 1501 int initial_capacity, 1502 int maximum_capacity) { 1503 // Creates a space in the young generation. The constructor does not 1504 // allocate memory from the OS. A SemiSpace is given a contiguous chunk of 1505 // memory of size 'capacity' when set up, and does not grow or shrink 1506 // otherwise. In the mark-compact collector, the memory region of the from 1507 // space is used as the marking stack. It requires contiguous memory 1508 // addresses. 1509 ASSERT(maximum_capacity >= Page::kPageSize); 1510 initial_capacity_ = RoundDown(initial_capacity, Page::kPageSize); 1511 capacity_ = initial_capacity; 1512 maximum_capacity_ = RoundDown(maximum_capacity, Page::kPageSize); 1513 maximum_committed_ = 0; 1514 committed_ = false; 1515 start_ = start; 1516 address_mask_ = ~(maximum_capacity - 1); 1517 object_mask_ = address_mask_ | kHeapObjectTagMask; 1518 object_expected_ = reinterpret_cast<uintptr_t>(start) | kHeapObjectTag; 1519 age_mark_ = start_; 1520 } 1521 1522 1523 void SemiSpace::TearDown() { 1524 start_ = NULL; 1525 capacity_ = 0; 1526 } 1527 1528 1529 bool SemiSpace::Commit() { 1530 ASSERT(!is_committed()); 1531 int pages = capacity_ / Page::kPageSize; 1532 if (!heap()->isolate()->memory_allocator()->CommitBlock(start_, 1533 capacity_, 1534 executable())) { 1535 return false; 1536 } 1537 1538 NewSpacePage* current = anchor(); 1539 for (int i = 0; i < pages; i++) { 1540 NewSpacePage* new_page = 1541 NewSpacePage::Initialize(heap(), start_ + i * Page::kPageSize, this); 1542 new_page->InsertAfter(current); 1543 current = new_page; 1544 } 1545 1546 SetCapacity(capacity_); 1547 committed_ = true; 1548 Reset(); 1549 return true; 1550 } 1551 1552 1553 bool SemiSpace::Uncommit() { 1554 ASSERT(is_committed()); 1555 Address start = start_ + maximum_capacity_ - capacity_; 1556 if (!heap()->isolate()->memory_allocator()->UncommitBlock(start, capacity_)) { 1557 return false; 1558 } 1559 anchor()->set_next_page(anchor()); 1560 anchor()->set_prev_page(anchor()); 1561 1562 committed_ = false; 1563 return true; 1564 } 1565 1566 1567 size_t SemiSpace::CommittedPhysicalMemory() { 1568 if (!is_committed()) return 0; 1569 size_t size = 0; 1570 NewSpacePageIterator it(this); 1571 while (it.has_next()) { 1572 size += it.next()->CommittedPhysicalMemory(); 1573 } 1574 return size; 1575 } 1576 1577 1578 bool SemiSpace::GrowTo(int new_capacity) { 1579 if (!is_committed()) { 1580 if (!Commit()) return false; 1581 } 1582 ASSERT((new_capacity & Page::kPageAlignmentMask) == 0); 1583 ASSERT(new_capacity <= maximum_capacity_); 1584 ASSERT(new_capacity > capacity_); 1585 int pages_before = capacity_ / Page::kPageSize; 1586 int pages_after = new_capacity / Page::kPageSize; 1587 1588 size_t delta = new_capacity - capacity_; 1589 1590 ASSERT(IsAligned(delta, OS::AllocateAlignment())); 1591 if (!heap()->isolate()->memory_allocator()->CommitBlock( 1592 start_ + capacity_, delta, executable())) { 1593 return false; 1594 } 1595 SetCapacity(new_capacity); 1596 NewSpacePage* last_page = anchor()->prev_page(); 1597 ASSERT(last_page != anchor()); 1598 for (int i = pages_before; i < pages_after; i++) { 1599 Address page_address = start_ + i * Page::kPageSize; 1600 NewSpacePage* new_page = NewSpacePage::Initialize(heap(), 1601 page_address, 1602 this); 1603 new_page->InsertAfter(last_page); 1604 Bitmap::Clear(new_page); 1605 // Duplicate the flags that was set on the old page. 1606 new_page->SetFlags(last_page->GetFlags(), 1607 NewSpacePage::kCopyOnFlipFlagsMask); 1608 last_page = new_page; 1609 } 1610 return true; 1611 } 1612 1613 1614 bool SemiSpace::ShrinkTo(int new_capacity) { 1615 ASSERT((new_capacity & Page::kPageAlignmentMask) == 0); 1616 ASSERT(new_capacity >= initial_capacity_); 1617 ASSERT(new_capacity < capacity_); 1618 if (is_committed()) { 1619 size_t delta = capacity_ - new_capacity; 1620 ASSERT(IsAligned(delta, OS::AllocateAlignment())); 1621 1622 MemoryAllocator* allocator = heap()->isolate()->memory_allocator(); 1623 if (!allocator->UncommitBlock(start_ + new_capacity, delta)) { 1624 return false; 1625 } 1626 1627 int pages_after = new_capacity / Page::kPageSize; 1628 NewSpacePage* new_last_page = 1629 NewSpacePage::FromAddress(start_ + (pages_after - 1) * Page::kPageSize); 1630 new_last_page->set_next_page(anchor()); 1631 anchor()->set_prev_page(new_last_page); 1632 ASSERT((current_page_ >= first_page()) && (current_page_ <= new_last_page)); 1633 } 1634 1635 SetCapacity(new_capacity); 1636 1637 return true; 1638 } 1639 1640 1641 void SemiSpace::FlipPages(intptr_t flags, intptr_t mask) { 1642 anchor_.set_owner(this); 1643 // Fixup back-pointers to anchor. Address of anchor changes 1644 // when we swap. 1645 anchor_.prev_page()->set_next_page(&anchor_); 1646 anchor_.next_page()->set_prev_page(&anchor_); 1647 1648 bool becomes_to_space = (id_ == kFromSpace); 1649 id_ = becomes_to_space ? kToSpace : kFromSpace; 1650 NewSpacePage* page = anchor_.next_page(); 1651 while (page != &anchor_) { 1652 page->set_owner(this); 1653 page->SetFlags(flags, mask); 1654 if (becomes_to_space) { 1655 page->ClearFlag(MemoryChunk::IN_FROM_SPACE); 1656 page->SetFlag(MemoryChunk::IN_TO_SPACE); 1657 page->ClearFlag(MemoryChunk::NEW_SPACE_BELOW_AGE_MARK); 1658 page->ResetLiveBytes(); 1659 } else { 1660 page->SetFlag(MemoryChunk::IN_FROM_SPACE); 1661 page->ClearFlag(MemoryChunk::IN_TO_SPACE); 1662 } 1663 ASSERT(page->IsFlagSet(MemoryChunk::SCAN_ON_SCAVENGE)); 1664 ASSERT(page->IsFlagSet(MemoryChunk::IN_TO_SPACE) || 1665 page->IsFlagSet(MemoryChunk::IN_FROM_SPACE)); 1666 page = page->next_page(); 1667 } 1668 } 1669 1670 1671 void SemiSpace::Reset() { 1672 ASSERT(anchor_.next_page() != &anchor_); 1673 current_page_ = anchor_.next_page(); 1674 } 1675 1676 1677 void SemiSpace::Swap(SemiSpace* from, SemiSpace* to) { 1678 // We won't be swapping semispaces without data in them. 1679 ASSERT(from->anchor_.next_page() != &from->anchor_); 1680 ASSERT(to->anchor_.next_page() != &to->anchor_); 1681 1682 // Swap bits. 1683 SemiSpace tmp = *from; 1684 *from = *to; 1685 *to = tmp; 1686 1687 // Fixup back-pointers to the page list anchor now that its address 1688 // has changed. 1689 // Swap to/from-space bits on pages. 1690 // Copy GC flags from old active space (from-space) to new (to-space). 1691 intptr_t flags = from->current_page()->GetFlags(); 1692 to->FlipPages(flags, NewSpacePage::kCopyOnFlipFlagsMask); 1693 1694 from->FlipPages(0, 0); 1695 } 1696 1697 1698 void SemiSpace::SetCapacity(int new_capacity) { 1699 capacity_ = new_capacity; 1700 if (capacity_ > maximum_committed_) { 1701 maximum_committed_ = capacity_; 1702 } 1703 } 1704 1705 1706 void SemiSpace::set_age_mark(Address mark) { 1707 ASSERT(NewSpacePage::FromLimit(mark)->semi_space() == this); 1708 age_mark_ = mark; 1709 // Mark all pages up to the one containing mark. 1710 NewSpacePageIterator it(space_start(), mark); 1711 while (it.has_next()) { 1712 it.next()->SetFlag(MemoryChunk::NEW_SPACE_BELOW_AGE_MARK); 1713 } 1714 } 1715 1716 1717 #ifdef DEBUG 1718 void SemiSpace::Print() { } 1719 #endif 1720 1721 #ifdef VERIFY_HEAP 1722 void SemiSpace::Verify() { 1723 bool is_from_space = (id_ == kFromSpace); 1724 NewSpacePage* page = anchor_.next_page(); 1725 CHECK(anchor_.semi_space() == this); 1726 while (page != &anchor_) { 1727 CHECK(page->semi_space() == this); 1728 CHECK(page->InNewSpace()); 1729 CHECK(page->IsFlagSet(is_from_space ? MemoryChunk::IN_FROM_SPACE 1730 : MemoryChunk::IN_TO_SPACE)); 1731 CHECK(!page->IsFlagSet(is_from_space ? MemoryChunk::IN_TO_SPACE 1732 : MemoryChunk::IN_FROM_SPACE)); 1733 CHECK(page->IsFlagSet(MemoryChunk::POINTERS_TO_HERE_ARE_INTERESTING)); 1734 if (!is_from_space) { 1735 // The pointers-from-here-are-interesting flag isn't updated dynamically 1736 // on from-space pages, so it might be out of sync with the marking state. 1737 if (page->heap()->incremental_marking()->IsMarking()) { 1738 CHECK(page->IsFlagSet(MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING)); 1739 } else { 1740 CHECK(!page->IsFlagSet( 1741 MemoryChunk::POINTERS_FROM_HERE_ARE_INTERESTING)); 1742 } 1743 // TODO(gc): Check that the live_bytes_count_ field matches the 1744 // black marking on the page (if we make it match in new-space). 1745 } 1746 CHECK(page->IsFlagSet(MemoryChunk::SCAN_ON_SCAVENGE)); 1747 CHECK(page->prev_page()->next_page() == page); 1748 page = page->next_page(); 1749 } 1750 } 1751 #endif 1752 1753 #ifdef DEBUG 1754 void SemiSpace::AssertValidRange(Address start, Address end) { 1755 // Addresses belong to same semi-space 1756 NewSpacePage* page = NewSpacePage::FromLimit(start); 1757 NewSpacePage* end_page = NewSpacePage::FromLimit(end); 1758 SemiSpace* space = page->semi_space(); 1759 CHECK_EQ(space, end_page->semi_space()); 1760 // Start address is before end address, either on same page, 1761 // or end address is on a later page in the linked list of 1762 // semi-space pages. 1763 if (page == end_page) { 1764 CHECK(start <= end); 1765 } else { 1766 while (page != end_page) { 1767 page = page->next_page(); 1768 CHECK_NE(page, space->anchor()); 1769 } 1770 } 1771 } 1772 #endif 1773 1774 1775 // ----------------------------------------------------------------------------- 1776 // SemiSpaceIterator implementation. 1777 SemiSpaceIterator::SemiSpaceIterator(NewSpace* space) { 1778 Initialize(space->bottom(), space->top(), NULL); 1779 } 1780 1781 1782 SemiSpaceIterator::SemiSpaceIterator(NewSpace* space, 1783 HeapObjectCallback size_func) { 1784 Initialize(space->bottom(), space->top(), size_func); 1785 } 1786 1787 1788 SemiSpaceIterator::SemiSpaceIterator(NewSpace* space, Address start) { 1789 Initialize(start, space->top(), NULL); 1790 } 1791 1792 1793 SemiSpaceIterator::SemiSpaceIterator(Address from, Address to) { 1794 Initialize(from, to, NULL); 1795 } 1796 1797 1798 void SemiSpaceIterator::Initialize(Address start, 1799 Address end, 1800 HeapObjectCallback size_func) { 1801 SemiSpace::AssertValidRange(start, end); 1802 current_ = start; 1803 limit_ = end; 1804 size_func_ = size_func; 1805 } 1806 1807 1808 #ifdef DEBUG 1809 // heap_histograms is shared, always clear it before using it. 1810 static void ClearHistograms(Isolate* isolate) { 1811 // We reset the name each time, though it hasn't changed. 1812 #define DEF_TYPE_NAME(name) isolate->heap_histograms()[name].set_name(#name); 1813 INSTANCE_TYPE_LIST(DEF_TYPE_NAME) 1814 #undef DEF_TYPE_NAME 1815 1816 #define CLEAR_HISTOGRAM(name) isolate->heap_histograms()[name].clear(); 1817 INSTANCE_TYPE_LIST(CLEAR_HISTOGRAM) 1818 #undef CLEAR_HISTOGRAM 1819 1820 isolate->js_spill_information()->Clear(); 1821 } 1822 1823 1824 static void ClearCodeKindStatistics(int* code_kind_statistics) { 1825 for (int i = 0; i < Code::NUMBER_OF_KINDS; i++) { 1826 code_kind_statistics[i] = 0; 1827 } 1828 } 1829 1830 1831 static void ReportCodeKindStatistics(int* code_kind_statistics) { 1832 PrintF("\n Code kind histograms: \n"); 1833 for (int i = 0; i < Code::NUMBER_OF_KINDS; i++) { 1834 if (code_kind_statistics[i] > 0) { 1835 PrintF(" %-20s: %10d bytes\n", 1836 Code::Kind2String(static_cast<Code::Kind>(i)), 1837 code_kind_statistics[i]); 1838 } 1839 } 1840 PrintF("\n"); 1841 } 1842 1843 1844 static int CollectHistogramInfo(HeapObject* obj) { 1845 Isolate* isolate = obj->GetIsolate(); 1846 InstanceType type = obj->map()->instance_type(); 1847 ASSERT(0 <= type && type <= LAST_TYPE); 1848 ASSERT(isolate->heap_histograms()[type].name() != NULL); 1849 isolate->heap_histograms()[type].increment_number(1); 1850 isolate->heap_histograms()[type].increment_bytes(obj->Size()); 1851 1852 if (FLAG_collect_heap_spill_statistics && obj->IsJSObject()) { 1853 JSObject::cast(obj)->IncrementSpillStatistics( 1854 isolate->js_spill_information()); 1855 } 1856 1857 return obj->Size(); 1858 } 1859 1860 1861 static void ReportHistogram(Isolate* isolate, bool print_spill) { 1862 PrintF("\n Object Histogram:\n"); 1863 for (int i = 0; i <= LAST_TYPE; i++) { 1864 if (isolate->heap_histograms()[i].number() > 0) { 1865 PrintF(" %-34s%10d (%10d bytes)\n", 1866 isolate->heap_histograms()[i].name(), 1867 isolate->heap_histograms()[i].number(), 1868 isolate->heap_histograms()[i].bytes()); 1869 } 1870 } 1871 PrintF("\n"); 1872 1873 // Summarize string types. 1874 int string_number = 0; 1875 int string_bytes = 0; 1876 #define INCREMENT(type, size, name, camel_name) \ 1877 string_number += isolate->heap_histograms()[type].number(); \ 1878 string_bytes += isolate->heap_histograms()[type].bytes(); 1879 STRING_TYPE_LIST(INCREMENT) 1880 #undef INCREMENT 1881 if (string_number > 0) { 1882 PrintF(" %-34s%10d (%10d bytes)\n\n", "STRING_TYPE", string_number, 1883 string_bytes); 1884 } 1885 1886 if (FLAG_collect_heap_spill_statistics && print_spill) { 1887 isolate->js_spill_information()->Print(); 1888 } 1889 } 1890 #endif // DEBUG 1891 1892 1893 // Support for statistics gathering for --heap-stats and --log-gc. 1894 void NewSpace::ClearHistograms() { 1895 for (int i = 0; i <= LAST_TYPE; i++) { 1896 allocated_histogram_[i].clear(); 1897 promoted_histogram_[i].clear(); 1898 } 1899 } 1900 1901 1902 // Because the copying collector does not touch garbage objects, we iterate 1903 // the new space before a collection to get a histogram of allocated objects. 1904 // This only happens when --log-gc flag is set. 1905 void NewSpace::CollectStatistics() { 1906 ClearHistograms(); 1907 SemiSpaceIterator it(this); 1908 for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) 1909 RecordAllocation(obj); 1910 } 1911 1912 1913 static void DoReportStatistics(Isolate* isolate, 1914 HistogramInfo* info, const char* description) { 1915 LOG(isolate, HeapSampleBeginEvent("NewSpace", description)); 1916 // Lump all the string types together. 1917 int string_number = 0; 1918 int string_bytes = 0; 1919 #define INCREMENT(type, size, name, camel_name) \ 1920 string_number += info[type].number(); \ 1921 string_bytes += info[type].bytes(); 1922 STRING_TYPE_LIST(INCREMENT) 1923 #undef INCREMENT 1924 if (string_number > 0) { 1925 LOG(isolate, 1926 HeapSampleItemEvent("STRING_TYPE", string_number, string_bytes)); 1927 } 1928 1929 // Then do the other types. 1930 for (int i = FIRST_NONSTRING_TYPE; i <= LAST_TYPE; ++i) { 1931 if (info[i].number() > 0) { 1932 LOG(isolate, 1933 HeapSampleItemEvent(info[i].name(), info[i].number(), 1934 info[i].bytes())); 1935 } 1936 } 1937 LOG(isolate, HeapSampleEndEvent("NewSpace", description)); 1938 } 1939 1940 1941 void NewSpace::ReportStatistics() { 1942 #ifdef DEBUG 1943 if (FLAG_heap_stats) { 1944 float pct = static_cast<float>(Available()) / Capacity(); 1945 PrintF(" capacity: %" V8_PTR_PREFIX "d" 1946 ", available: %" V8_PTR_PREFIX "d, %%%d\n", 1947 Capacity(), Available(), static_cast<int>(pct*100)); 1948 PrintF("\n Object Histogram:\n"); 1949 for (int i = 0; i <= LAST_TYPE; i++) { 1950 if (allocated_histogram_[i].number() > 0) { 1951 PrintF(" %-34s%10d (%10d bytes)\n", 1952 allocated_histogram_[i].name(), 1953 allocated_histogram_[i].number(), 1954 allocated_histogram_[i].bytes()); 1955 } 1956 } 1957 PrintF("\n"); 1958 } 1959 #endif // DEBUG 1960 1961 if (FLAG_log_gc) { 1962 Isolate* isolate = heap()->isolate(); 1963 DoReportStatistics(isolate, allocated_histogram_, "allocated"); 1964 DoReportStatistics(isolate, promoted_histogram_, "promoted"); 1965 } 1966 } 1967 1968 1969 void NewSpace::RecordAllocation(HeapObject* obj) { 1970 InstanceType type = obj->map()->instance_type(); 1971 ASSERT(0 <= type && type <= LAST_TYPE); 1972 allocated_histogram_[type].increment_number(1); 1973 allocated_histogram_[type].increment_bytes(obj->Size()); 1974 } 1975 1976 1977 void NewSpace::RecordPromotion(HeapObject* obj) { 1978 InstanceType type = obj->map()->instance_type(); 1979 ASSERT(0 <= type && type <= LAST_TYPE); 1980 promoted_histogram_[type].increment_number(1); 1981 promoted_histogram_[type].increment_bytes(obj->Size()); 1982 } 1983 1984 1985 size_t NewSpace::CommittedPhysicalMemory() { 1986 if (!VirtualMemory::HasLazyCommits()) return CommittedMemory(); 1987 MemoryChunk::UpdateHighWaterMark(allocation_info_.top()); 1988 size_t size = to_space_.CommittedPhysicalMemory(); 1989 if (from_space_.is_committed()) { 1990 size += from_space_.CommittedPhysicalMemory(); 1991 } 1992 return size; 1993 } 1994 1995 1996 // ----------------------------------------------------------------------------- 1997 // Free lists for old object spaces implementation 1998 1999 void FreeListNode::set_size(Heap* heap, int size_in_bytes) { 2000 ASSERT(size_in_bytes > 0); 2001 ASSERT(IsAligned(size_in_bytes, kPointerSize)); 2002 2003 // We write a map and possibly size information to the block. If the block 2004 // is big enough to be a FreeSpace with at least one extra word (the next 2005 // pointer), we set its map to be the free space map and its size to an 2006 // appropriate array length for the desired size from HeapObject::Size(). 2007 // If the block is too small (eg, one or two words), to hold both a size 2008 // field and a next pointer, we give it a filler map that gives it the 2009 // correct size. 2010 if (size_in_bytes > FreeSpace::kHeaderSize) { 2011 // Can't use FreeSpace::cast because it fails during deserialization. 2012 // We have to set the size first with a release store before we store 2013 // the map because a concurrent store buffer scan on scavenge must not 2014 // observe a map with an invalid size. 2015 FreeSpace* this_as_free_space = reinterpret_cast<FreeSpace*>(this); 2016 this_as_free_space->nobarrier_set_size(size_in_bytes); 2017 synchronized_set_map_no_write_barrier(heap->raw_unchecked_free_space_map()); 2018 } else if (size_in_bytes == kPointerSize) { 2019 set_map_no_write_barrier(heap->raw_unchecked_one_pointer_filler_map()); 2020 } else if (size_in_bytes == 2 * kPointerSize) { 2021 set_map_no_write_barrier(heap->raw_unchecked_two_pointer_filler_map()); 2022 } else { 2023 UNREACHABLE(); 2024 } 2025 // We would like to ASSERT(Size() == size_in_bytes) but this would fail during 2026 // deserialization because the free space map is not done yet. 2027 } 2028 2029 2030 FreeListNode* FreeListNode::next() { 2031 ASSERT(IsFreeListNode(this)); 2032 if (map() == GetHeap()->raw_unchecked_free_space_map()) { 2033 ASSERT(map() == NULL || Size() >= kNextOffset + kPointerSize); 2034 return reinterpret_cast<FreeListNode*>( 2035 Memory::Address_at(address() + kNextOffset)); 2036 } else { 2037 return reinterpret_cast<FreeListNode*>( 2038 Memory::Address_at(address() + kPointerSize)); 2039 } 2040 } 2041 2042 2043 FreeListNode** FreeListNode::next_address() { 2044 ASSERT(IsFreeListNode(this)); 2045 if (map() == GetHeap()->raw_unchecked_free_space_map()) { 2046 ASSERT(Size() >= kNextOffset + kPointerSize); 2047 return reinterpret_cast<FreeListNode**>(address() + kNextOffset); 2048 } else { 2049 return reinterpret_cast<FreeListNode**>(address() + kPointerSize); 2050 } 2051 } 2052 2053 2054 void FreeListNode::set_next(FreeListNode* next) { 2055 ASSERT(IsFreeListNode(this)); 2056 // While we are booting the VM the free space map will actually be null. So 2057 // we have to make sure that we don't try to use it for anything at that 2058 // stage. 2059 if (map() == GetHeap()->raw_unchecked_free_space_map()) { 2060 ASSERT(map() == NULL || Size() >= kNextOffset + kPointerSize); 2061 base::NoBarrier_Store( 2062 reinterpret_cast<base::AtomicWord*>(address() + kNextOffset), 2063 reinterpret_cast<base::AtomicWord>(next)); 2064 } else { 2065 base::NoBarrier_Store( 2066 reinterpret_cast<base::AtomicWord*>(address() + kPointerSize), 2067 reinterpret_cast<base::AtomicWord>(next)); 2068 } 2069 } 2070 2071 2072 intptr_t FreeListCategory::Concatenate(FreeListCategory* category) { 2073 intptr_t free_bytes = 0; 2074 if (category->top() != NULL) { 2075 // This is safe (not going to deadlock) since Concatenate operations 2076 // are never performed on the same free lists at the same time in 2077 // reverse order. 2078 LockGuard<Mutex> target_lock_guard(mutex()); 2079 LockGuard<Mutex> source_lock_guard(category->mutex()); 2080 ASSERT(category->end_ != NULL); 2081 free_bytes = category->available(); 2082 if (end_ == NULL) { 2083 end_ = category->end(); 2084 } else { 2085 category->end()->set_next(top()); 2086 } 2087 set_top(category->top()); 2088 base::NoBarrier_Store(&top_, category->top_); 2089 available_ += category->available(); 2090 category->Reset(); 2091 } 2092 return free_bytes; 2093 } 2094 2095 2096 void FreeListCategory::Reset() { 2097 set_top(NULL); 2098 set_end(NULL); 2099 set_available(0); 2100 } 2101 2102 2103 intptr_t FreeListCategory::EvictFreeListItemsInList(Page* p) { 2104 int sum = 0; 2105 FreeListNode* t = top(); 2106 FreeListNode** n = &t; 2107 while (*n != NULL) { 2108 if (Page::FromAddress((*n)->address()) == p) { 2109 FreeSpace* free_space = reinterpret_cast<FreeSpace*>(*n); 2110 sum += free_space->Size(); 2111 *n = (*n)->next(); 2112 } else { 2113 n = (*n)->next_address(); 2114 } 2115 } 2116 set_top(t); 2117 if (top() == NULL) { 2118 set_end(NULL); 2119 } 2120 available_ -= sum; 2121 return sum; 2122 } 2123 2124 2125 bool FreeListCategory::ContainsPageFreeListItemsInList(Page* p) { 2126 FreeListNode* node = top(); 2127 while (node != NULL) { 2128 if (Page::FromAddress(node->address()) == p) return true; 2129 node = node->next(); 2130 } 2131 return false; 2132 } 2133 2134 2135 FreeListNode* FreeListCategory::PickNodeFromList(int *node_size) { 2136 FreeListNode* node = top(); 2137 2138 if (node == NULL) return NULL; 2139 2140 while (node != NULL && 2141 Page::FromAddress(node->address())->IsEvacuationCandidate()) { 2142 available_ -= reinterpret_cast<FreeSpace*>(node)->Size(); 2143 node = node->next(); 2144 } 2145 2146 if (node != NULL) { 2147 set_top(node->next()); 2148 *node_size = reinterpret_cast<FreeSpace*>(node)->Size(); 2149 available_ -= *node_size; 2150 } else { 2151 set_top(NULL); 2152 } 2153 2154 if (top() == NULL) { 2155 set_end(NULL); 2156 } 2157 2158 return node; 2159 } 2160 2161 2162 FreeListNode* FreeListCategory::PickNodeFromList(int size_in_bytes, 2163 int *node_size) { 2164 FreeListNode* node = PickNodeFromList(node_size); 2165 if (node != NULL && *node_size < size_in_bytes) { 2166 Free(node, *node_size); 2167 *node_size = 0; 2168 return NULL; 2169 } 2170 return node; 2171 } 2172 2173 2174 void FreeListCategory::Free(FreeListNode* node, int size_in_bytes) { 2175 node->set_next(top()); 2176 set_top(node); 2177 if (end_ == NULL) { 2178 end_ = node; 2179 } 2180 available_ += size_in_bytes; 2181 } 2182 2183 2184 void FreeListCategory::RepairFreeList(Heap* heap) { 2185 FreeListNode* n = top(); 2186 while (n != NULL) { 2187 Map** map_location = reinterpret_cast<Map**>(n->address()); 2188 if (*map_location == NULL) { 2189 *map_location = heap->free_space_map(); 2190 } else { 2191 ASSERT(*map_location == heap->free_space_map()); 2192 } 2193 n = n->next(); 2194 } 2195 } 2196 2197 2198 FreeList::FreeList(PagedSpace* owner) 2199 : owner_(owner), heap_(owner->heap()) { 2200 Reset(); 2201 } 2202 2203 2204 intptr_t FreeList::Concatenate(FreeList* free_list) { 2205 intptr_t free_bytes = 0; 2206 free_bytes += small_list_.Concatenate(free_list->small_list()); 2207 free_bytes += medium_list_.Concatenate(free_list->medium_list()); 2208 free_bytes += large_list_.Concatenate(free_list->large_list()); 2209 free_bytes += huge_list_.Concatenate(free_list->huge_list()); 2210 return free_bytes; 2211 } 2212 2213 2214 void FreeList::Reset() { 2215 small_list_.Reset(); 2216 medium_list_.Reset(); 2217 large_list_.Reset(); 2218 huge_list_.Reset(); 2219 } 2220 2221 2222 int FreeList::Free(Address start, int size_in_bytes) { 2223 if (size_in_bytes == 0) return 0; 2224 2225 FreeListNode* node = FreeListNode::FromAddress(start); 2226 node->set_size(heap_, size_in_bytes); 2227 Page* page = Page::FromAddress(start); 2228 2229 // Early return to drop too-small blocks on the floor. 2230 if (size_in_bytes < kSmallListMin) { 2231 page->add_non_available_small_blocks(size_in_bytes); 2232 return size_in_bytes; 2233 } 2234 2235 // Insert other blocks at the head of a free list of the appropriate 2236 // magnitude. 2237 if (size_in_bytes <= kSmallListMax) { 2238 small_list_.Free(node, size_in_bytes); 2239 page->add_available_in_small_free_list(size_in_bytes); 2240 } else if (size_in_bytes <= kMediumListMax) { 2241 medium_list_.Free(node, size_in_bytes); 2242 page->add_available_in_medium_free_list(size_in_bytes); 2243 } else if (size_in_bytes <= kLargeListMax) { 2244 large_list_.Free(node, size_in_bytes); 2245 page->add_available_in_large_free_list(size_in_bytes); 2246 } else { 2247 huge_list_.Free(node, size_in_bytes); 2248 page->add_available_in_huge_free_list(size_in_bytes); 2249 } 2250 2251 ASSERT(IsVeryLong() || available() == SumFreeLists()); 2252 return 0; 2253 } 2254 2255 2256 FreeListNode* FreeList::FindNodeFor(int size_in_bytes, int* node_size) { 2257 FreeListNode* node = NULL; 2258 Page* page = NULL; 2259 2260 if (size_in_bytes <= kSmallAllocationMax) { 2261 node = small_list_.PickNodeFromList(node_size); 2262 if (node != NULL) { 2263 ASSERT(size_in_bytes <= *node_size); 2264 page = Page::FromAddress(node->address()); 2265 page->add_available_in_small_free_list(-(*node_size)); 2266 ASSERT(IsVeryLong() || available() == SumFreeLists()); 2267 return node; 2268 } 2269 } 2270 2271 if (size_in_bytes <= kMediumAllocationMax) { 2272 node = medium_list_.PickNodeFromList(node_size); 2273 if (node != NULL) { 2274 ASSERT(size_in_bytes <= *node_size); 2275 page = Page::FromAddress(node->address()); 2276 page->add_available_in_medium_free_list(-(*node_size)); 2277 ASSERT(IsVeryLong() || available() == SumFreeLists()); 2278 return node; 2279 } 2280 } 2281 2282 if (size_in_bytes <= kLargeAllocationMax) { 2283 node = large_list_.PickNodeFromList(node_size); 2284 if (node != NULL) { 2285 ASSERT(size_in_bytes <= *node_size); 2286 page = Page::FromAddress(node->address()); 2287 page->add_available_in_large_free_list(-(*node_size)); 2288 ASSERT(IsVeryLong() || available() == SumFreeLists()); 2289 return node; 2290 } 2291 } 2292 2293 int huge_list_available = huge_list_.available(); 2294 FreeListNode* top_node = huge_list_.top(); 2295 for (FreeListNode** cur = &top_node; 2296 *cur != NULL; 2297 cur = (*cur)->next_address()) { 2298 FreeListNode* cur_node = *cur; 2299 while (cur_node != NULL && 2300 Page::FromAddress(cur_node->address())->IsEvacuationCandidate()) { 2301 int size = reinterpret_cast<FreeSpace*>(cur_node)->Size(); 2302 huge_list_available -= size; 2303 page = Page::FromAddress(cur_node->address()); 2304 page->add_available_in_huge_free_list(-size); 2305 cur_node = cur_node->next(); 2306 } 2307 2308 *cur = cur_node; 2309 if (cur_node == NULL) { 2310 huge_list_.set_end(NULL); 2311 break; 2312 } 2313 2314 ASSERT((*cur)->map() == heap_->raw_unchecked_free_space_map()); 2315 FreeSpace* cur_as_free_space = reinterpret_cast<FreeSpace*>(*cur); 2316 int size = cur_as_free_space->Size(); 2317 if (size >= size_in_bytes) { 2318 // Large enough node found. Unlink it from the list. 2319 node = *cur; 2320 *cur = node->next(); 2321 *node_size = size; 2322 huge_list_available -= size; 2323 page = Page::FromAddress(node->address()); 2324 page->add_available_in_huge_free_list(-size); 2325 break; 2326 } 2327 } 2328 2329 huge_list_.set_top(top_node); 2330 if (huge_list_.top() == NULL) { 2331 huge_list_.set_end(NULL); 2332 } 2333 huge_list_.set_available(huge_list_available); 2334 2335 if (node != NULL) { 2336 ASSERT(IsVeryLong() || available() == SumFreeLists()); 2337 return node; 2338 } 2339 2340 if (size_in_bytes <= kSmallListMax) { 2341 node = small_list_.PickNodeFromList(size_in_bytes, node_size); 2342 if (node != NULL) { 2343 ASSERT(size_in_bytes <= *node_size); 2344 page = Page::FromAddress(node->address()); 2345 page->add_available_in_small_free_list(-(*node_size)); 2346 } 2347 } else if (size_in_bytes <= kMediumListMax) { 2348 node = medium_list_.PickNodeFromList(size_in_bytes, node_size); 2349 if (node != NULL) { 2350 ASSERT(size_in_bytes <= *node_size); 2351 page = Page::FromAddress(node->address()); 2352 page->add_available_in_medium_free_list(-(*node_size)); 2353 } 2354 } else if (size_in_bytes <= kLargeListMax) { 2355 node = large_list_.PickNodeFromList(size_in_bytes, node_size); 2356 if (node != NULL) { 2357 ASSERT(size_in_bytes <= *node_size); 2358 page = Page::FromAddress(node->address()); 2359 page->add_available_in_large_free_list(-(*node_size)); 2360 } 2361 } 2362 2363 ASSERT(IsVeryLong() || available() == SumFreeLists()); 2364 return node; 2365 } 2366 2367 2368 // Allocation on the old space free list. If it succeeds then a new linear 2369 // allocation space has been set up with the top and limit of the space. If 2370 // the allocation fails then NULL is returned, and the caller can perform a GC 2371 // or allocate a new page before retrying. 2372 HeapObject* FreeList::Allocate(int size_in_bytes) { 2373 ASSERT(0 < size_in_bytes); 2374 ASSERT(size_in_bytes <= kMaxBlockSize); 2375 ASSERT(IsAligned(size_in_bytes, kPointerSize)); 2376 // Don't free list allocate if there is linear space available. 2377 ASSERT(owner_->limit() - owner_->top() < size_in_bytes); 2378 2379 int old_linear_size = static_cast<int>(owner_->limit() - owner_->top()); 2380 // Mark the old linear allocation area with a free space map so it can be 2381 // skipped when scanning the heap. This also puts it back in the free list 2382 // if it is big enough. 2383 owner_->Free(owner_->top(), old_linear_size); 2384 2385 owner_->heap()->incremental_marking()->OldSpaceStep( 2386 size_in_bytes - old_linear_size); 2387 2388 int new_node_size = 0; 2389 FreeListNode* new_node = FindNodeFor(size_in_bytes, &new_node_size); 2390 if (new_node == NULL) { 2391 owner_->SetTopAndLimit(NULL, NULL); 2392 return NULL; 2393 } 2394 2395 int bytes_left = new_node_size - size_in_bytes; 2396 ASSERT(bytes_left >= 0); 2397 2398 #ifdef DEBUG 2399 for (int i = 0; i < size_in_bytes / kPointerSize; i++) { 2400 reinterpret_cast<Object**>(new_node->address())[i] = 2401 Smi::FromInt(kCodeZapValue); 2402 } 2403 #endif 2404 2405 // The old-space-step might have finished sweeping and restarted marking. 2406 // Verify that it did not turn the page of the new node into an evacuation 2407 // candidate. 2408 ASSERT(!MarkCompactCollector::IsOnEvacuationCandidate(new_node)); 2409 2410 const int kThreshold = IncrementalMarking::kAllocatedThreshold; 2411 2412 // Memory in the linear allocation area is counted as allocated. We may free 2413 // a little of this again immediately - see below. 2414 owner_->Allocate(new_node_size); 2415 2416 if (owner_->heap()->inline_allocation_disabled()) { 2417 // Keep the linear allocation area empty if requested to do so, just 2418 // return area back to the free list instead. 2419 owner_->Free(new_node->address() + size_in_bytes, bytes_left); 2420 ASSERT(owner_->top() == NULL && owner_->limit() == NULL); 2421 } else if (bytes_left > kThreshold && 2422 owner_->heap()->incremental_marking()->IsMarkingIncomplete() && 2423 FLAG_incremental_marking_steps) { 2424 int linear_size = owner_->RoundSizeDownToObjectAlignment(kThreshold); 2425 // We don't want to give too large linear areas to the allocator while 2426 // incremental marking is going on, because we won't check again whether 2427 // we want to do another increment until the linear area is used up. 2428 owner_->Free(new_node->address() + size_in_bytes + linear_size, 2429 new_node_size - size_in_bytes - linear_size); 2430 owner_->SetTopAndLimit(new_node->address() + size_in_bytes, 2431 new_node->address() + size_in_bytes + linear_size); 2432 } else if (bytes_left > 0) { 2433 // Normally we give the rest of the node to the allocator as its new 2434 // linear allocation area. 2435 owner_->SetTopAndLimit(new_node->address() + size_in_bytes, 2436 new_node->address() + new_node_size); 2437 } else { 2438 // TODO(gc) Try not freeing linear allocation region when bytes_left 2439 // are zero. 2440 owner_->SetTopAndLimit(NULL, NULL); 2441 } 2442 2443 return new_node; 2444 } 2445 2446 2447 intptr_t FreeList::EvictFreeListItems(Page* p) { 2448 intptr_t sum = huge_list_.EvictFreeListItemsInList(p); 2449 p->set_available_in_huge_free_list(0); 2450 2451 if (sum < p->area_size()) { 2452 sum += small_list_.EvictFreeListItemsInList(p) + 2453 medium_list_.EvictFreeListItemsInList(p) + 2454 large_list_.EvictFreeListItemsInList(p); 2455 p->set_available_in_small_free_list(0); 2456 p->set_available_in_medium_free_list(0); 2457 p->set_available_in_large_free_list(0); 2458 } 2459 2460 return sum; 2461 } 2462 2463 2464 bool FreeList::ContainsPageFreeListItems(Page* p) { 2465 return huge_list_.EvictFreeListItemsInList(p) || 2466 small_list_.EvictFreeListItemsInList(p) || 2467 medium_list_.EvictFreeListItemsInList(p) || 2468 large_list_.EvictFreeListItemsInList(p); 2469 } 2470 2471 2472 void FreeList::RepairLists(Heap* heap) { 2473 small_list_.RepairFreeList(heap); 2474 medium_list_.RepairFreeList(heap); 2475 large_list_.RepairFreeList(heap); 2476 huge_list_.RepairFreeList(heap); 2477 } 2478 2479 2480 #ifdef DEBUG 2481 intptr_t FreeListCategory::SumFreeList() { 2482 intptr_t sum = 0; 2483 FreeListNode* cur = top(); 2484 while (cur != NULL) { 2485 ASSERT(cur->map() == cur->GetHeap()->raw_unchecked_free_space_map()); 2486 FreeSpace* cur_as_free_space = reinterpret_cast<FreeSpace*>(cur); 2487 sum += cur_as_free_space->nobarrier_size(); 2488 cur = cur->next(); 2489 } 2490 return sum; 2491 } 2492 2493 2494 static const int kVeryLongFreeList = 500; 2495 2496 2497 int FreeListCategory::FreeListLength() { 2498 int length = 0; 2499 FreeListNode* cur = top(); 2500 while (cur != NULL) { 2501 length++; 2502 cur = cur->next(); 2503 if (length == kVeryLongFreeList) return length; 2504 } 2505 return length; 2506 } 2507 2508 2509 bool FreeList::IsVeryLong() { 2510 if (small_list_.FreeListLength() == kVeryLongFreeList) return true; 2511 if (medium_list_.FreeListLength() == kVeryLongFreeList) return true; 2512 if (large_list_.FreeListLength() == kVeryLongFreeList) return true; 2513 if (huge_list_.FreeListLength() == kVeryLongFreeList) return true; 2514 return false; 2515 } 2516 2517 2518 // This can take a very long time because it is linear in the number of entries 2519 // on the free list, so it should not be called if FreeListLength returns 2520 // kVeryLongFreeList. 2521 intptr_t FreeList::SumFreeLists() { 2522 intptr_t sum = small_list_.SumFreeList(); 2523 sum += medium_list_.SumFreeList(); 2524 sum += large_list_.SumFreeList(); 2525 sum += huge_list_.SumFreeList(); 2526 return sum; 2527 } 2528 #endif 2529 2530 2531 // ----------------------------------------------------------------------------- 2532 // OldSpace implementation 2533 2534 void PagedSpace::PrepareForMarkCompact() { 2535 // We don't have a linear allocation area while sweeping. It will be restored 2536 // on the first allocation after the sweep. 2537 EmptyAllocationInfo(); 2538 2539 // This counter will be increased for pages which will be swept by the 2540 // sweeper threads. 2541 unswept_free_bytes_ = 0; 2542 2543 // Clear the free list before a full GC---it will be rebuilt afterward. 2544 free_list_.Reset(); 2545 } 2546 2547 2548 intptr_t PagedSpace::SizeOfObjects() { 2549 ASSERT(heap()->mark_compact_collector()->IsConcurrentSweepingInProgress() || 2550 (unswept_free_bytes_ == 0)); 2551 return Size() - unswept_free_bytes_ - (limit() - top()); 2552 } 2553 2554 2555 // After we have booted, we have created a map which represents free space 2556 // on the heap. If there was already a free list then the elements on it 2557 // were created with the wrong FreeSpaceMap (normally NULL), so we need to 2558 // fix them. 2559 void PagedSpace::RepairFreeListsAfterBoot() { 2560 free_list_.RepairLists(heap()); 2561 } 2562 2563 2564 void PagedSpace::EvictEvacuationCandidatesFromFreeLists() { 2565 if (allocation_info_.top() >= allocation_info_.limit()) return; 2566 2567 if (Page::FromAllocationTop(allocation_info_.top())-> 2568 IsEvacuationCandidate()) { 2569 // Create filler object to keep page iterable if it was iterable. 2570 int remaining = 2571 static_cast<int>(allocation_info_.limit() - allocation_info_.top()); 2572 heap()->CreateFillerObjectAt(allocation_info_.top(), remaining); 2573 2574 allocation_info_.set_top(NULL); 2575 allocation_info_.set_limit(NULL); 2576 } 2577 } 2578 2579 2580 HeapObject* PagedSpace::WaitForSweeperThreadsAndRetryAllocation( 2581 int size_in_bytes) { 2582 MarkCompactCollector* collector = heap()->mark_compact_collector(); 2583 2584 // If sweeper threads are still running, wait for them. 2585 if (collector->IsConcurrentSweepingInProgress()) { 2586 collector->WaitUntilSweepingCompleted(); 2587 2588 // After waiting for the sweeper threads, there may be new free-list 2589 // entries. 2590 return free_list_.Allocate(size_in_bytes); 2591 } 2592 return NULL; 2593 } 2594 2595 2596 HeapObject* PagedSpace::SlowAllocateRaw(int size_in_bytes) { 2597 // Allocation in this space has failed. 2598 2599 // If sweeper threads are active, try to re-fill the free-lists. 2600 MarkCompactCollector* collector = heap()->mark_compact_collector(); 2601 if (collector->IsConcurrentSweepingInProgress()) { 2602 collector->RefillFreeList(this); 2603 2604 // Retry the free list allocation. 2605 HeapObject* object = free_list_.Allocate(size_in_bytes); 2606 if (object != NULL) return object; 2607 } 2608 2609 // Free list allocation failed and there is no next page. Fail if we have 2610 // hit the old generation size limit that should cause a garbage 2611 // collection. 2612 if (!heap()->always_allocate() 2613 && heap()->OldGenerationAllocationLimitReached()) { 2614 // If sweeper threads are active, wait for them at that point and steal 2615 // elements form their free-lists. 2616 HeapObject* object = WaitForSweeperThreadsAndRetryAllocation(size_in_bytes); 2617 if (object != NULL) return object; 2618 } 2619 2620 // Try to expand the space and allocate in the new next page. 2621 if (Expand()) { 2622 ASSERT(CountTotalPages() > 1 || size_in_bytes <= free_list_.available()); 2623 return free_list_.Allocate(size_in_bytes); 2624 } 2625 2626 // If sweeper threads are active, wait for them at that point and steal 2627 // elements form their free-lists. Allocation may still fail their which 2628 // would indicate that there is not enough memory for the given allocation. 2629 return WaitForSweeperThreadsAndRetryAllocation(size_in_bytes); 2630 } 2631 2632 2633 #ifdef DEBUG 2634 void PagedSpace::ReportCodeStatistics(Isolate* isolate) { 2635 CommentStatistic* comments_statistics = 2636 isolate->paged_space_comments_statistics(); 2637 ReportCodeKindStatistics(isolate->code_kind_statistics()); 2638 PrintF("Code comment statistics (\" [ comment-txt : size/ " 2639 "count (average)\"):\n"); 2640 for (int i = 0; i <= CommentStatistic::kMaxComments; i++) { 2641 const CommentStatistic& cs = comments_statistics[i]; 2642 if (cs.size > 0) { 2643 PrintF(" %-30s: %10d/%6d (%d)\n", cs.comment, cs.size, cs.count, 2644 cs.size/cs.count); 2645 } 2646 } 2647 PrintF("\n"); 2648 } 2649 2650 2651 void PagedSpace::ResetCodeStatistics(Isolate* isolate) { 2652 CommentStatistic* comments_statistics = 2653 isolate->paged_space_comments_statistics(); 2654 ClearCodeKindStatistics(isolate->code_kind_statistics()); 2655 for (int i = 0; i < CommentStatistic::kMaxComments; i++) { 2656 comments_statistics[i].Clear(); 2657 } 2658 comments_statistics[CommentStatistic::kMaxComments].comment = "Unknown"; 2659 comments_statistics[CommentStatistic::kMaxComments].size = 0; 2660 comments_statistics[CommentStatistic::kMaxComments].count = 0; 2661 } 2662 2663 2664 // Adds comment to 'comment_statistics' table. Performance OK as long as 2665 // 'kMaxComments' is small 2666 static void EnterComment(Isolate* isolate, const char* comment, int delta) { 2667 CommentStatistic* comments_statistics = 2668 isolate->paged_space_comments_statistics(); 2669 // Do not count empty comments 2670 if (delta <= 0) return; 2671 CommentStatistic* cs = &comments_statistics[CommentStatistic::kMaxComments]; 2672 // Search for a free or matching entry in 'comments_statistics': 'cs' 2673 // points to result. 2674 for (int i = 0; i < CommentStatistic::kMaxComments; i++) { 2675 if (comments_statistics[i].comment == NULL) { 2676 cs = &comments_statistics[i]; 2677 cs->comment = comment; 2678 break; 2679 } else if (strcmp(comments_statistics[i].comment, comment) == 0) { 2680 cs = &comments_statistics[i]; 2681 break; 2682 } 2683 } 2684 // Update entry for 'comment' 2685 cs->size += delta; 2686 cs->count += 1; 2687 } 2688 2689 2690 // Call for each nested comment start (start marked with '[ xxx', end marked 2691 // with ']'. RelocIterator 'it' must point to a comment reloc info. 2692 static void CollectCommentStatistics(Isolate* isolate, RelocIterator* it) { 2693 ASSERT(!it->done()); 2694 ASSERT(it->rinfo()->rmode() == RelocInfo::COMMENT); 2695 const char* tmp = reinterpret_cast<const char*>(it->rinfo()->data()); 2696 if (tmp[0] != '[') { 2697 // Not a nested comment; skip 2698 return; 2699 } 2700 2701 // Search for end of nested comment or a new nested comment 2702 const char* const comment_txt = 2703 reinterpret_cast<const char*>(it->rinfo()->data()); 2704 const byte* prev_pc = it->rinfo()->pc(); 2705 int flat_delta = 0; 2706 it->next(); 2707 while (true) { 2708 // All nested comments must be terminated properly, and therefore exit 2709 // from loop. 2710 ASSERT(!it->done()); 2711 if (it->rinfo()->rmode() == RelocInfo::COMMENT) { 2712 const char* const txt = 2713 reinterpret_cast<const char*>(it->rinfo()->data()); 2714 flat_delta += static_cast<int>(it->rinfo()->pc() - prev_pc); 2715 if (txt[0] == ']') break; // End of nested comment 2716 // A new comment 2717 CollectCommentStatistics(isolate, it); 2718 // Skip code that was covered with previous comment 2719 prev_pc = it->rinfo()->pc(); 2720 } 2721 it->next(); 2722 } 2723 EnterComment(isolate, comment_txt, flat_delta); 2724 } 2725 2726 2727 // Collects code size statistics: 2728 // - by code kind 2729 // - by code comment 2730 void PagedSpace::CollectCodeStatistics() { 2731 Isolate* isolate = heap()->isolate(); 2732 HeapObjectIterator obj_it(this); 2733 for (HeapObject* obj = obj_it.Next(); obj != NULL; obj = obj_it.Next()) { 2734 if (obj->IsCode()) { 2735 Code* code = Code::cast(obj); 2736 isolate->code_kind_statistics()[code->kind()] += code->Size(); 2737 RelocIterator it(code); 2738 int delta = 0; 2739 const byte* prev_pc = code->instruction_start(); 2740 while (!it.done()) { 2741 if (it.rinfo()->rmode() == RelocInfo::COMMENT) { 2742 delta += static_cast<int>(it.rinfo()->pc() - prev_pc); 2743 CollectCommentStatistics(isolate, &it); 2744 prev_pc = it.rinfo()->pc(); 2745 } 2746 it.next(); 2747 } 2748 2749 ASSERT(code->instruction_start() <= prev_pc && 2750 prev_pc <= code->instruction_end()); 2751 delta += static_cast<int>(code->instruction_end() - prev_pc); 2752 EnterComment(isolate, "NoComment", delta); 2753 } 2754 } 2755 } 2756 2757 2758 void PagedSpace::ReportStatistics() { 2759 int pct = static_cast<int>(Available() * 100 / Capacity()); 2760 PrintF(" capacity: %" V8_PTR_PREFIX "d" 2761 ", waste: %" V8_PTR_PREFIX "d" 2762 ", available: %" V8_PTR_PREFIX "d, %%%d\n", 2763 Capacity(), Waste(), Available(), pct); 2764 2765 if (was_swept_conservatively_) return; 2766 ClearHistograms(heap()->isolate()); 2767 HeapObjectIterator obj_it(this); 2768 for (HeapObject* obj = obj_it.Next(); obj != NULL; obj = obj_it.Next()) 2769 CollectHistogramInfo(obj); 2770 ReportHistogram(heap()->isolate(), true); 2771 } 2772 #endif 2773 2774 2775 // ----------------------------------------------------------------------------- 2776 // MapSpace implementation 2777 // TODO(mvstanton): this is weird...the compiler can't make a vtable unless 2778 // there is at least one non-inlined virtual function. I would prefer to hide 2779 // the VerifyObject definition behind VERIFY_HEAP. 2780 2781 void MapSpace::VerifyObject(HeapObject* object) { 2782 CHECK(object->IsMap()); 2783 } 2784 2785 2786 // ----------------------------------------------------------------------------- 2787 // CellSpace and PropertyCellSpace implementation 2788 // TODO(mvstanton): this is weird...the compiler can't make a vtable unless 2789 // there is at least one non-inlined virtual function. I would prefer to hide 2790 // the VerifyObject definition behind VERIFY_HEAP. 2791 2792 void CellSpace::VerifyObject(HeapObject* object) { 2793 CHECK(object->IsCell()); 2794 } 2795 2796 2797 void PropertyCellSpace::VerifyObject(HeapObject* object) { 2798 CHECK(object->IsPropertyCell()); 2799 } 2800 2801 2802 // ----------------------------------------------------------------------------- 2803 // LargeObjectIterator 2804 2805 LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space) { 2806 current_ = space->first_page_; 2807 size_func_ = NULL; 2808 } 2809 2810 2811 LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space, 2812 HeapObjectCallback size_func) { 2813 current_ = space->first_page_; 2814 size_func_ = size_func; 2815 } 2816 2817 2818 HeapObject* LargeObjectIterator::Next() { 2819 if (current_ == NULL) return NULL; 2820 2821 HeapObject* object = current_->GetObject(); 2822 current_ = current_->next_page(); 2823 return object; 2824 } 2825 2826 2827 // ----------------------------------------------------------------------------- 2828 // LargeObjectSpace 2829 static bool ComparePointers(void* key1, void* key2) { 2830 return key1 == key2; 2831 } 2832 2833 2834 LargeObjectSpace::LargeObjectSpace(Heap* heap, 2835 intptr_t max_capacity, 2836 AllocationSpace id) 2837 : Space(heap, id, NOT_EXECUTABLE), // Managed on a per-allocation basis 2838 max_capacity_(max_capacity), 2839 first_page_(NULL), 2840 size_(0), 2841 page_count_(0), 2842 objects_size_(0), 2843 chunk_map_(ComparePointers, 1024) {} 2844 2845 2846 bool LargeObjectSpace::SetUp() { 2847 first_page_ = NULL; 2848 size_ = 0; 2849 maximum_committed_ = 0; 2850 page_count_ = 0; 2851 objects_size_ = 0; 2852 chunk_map_.Clear(); 2853 return true; 2854 } 2855 2856 2857 void LargeObjectSpace::TearDown() { 2858 while (first_page_ != NULL) { 2859 LargePage* page = first_page_; 2860 first_page_ = first_page_->next_page(); 2861 LOG(heap()->isolate(), DeleteEvent("LargeObjectChunk", page->address())); 2862 2863 ObjectSpace space = static_cast<ObjectSpace>(1 << identity()); 2864 heap()->isolate()->memory_allocator()->PerformAllocationCallback( 2865 space, kAllocationActionFree, page->size()); 2866 heap()->isolate()->memory_allocator()->Free(page); 2867 } 2868 SetUp(); 2869 } 2870 2871 2872 AllocationResult LargeObjectSpace::AllocateRaw(int object_size, 2873 Executability executable) { 2874 // Check if we want to force a GC before growing the old space further. 2875 // If so, fail the allocation. 2876 if (!heap()->always_allocate() && 2877 heap()->OldGenerationAllocationLimitReached()) { 2878 return AllocationResult::Retry(identity()); 2879 } 2880 2881 if (Size() + object_size > max_capacity_) { 2882 return AllocationResult::Retry(identity()); 2883 } 2884 2885 LargePage* page = heap()->isolate()->memory_allocator()-> 2886 AllocateLargePage(object_size, this, executable); 2887 if (page == NULL) return AllocationResult::Retry(identity()); 2888 ASSERT(page->area_size() >= object_size); 2889 2890 size_ += static_cast<int>(page->size()); 2891 objects_size_ += object_size; 2892 page_count_++; 2893 page->set_next_page(first_page_); 2894 first_page_ = page; 2895 2896 if (size_ > maximum_committed_) { 2897 maximum_committed_ = size_; 2898 } 2899 2900 // Register all MemoryChunk::kAlignment-aligned chunks covered by 2901 // this large page in the chunk map. 2902 uintptr_t base = reinterpret_cast<uintptr_t>(page) / MemoryChunk::kAlignment; 2903 uintptr_t limit = base + (page->size() - 1) / MemoryChunk::kAlignment; 2904 for (uintptr_t key = base; key <= limit; key++) { 2905 HashMap::Entry* entry = chunk_map_.Lookup(reinterpret_cast<void*>(key), 2906 static_cast<uint32_t>(key), 2907 true); 2908 ASSERT(entry != NULL); 2909 entry->value = page; 2910 } 2911 2912 HeapObject* object = page->GetObject(); 2913 2914 if (Heap::ShouldZapGarbage()) { 2915 // Make the object consistent so the heap can be verified in OldSpaceStep. 2916 // We only need to do this in debug builds or if verify_heap is on. 2917 reinterpret_cast<Object**>(object->address())[0] = 2918 heap()->fixed_array_map(); 2919 reinterpret_cast<Object**>(object->address())[1] = Smi::FromInt(0); 2920 } 2921 2922 heap()->incremental_marking()->OldSpaceStep(object_size); 2923 return object; 2924 } 2925 2926 2927 size_t LargeObjectSpace::CommittedPhysicalMemory() { 2928 if (!VirtualMemory::HasLazyCommits()) return CommittedMemory(); 2929 size_t size = 0; 2930 LargePage* current = first_page_; 2931 while (current != NULL) { 2932 size += current->CommittedPhysicalMemory(); 2933 current = current->next_page(); 2934 } 2935 return size; 2936 } 2937 2938 2939 // GC support 2940 Object* LargeObjectSpace::FindObject(Address a) { 2941 LargePage* page = FindPage(a); 2942 if (page != NULL) { 2943 return page->GetObject(); 2944 } 2945 return Smi::FromInt(0); // Signaling not found. 2946 } 2947 2948 2949 LargePage* LargeObjectSpace::FindPage(Address a) { 2950 uintptr_t key = reinterpret_cast<uintptr_t>(a) / MemoryChunk::kAlignment; 2951 HashMap::Entry* e = chunk_map_.Lookup(reinterpret_cast<void*>(key), 2952 static_cast<uint32_t>(key), 2953 false); 2954 if (e != NULL) { 2955 ASSERT(e->value != NULL); 2956 LargePage* page = reinterpret_cast<LargePage*>(e->value); 2957 ASSERT(page->is_valid()); 2958 if (page->Contains(a)) { 2959 return page; 2960 } 2961 } 2962 return NULL; 2963 } 2964 2965 2966 void LargeObjectSpace::FreeUnmarkedObjects() { 2967 LargePage* previous = NULL; 2968 LargePage* current = first_page_; 2969 while (current != NULL) { 2970 HeapObject* object = current->GetObject(); 2971 // Can this large page contain pointers to non-trivial objects. No other 2972 // pointer object is this big. 2973 bool is_pointer_object = object->IsFixedArray(); 2974 MarkBit mark_bit = Marking::MarkBitFrom(object); 2975 if (mark_bit.Get()) { 2976 mark_bit.Clear(); 2977 Page::FromAddress(object->address())->ResetProgressBar(); 2978 Page::FromAddress(object->address())->ResetLiveBytes(); 2979 previous = current; 2980 current = current->next_page(); 2981 } else { 2982 LargePage* page = current; 2983 // Cut the chunk out from the chunk list. 2984 current = current->next_page(); 2985 if (previous == NULL) { 2986 first_page_ = current; 2987 } else { 2988 previous->set_next_page(current); 2989 } 2990 2991 // Free the chunk. 2992 heap()->mark_compact_collector()->ReportDeleteIfNeeded( 2993 object, heap()->isolate()); 2994 size_ -= static_cast<int>(page->size()); 2995 objects_size_ -= object->Size(); 2996 page_count_--; 2997 2998 // Remove entries belonging to this page. 2999 // Use variable alignment to help pass length check (<= 80 characters) 3000 // of single line in tools/presubmit.py. 3001 const intptr_t alignment = MemoryChunk::kAlignment; 3002 uintptr_t base = reinterpret_cast<uintptr_t>(page)/alignment; 3003 uintptr_t limit = base + (page->size()-1)/alignment; 3004 for (uintptr_t key = base; key <= limit; key++) { 3005 chunk_map_.Remove(reinterpret_cast<void*>(key), 3006 static_cast<uint32_t>(key)); 3007 } 3008 3009 if (is_pointer_object) { 3010 heap()->QueueMemoryChunkForFree(page); 3011 } else { 3012 heap()->isolate()->memory_allocator()->Free(page); 3013 } 3014 } 3015 } 3016 heap()->FreeQueuedChunks(); 3017 } 3018 3019 3020 bool LargeObjectSpace::Contains(HeapObject* object) { 3021 Address address = object->address(); 3022 MemoryChunk* chunk = MemoryChunk::FromAddress(address); 3023 3024 bool owned = (chunk->owner() == this); 3025 3026 SLOW_ASSERT(!owned || FindObject(address)->IsHeapObject()); 3027 3028 return owned; 3029 } 3030 3031 3032 #ifdef VERIFY_HEAP 3033 // We do not assume that the large object iterator works, because it depends 3034 // on the invariants we are checking during verification. 3035 void LargeObjectSpace::Verify() { 3036 for (LargePage* chunk = first_page_; 3037 chunk != NULL; 3038 chunk = chunk->next_page()) { 3039 // Each chunk contains an object that starts at the large object page's 3040 // object area start. 3041 HeapObject* object = chunk->GetObject(); 3042 Page* page = Page::FromAddress(object->address()); 3043 CHECK(object->address() == page->area_start()); 3044 3045 // The first word should be a map, and we expect all map pointers to be 3046 // in map space. 3047 Map* map = object->map(); 3048 CHECK(map->IsMap()); 3049 CHECK(heap()->map_space()->Contains(map)); 3050 3051 // We have only code, sequential strings, external strings 3052 // (sequential strings that have been morphed into external 3053 // strings), fixed arrays, and byte arrays in large object space. 3054 CHECK(object->IsCode() || object->IsSeqString() || 3055 object->IsExternalString() || object->IsFixedArray() || 3056 object->IsFixedDoubleArray() || object->IsByteArray()); 3057 3058 // The object itself should look OK. 3059 object->ObjectVerify(); 3060 3061 // Byte arrays and strings don't have interior pointers. 3062 if (object->IsCode()) { 3063 VerifyPointersVisitor code_visitor; 3064 object->IterateBody(map->instance_type(), 3065 object->Size(), 3066 &code_visitor); 3067 } else if (object->IsFixedArray()) { 3068 FixedArray* array = FixedArray::cast(object); 3069 for (int j = 0; j < array->length(); j++) { 3070 Object* element = array->get(j); 3071 if (element->IsHeapObject()) { 3072 HeapObject* element_object = HeapObject::cast(element); 3073 CHECK(heap()->Contains(element_object)); 3074 CHECK(element_object->map()->IsMap()); 3075 } 3076 } 3077 } 3078 } 3079 } 3080 #endif 3081 3082 3083 #ifdef DEBUG 3084 void LargeObjectSpace::Print() { 3085 LargeObjectIterator it(this); 3086 for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { 3087 obj->Print(); 3088 } 3089 } 3090 3091 3092 void LargeObjectSpace::ReportStatistics() { 3093 PrintF(" size: %" V8_PTR_PREFIX "d\n", size_); 3094 int num_objects = 0; 3095 ClearHistograms(heap()->isolate()); 3096 LargeObjectIterator it(this); 3097 for (HeapObject* obj = it.Next(); obj != NULL; obj = it.Next()) { 3098 num_objects++; 3099 CollectHistogramInfo(obj); 3100 } 3101 3102 PrintF(" number of objects %d, " 3103 "size of objects %" V8_PTR_PREFIX "d\n", num_objects, objects_size_); 3104 if (num_objects > 0) ReportHistogram(heap()->isolate(), false); 3105 } 3106 3107 3108 void LargeObjectSpace::CollectCodeStatistics() { 3109 Isolate* isolate = heap()->isolate(); 3110 LargeObjectIterator obj_it(this); 3111 for (HeapObject* obj = obj_it.Next(); obj != NULL; obj = obj_it.Next()) { 3112 if (obj->IsCode()) { 3113 Code* code = Code::cast(obj); 3114 isolate->code_kind_statistics()[code->kind()] += code->Size(); 3115 } 3116 } 3117 } 3118 3119 3120 void Page::Print() { 3121 // Make a best-effort to print the objects in the page. 3122 PrintF("Page@%p in %s\n", 3123 this->address(), 3124 AllocationSpaceName(this->owner()->identity())); 3125 printf(" --------------------------------------\n"); 3126 HeapObjectIterator objects(this, heap()->GcSafeSizeOfOldObjectFunction()); 3127 unsigned mark_size = 0; 3128 for (HeapObject* object = objects.Next(); 3129 object != NULL; 3130 object = objects.Next()) { 3131 bool is_marked = Marking::MarkBitFrom(object).Get(); 3132 PrintF(" %c ", (is_marked ? '!' : ' ')); // Indent a little. 3133 if (is_marked) { 3134 mark_size += heap()->GcSafeSizeOfOldObjectFunction()(object); 3135 } 3136 object->ShortPrint(); 3137 PrintF("\n"); 3138 } 3139 printf(" --------------------------------------\n"); 3140 printf(" Marked: %x, LiveCount: %x\n", mark_size, LiveBytes()); 3141 } 3142 3143 #endif // DEBUG 3144 3145 } } // namespace v8::internal 3146