1 // Copyright 2006-2010 the V8 project authors. All rights reserved. 2 // Redistribution and use in source and binary forms, with or without 3 // modification, are permitted provided that the following conditions are 4 // met: 5 // 6 // * Redistributions of source code must retain the above copyright 7 // notice, this list of conditions and the following disclaimer. 8 // * Redistributions in binary form must reproduce the above 9 // copyright notice, this list of conditions and the following 10 // disclaimer in the documentation and/or other materials provided 11 // with the distribution. 12 // * Neither the name of Google Inc. nor the names of its 13 // contributors may be used to endorse or promote products derived 14 // from this software without specific prior written permission. 15 // 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 28 #include "v8.h" 29 30 #include "liveobjectlist-inl.h" 31 #include "macro-assembler.h" 32 #include "mark-compact.h" 33 #include "platform.h" 34 35 namespace v8 { 36 namespace internal { 37 38 // For contiguous spaces, top should be in the space (or at the end) and limit 39 // should be the end of the space. 40 #define ASSERT_SEMISPACE_ALLOCATION_INFO(info, space) \ 41 ASSERT((space).low() <= (info).top \ 42 && (info).top <= (space).high() \ 43 && (info).limit == (space).high()) 44 45 // ---------------------------------------------------------------------------- 46 // HeapObjectIterator 47 48 HeapObjectIterator::HeapObjectIterator(PagedSpace* space) { 49 Initialize(space->bottom(), space->top(), NULL); 50 } 51 52 53 HeapObjectIterator::HeapObjectIterator(PagedSpace* space, 54 HeapObjectCallback size_func) { 55 Initialize(space->bottom(), space->top(), size_func); 56 } 57 58 59 HeapObjectIterator::HeapObjectIterator(PagedSpace* space, Address start) { 60 Initialize(start, space->top(), NULL); 61 } 62 63 64 HeapObjectIterator::HeapObjectIterator(PagedSpace* space, Address start, 65 HeapObjectCallback size_func) { 66 Initialize(start, space->top(), size_func); 67 } 68 69 70 HeapObjectIterator::HeapObjectIterator(Page* page, 71 HeapObjectCallback size_func) { 72 Initialize(page->ObjectAreaStart(), page->AllocationTop(), size_func); 73 } 74 75 76 void HeapObjectIterator::Initialize(Address cur, Address end, 77 HeapObjectCallback size_f) { 78 cur_addr_ = cur; 79 end_addr_ = end; 80 end_page_ = Page::FromAllocationTop(end); 81 size_func_ = size_f; 82 Page* p = Page::FromAllocationTop(cur_addr_); 83 cur_limit_ = (p == end_page_) ? end_addr_ : p->AllocationTop(); 84 85 #ifdef DEBUG 86 Verify(); 87 #endif 88 } 89 90 91 HeapObject* HeapObjectIterator::FromNextPage() { 92 if (cur_addr_ == end_addr_) return NULL; 93 94 Page* cur_page = Page::FromAllocationTop(cur_addr_); 95 cur_page = cur_page->next_page(); 96 ASSERT(cur_page->is_valid()); 97 98 cur_addr_ = cur_page->ObjectAreaStart(); 99 cur_limit_ = (cur_page == end_page_) ? end_addr_ : cur_page->AllocationTop(); 100 101 if (cur_addr_ == end_addr_) return NULL; 102 ASSERT(cur_addr_ < cur_limit_); 103 #ifdef DEBUG 104 Verify(); 105 #endif 106 return FromCurrentPage(); 107 } 108 109 110 #ifdef DEBUG 111 void HeapObjectIterator::Verify() { 112 Page* p = Page::FromAllocationTop(cur_addr_); 113 ASSERT(p == Page::FromAllocationTop(cur_limit_)); 114 ASSERT(p->Offset(cur_addr_) <= p->Offset(cur_limit_)); 115 } 116 #endif 117 118 119 // ----------------------------------------------------------------------------- 120 // PageIterator 121 122 PageIterator::PageIterator(PagedSpace* space, Mode mode) : space_(space) { 123 prev_page_ = NULL; 124 switch (mode) { 125 case PAGES_IN_USE: 126 stop_page_ = space->AllocationTopPage(); 127 break; 128 case PAGES_USED_BY_MC: 129 stop_page_ = space->MCRelocationTopPage(); 130 break; 131 case ALL_PAGES: 132 #ifdef DEBUG 133 // Verify that the cached last page in the space is actually the 134 // last page. 135 for (Page* p = space->first_page_; p->is_valid(); p = p->next_page()) { 136 if (!p->next_page()->is_valid()) { 137 ASSERT(space->last_page_ == p); 138 } 139 } 140 #endif 141 stop_page_ = space->last_page_; 142 break; 143 } 144 } 145 146 147 // ----------------------------------------------------------------------------- 148 // CodeRange 149 150 151 CodeRange::CodeRange(Isolate* isolate) 152 : isolate_(isolate), 153 code_range_(NULL), 154 free_list_(0), 155 allocation_list_(0), 156 current_allocation_block_index_(0) { 157 } 158 159 160 bool CodeRange::Setup(const size_t requested) { 161 ASSERT(code_range_ == NULL); 162 163 code_range_ = new VirtualMemory(requested); 164 CHECK(code_range_ != NULL); 165 if (!code_range_->IsReserved()) { 166 delete code_range_; 167 code_range_ = NULL; 168 return false; 169 } 170 171 // We are sure that we have mapped a block of requested addresses. 172 ASSERT(code_range_->size() == requested); 173 LOG(isolate_, NewEvent("CodeRange", code_range_->address(), requested)); 174 allocation_list_.Add(FreeBlock(code_range_->address(), code_range_->size())); 175 current_allocation_block_index_ = 0; 176 return true; 177 } 178 179 180 int CodeRange::CompareFreeBlockAddress(const FreeBlock* left, 181 const FreeBlock* right) { 182 // The entire point of CodeRange is that the difference between two 183 // addresses in the range can be represented as a signed 32-bit int, 184 // so the cast is semantically correct. 185 return static_cast<int>(left->start - right->start); 186 } 187 188 189 void CodeRange::GetNextAllocationBlock(size_t requested) { 190 for (current_allocation_block_index_++; 191 current_allocation_block_index_ < allocation_list_.length(); 192 current_allocation_block_index_++) { 193 if (requested <= allocation_list_[current_allocation_block_index_].size) { 194 return; // Found a large enough allocation block. 195 } 196 } 197 198 // Sort and merge the free blocks on the free list and the allocation list. 199 free_list_.AddAll(allocation_list_); 200 allocation_list_.Clear(); 201 free_list_.Sort(&CompareFreeBlockAddress); 202 for (int i = 0; i < free_list_.length();) { 203 FreeBlock merged = free_list_[i]; 204 i++; 205 // Add adjacent free blocks to the current merged block. 206 while (i < free_list_.length() && 207 free_list_[i].start == merged.start + merged.size) { 208 merged.size += free_list_[i].size; 209 i++; 210 } 211 if (merged.size > 0) { 212 allocation_list_.Add(merged); 213 } 214 } 215 free_list_.Clear(); 216 217 for (current_allocation_block_index_ = 0; 218 current_allocation_block_index_ < allocation_list_.length(); 219 current_allocation_block_index_++) { 220 if (requested <= allocation_list_[current_allocation_block_index_].size) { 221 return; // Found a large enough allocation block. 222 } 223 } 224 225 // Code range is full or too fragmented. 226 V8::FatalProcessOutOfMemory("CodeRange::GetNextAllocationBlock"); 227 } 228 229 230 231 void* CodeRange::AllocateRawMemory(const size_t requested, size_t* allocated) { 232 ASSERT(current_allocation_block_index_ < allocation_list_.length()); 233 if (requested > allocation_list_[current_allocation_block_index_].size) { 234 // Find an allocation block large enough. This function call may 235 // call V8::FatalProcessOutOfMemory if it cannot find a large enough block. 236 GetNextAllocationBlock(requested); 237 } 238 // Commit the requested memory at the start of the current allocation block. 239 *allocated = RoundUp(requested, Page::kPageSize); 240 FreeBlock current = allocation_list_[current_allocation_block_index_]; 241 if (*allocated >= current.size - Page::kPageSize) { 242 // Don't leave a small free block, useless for a large object or chunk. 243 *allocated = current.size; 244 } 245 ASSERT(*allocated <= current.size); 246 if (!code_range_->Commit(current.start, *allocated, true)) { 247 *allocated = 0; 248 return NULL; 249 } 250 allocation_list_[current_allocation_block_index_].start += *allocated; 251 allocation_list_[current_allocation_block_index_].size -= *allocated; 252 if (*allocated == current.size) { 253 GetNextAllocationBlock(0); // This block is used up, get the next one. 254 } 255 return current.start; 256 } 257 258 259 void CodeRange::FreeRawMemory(void* address, size_t length) { 260 free_list_.Add(FreeBlock(address, length)); 261 code_range_->Uncommit(address, length); 262 } 263 264 265 void CodeRange::TearDown() { 266 delete code_range_; // Frees all memory in the virtual memory range. 267 code_range_ = NULL; 268 free_list_.Free(); 269 allocation_list_.Free(); 270 } 271 272 273 // ----------------------------------------------------------------------------- 274 // MemoryAllocator 275 // 276 277 // 270 is an estimate based on the static default heap size of a pair of 256K 278 // semispaces and a 64M old generation. 279 const int kEstimatedNumberOfChunks = 270; 280 281 282 MemoryAllocator::MemoryAllocator(Isolate* isolate) 283 : isolate_(isolate), 284 capacity_(0), 285 capacity_executable_(0), 286 size_(0), 287 size_executable_(0), 288 initial_chunk_(NULL), 289 chunks_(kEstimatedNumberOfChunks), 290 free_chunk_ids_(kEstimatedNumberOfChunks), 291 max_nof_chunks_(0), 292 top_(0) { 293 } 294 295 296 void MemoryAllocator::Push(int free_chunk_id) { 297 ASSERT(max_nof_chunks_ > 0); 298 ASSERT(top_ < max_nof_chunks_); 299 free_chunk_ids_[top_++] = free_chunk_id; 300 } 301 302 303 int MemoryAllocator::Pop() { 304 ASSERT(top_ > 0); 305 return free_chunk_ids_[--top_]; 306 } 307 308 309 bool MemoryAllocator::Setup(intptr_t capacity, intptr_t capacity_executable) { 310 capacity_ = RoundUp(capacity, Page::kPageSize); 311 capacity_executable_ = RoundUp(capacity_executable, Page::kPageSize); 312 ASSERT_GE(capacity_, capacity_executable_); 313 314 // Over-estimate the size of chunks_ array. It assumes the expansion of old 315 // space is always in the unit of a chunk (kChunkSize) except the last 316 // expansion. 317 // 318 // Due to alignment, allocated space might be one page less than required 319 // number (kPagesPerChunk) of pages for old spaces. 320 // 321 // Reserve two chunk ids for semispaces, one for map space, one for old 322 // space, and one for code space. 323 max_nof_chunks_ = 324 static_cast<int>((capacity_ / (kChunkSize - Page::kPageSize))) + 5; 325 if (max_nof_chunks_ > kMaxNofChunks) return false; 326 327 size_ = 0; 328 size_executable_ = 0; 329 ChunkInfo info; // uninitialized element. 330 for (int i = max_nof_chunks_ - 1; i >= 0; i--) { 331 chunks_.Add(info); 332 free_chunk_ids_.Add(i); 333 } 334 top_ = max_nof_chunks_; 335 return true; 336 } 337 338 339 void MemoryAllocator::TearDown() { 340 for (int i = 0; i < max_nof_chunks_; i++) { 341 if (chunks_[i].address() != NULL) DeleteChunk(i); 342 } 343 chunks_.Clear(); 344 free_chunk_ids_.Clear(); 345 346 if (initial_chunk_ != NULL) { 347 LOG(isolate_, DeleteEvent("InitialChunk", initial_chunk_->address())); 348 delete initial_chunk_; 349 initial_chunk_ = NULL; 350 } 351 352 ASSERT(top_ == max_nof_chunks_); // all chunks are free 353 top_ = 0; 354 capacity_ = 0; 355 capacity_executable_ = 0; 356 size_ = 0; 357 max_nof_chunks_ = 0; 358 } 359 360 361 void* MemoryAllocator::AllocateRawMemory(const size_t requested, 362 size_t* allocated, 363 Executability executable) { 364 if (size_ + static_cast<size_t>(requested) > static_cast<size_t>(capacity_)) { 365 return NULL; 366 } 367 368 void* mem; 369 if (executable == EXECUTABLE) { 370 // Check executable memory limit. 371 if (size_executable_ + requested > 372 static_cast<size_t>(capacity_executable_)) { 373 LOG(isolate_, 374 StringEvent("MemoryAllocator::AllocateRawMemory", 375 "V8 Executable Allocation capacity exceeded")); 376 return NULL; 377 } 378 // Allocate executable memory either from code range or from the 379 // OS. 380 if (isolate_->code_range()->exists()) { 381 mem = isolate_->code_range()->AllocateRawMemory(requested, allocated); 382 } else { 383 mem = OS::Allocate(requested, allocated, true); 384 } 385 // Update executable memory size. 386 size_executable_ += static_cast<int>(*allocated); 387 } else { 388 mem = OS::Allocate(requested, allocated, false); 389 } 390 int alloced = static_cast<int>(*allocated); 391 size_ += alloced; 392 393 #ifdef DEBUG 394 ZapBlock(reinterpret_cast<Address>(mem), alloced); 395 #endif 396 isolate_->counters()->memory_allocated()->Increment(alloced); 397 return mem; 398 } 399 400 401 void MemoryAllocator::FreeRawMemory(void* mem, 402 size_t length, 403 Executability executable) { 404 #ifdef DEBUG 405 ZapBlock(reinterpret_cast<Address>(mem), length); 406 #endif 407 if (isolate_->code_range()->contains(static_cast<Address>(mem))) { 408 isolate_->code_range()->FreeRawMemory(mem, length); 409 } else { 410 OS::Free(mem, length); 411 } 412 isolate_->counters()->memory_allocated()->Decrement(static_cast<int>(length)); 413 size_ -= static_cast<int>(length); 414 if (executable == EXECUTABLE) size_executable_ -= static_cast<int>(length); 415 416 ASSERT(size_ >= 0); 417 ASSERT(size_executable_ >= 0); 418 } 419 420 421 void MemoryAllocator::PerformAllocationCallback(ObjectSpace space, 422 AllocationAction action, 423 size_t size) { 424 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { 425 MemoryAllocationCallbackRegistration registration = 426 memory_allocation_callbacks_[i]; 427 if ((registration.space & space) == space && 428 (registration.action & action) == action) 429 registration.callback(space, action, static_cast<int>(size)); 430 } 431 } 432 433 434 bool MemoryAllocator::MemoryAllocationCallbackRegistered( 435 MemoryAllocationCallback callback) { 436 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { 437 if (memory_allocation_callbacks_[i].callback == callback) return true; 438 } 439 return false; 440 } 441 442 443 void MemoryAllocator::AddMemoryAllocationCallback( 444 MemoryAllocationCallback callback, 445 ObjectSpace space, 446 AllocationAction action) { 447 ASSERT(callback != NULL); 448 MemoryAllocationCallbackRegistration registration(callback, space, action); 449 ASSERT(!MemoryAllocator::MemoryAllocationCallbackRegistered(callback)); 450 return memory_allocation_callbacks_.Add(registration); 451 } 452 453 454 void MemoryAllocator::RemoveMemoryAllocationCallback( 455 MemoryAllocationCallback callback) { 456 ASSERT(callback != NULL); 457 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { 458 if (memory_allocation_callbacks_[i].callback == callback) { 459 memory_allocation_callbacks_.Remove(i); 460 return; 461 } 462 } 463 UNREACHABLE(); 464 } 465 466 void* MemoryAllocator::ReserveInitialChunk(const size_t requested) { 467 ASSERT(initial_chunk_ == NULL); 468 469 initial_chunk_ = new VirtualMemory(requested); 470 CHECK(initial_chunk_ != NULL); 471 if (!initial_chunk_->IsReserved()) { 472 delete initial_chunk_; 473 initial_chunk_ = NULL; 474 return NULL; 475 } 476 477 // We are sure that we have mapped a block of requested addresses. 478 ASSERT(initial_chunk_->size() == requested); 479 LOG(isolate_, 480 NewEvent("InitialChunk", initial_chunk_->address(), requested)); 481 size_ += static_cast<int>(requested); 482 return initial_chunk_->address(); 483 } 484 485 486 static int PagesInChunk(Address start, size_t size) { 487 // The first page starts on the first page-aligned address from start onward 488 // and the last page ends on the last page-aligned address before 489 // start+size. Page::kPageSize is a power of two so we can divide by 490 // shifting. 491 return static_cast<int>((RoundDown(start + size, Page::kPageSize) 492 - RoundUp(start, Page::kPageSize)) >> kPageSizeBits); 493 } 494 495 496 Page* MemoryAllocator::AllocatePages(int requested_pages, 497 int* allocated_pages, 498 PagedSpace* owner) { 499 if (requested_pages <= 0) return Page::FromAddress(NULL); 500 size_t chunk_size = requested_pages * Page::kPageSize; 501 502 void* chunk = AllocateRawMemory(chunk_size, &chunk_size, owner->executable()); 503 if (chunk == NULL) return Page::FromAddress(NULL); 504 LOG(isolate_, NewEvent("PagedChunk", chunk, chunk_size)); 505 506 *allocated_pages = PagesInChunk(static_cast<Address>(chunk), chunk_size); 507 // We may 'lose' a page due to alignment. 508 ASSERT(*allocated_pages >= kPagesPerChunk - 1); 509 if (*allocated_pages == 0) { 510 FreeRawMemory(chunk, chunk_size, owner->executable()); 511 LOG(isolate_, DeleteEvent("PagedChunk", chunk)); 512 return Page::FromAddress(NULL); 513 } 514 515 int chunk_id = Pop(); 516 chunks_[chunk_id].init(static_cast<Address>(chunk), chunk_size, owner); 517 518 ObjectSpace space = static_cast<ObjectSpace>(1 << owner->identity()); 519 PerformAllocationCallback(space, kAllocationActionAllocate, chunk_size); 520 Page* new_pages = InitializePagesInChunk(chunk_id, *allocated_pages, owner); 521 522 return new_pages; 523 } 524 525 526 Page* MemoryAllocator::CommitPages(Address start, size_t size, 527 PagedSpace* owner, int* num_pages) { 528 ASSERT(start != NULL); 529 *num_pages = PagesInChunk(start, size); 530 ASSERT(*num_pages > 0); 531 ASSERT(initial_chunk_ != NULL); 532 ASSERT(InInitialChunk(start)); 533 ASSERT(InInitialChunk(start + size - 1)); 534 if (!initial_chunk_->Commit(start, size, owner->executable() == EXECUTABLE)) { 535 return Page::FromAddress(NULL); 536 } 537 #ifdef DEBUG 538 ZapBlock(start, size); 539 #endif 540 isolate_->counters()->memory_allocated()->Increment(static_cast<int>(size)); 541 542 // So long as we correctly overestimated the number of chunks we should not 543 // run out of chunk ids. 544 CHECK(!OutOfChunkIds()); 545 int chunk_id = Pop(); 546 chunks_[chunk_id].init(start, size, owner); 547 return InitializePagesInChunk(chunk_id, *num_pages, owner); 548 } 549 550 551 bool MemoryAllocator::CommitBlock(Address start, 552 size_t size, 553 Executability executable) { 554 ASSERT(start != NULL); 555 ASSERT(size > 0); 556 ASSERT(initial_chunk_ != NULL); 557 ASSERT(InInitialChunk(start)); 558 ASSERT(InInitialChunk(start + size - 1)); 559 560 if (!initial_chunk_->Commit(start, size, executable)) return false; 561 #ifdef DEBUG 562 ZapBlock(start, size); 563 #endif 564 isolate_->counters()->memory_allocated()->Increment(static_cast<int>(size)); 565 return true; 566 } 567 568 569 bool MemoryAllocator::UncommitBlock(Address start, size_t size) { 570 ASSERT(start != NULL); 571 ASSERT(size > 0); 572 ASSERT(initial_chunk_ != NULL); 573 ASSERT(InInitialChunk(start)); 574 ASSERT(InInitialChunk(start + size - 1)); 575 576 if (!initial_chunk_->Uncommit(start, size)) return false; 577 isolate_->counters()->memory_allocated()->Decrement(static_cast<int>(size)); 578 return true; 579 } 580 581 582 void MemoryAllocator::ZapBlock(Address start, size_t size) { 583 for (size_t s = 0; s + kPointerSize <= size; s += kPointerSize) { 584 Memory::Address_at(start + s) = kZapValue; 585 } 586 } 587 588 589 Page* MemoryAllocator::InitializePagesInChunk(int chunk_id, int pages_in_chunk, 590 PagedSpace* owner) { 591 ASSERT(IsValidChunk(chunk_id)); 592 ASSERT(pages_in_chunk > 0); 593 594 Address chunk_start = chunks_[chunk_id].address(); 595 596 Address low = RoundUp(chunk_start, Page::kPageSize); 597 598 #ifdef DEBUG 599 size_t chunk_size = chunks_[chunk_id].size(); 600 Address high = RoundDown(chunk_start + chunk_size, Page::kPageSize); 601 ASSERT(pages_in_chunk <= 602 ((OffsetFrom(high) - OffsetFrom(low)) / Page::kPageSize)); 603 #endif 604 605 Address page_addr = low; 606 for (int i = 0; i < pages_in_chunk; i++) { 607 Page* p = Page::FromAddress(page_addr); 608 p->heap_ = owner->heap(); 609 p->opaque_header = OffsetFrom(page_addr + Page::kPageSize) | chunk_id; 610 p->InvalidateWatermark(true); 611 p->SetIsLargeObjectPage(false); 612 p->SetAllocationWatermark(p->ObjectAreaStart()); 613 p->SetCachedAllocationWatermark(p->ObjectAreaStart()); 614 page_addr += Page::kPageSize; 615 } 616 617 // Set the next page of the last page to 0. 618 Page* last_page = Page::FromAddress(page_addr - Page::kPageSize); 619 last_page->opaque_header = OffsetFrom(0) | chunk_id; 620 621 return Page::FromAddress(low); 622 } 623 624 625 Page* MemoryAllocator::FreePages(Page* p) { 626 if (!p->is_valid()) return p; 627 628 // Find the first page in the same chunk as 'p' 629 Page* first_page = FindFirstPageInSameChunk(p); 630 Page* page_to_return = Page::FromAddress(NULL); 631 632 if (p != first_page) { 633 // Find the last page in the same chunk as 'prev'. 634 Page* last_page = FindLastPageInSameChunk(p); 635 first_page = GetNextPage(last_page); // first page in next chunk 636 637 // set the next_page of last_page to NULL 638 SetNextPage(last_page, Page::FromAddress(NULL)); 639 page_to_return = p; // return 'p' when exiting 640 } 641 642 while (first_page->is_valid()) { 643 int chunk_id = GetChunkId(first_page); 644 ASSERT(IsValidChunk(chunk_id)); 645 646 // Find the first page of the next chunk before deleting this chunk. 647 first_page = GetNextPage(FindLastPageInSameChunk(first_page)); 648 649 // Free the current chunk. 650 DeleteChunk(chunk_id); 651 } 652 653 return page_to_return; 654 } 655 656 657 void MemoryAllocator::FreeAllPages(PagedSpace* space) { 658 for (int i = 0, length = chunks_.length(); i < length; i++) { 659 if (chunks_[i].owner() == space) { 660 DeleteChunk(i); 661 } 662 } 663 } 664 665 666 void MemoryAllocator::DeleteChunk(int chunk_id) { 667 ASSERT(IsValidChunk(chunk_id)); 668 669 ChunkInfo& c = chunks_[chunk_id]; 670 671 // We cannot free a chunk contained in the initial chunk because it was not 672 // allocated with AllocateRawMemory. Instead we uncommit the virtual 673 // memory. 674 if (InInitialChunk(c.address())) { 675 // TODO(1240712): VirtualMemory::Uncommit has a return value which 676 // is ignored here. 677 initial_chunk_->Uncommit(c.address(), c.size()); 678 Counters* counters = isolate_->counters(); 679 counters->memory_allocated()->Decrement(static_cast<int>(c.size())); 680 } else { 681 LOG(isolate_, DeleteEvent("PagedChunk", c.address())); 682 ObjectSpace space = static_cast<ObjectSpace>(1 << c.owner_identity()); 683 size_t size = c.size(); 684 FreeRawMemory(c.address(), size, c.executable()); 685 PerformAllocationCallback(space, kAllocationActionFree, size); 686 } 687 c.init(NULL, 0, NULL); 688 Push(chunk_id); 689 } 690 691 692 Page* MemoryAllocator::FindFirstPageInSameChunk(Page* p) { 693 int chunk_id = GetChunkId(p); 694 ASSERT(IsValidChunk(chunk_id)); 695 696 Address low = RoundUp(chunks_[chunk_id].address(), Page::kPageSize); 697 return Page::FromAddress(low); 698 } 699 700 701 Page* MemoryAllocator::FindLastPageInSameChunk(Page* p) { 702 int chunk_id = GetChunkId(p); 703 ASSERT(IsValidChunk(chunk_id)); 704 705 Address chunk_start = chunks_[chunk_id].address(); 706 size_t chunk_size = chunks_[chunk_id].size(); 707 708 Address high = RoundDown(chunk_start + chunk_size, Page::kPageSize); 709 ASSERT(chunk_start <= p->address() && p->address() < high); 710 711 return Page::FromAddress(high - Page::kPageSize); 712 } 713 714 715 #ifdef DEBUG 716 void MemoryAllocator::ReportStatistics() { 717 float pct = static_cast<float>(capacity_ - size_) / capacity_; 718 PrintF(" capacity: %" V8_PTR_PREFIX "d" 719 ", used: %" V8_PTR_PREFIX "d" 720 ", available: %%%d\n\n", 721 capacity_, size_, static_cast<int>(pct*100)); 722 } 723 #endif 724 725 726 void MemoryAllocator::RelinkPageListInChunkOrder(PagedSpace* space, 727 Page** first_page, 728 Page** last_page, 729 Page** last_page_in_use) { 730 Page* first = NULL; 731 Page* last = NULL; 732 733 for (int i = 0, length = chunks_.length(); i < length; i++) { 734 ChunkInfo& chunk = chunks_[i]; 735 736 if (chunk.owner() == space) { 737 if (first == NULL) { 738 Address low = RoundUp(chunk.address(), Page::kPageSize); 739 first = Page::FromAddress(low); 740 } 741 last = RelinkPagesInChunk(i, 742 chunk.address(), 743 chunk.size(), 744 last, 745 last_page_in_use); 746 } 747 } 748 749 if (first_page != NULL) { 750 *first_page = first; 751 } 752 753 if (last_page != NULL) { 754 *last_page = last; 755 } 756 } 757 758 759 Page* MemoryAllocator::RelinkPagesInChunk(int chunk_id, 760 Address chunk_start, 761 size_t chunk_size, 762 Page* prev, 763 Page** last_page_in_use) { 764 Address page_addr = RoundUp(chunk_start, Page::kPageSize); 765 int pages_in_chunk = PagesInChunk(chunk_start, chunk_size); 766 767 if (prev->is_valid()) { 768 SetNextPage(prev, Page::FromAddress(page_addr)); 769 } 770 771 for (int i = 0; i < pages_in_chunk; i++) { 772 Page* p = Page::FromAddress(page_addr); 773 p->opaque_header = OffsetFrom(page_addr + Page::kPageSize) | chunk_id; 774 page_addr += Page::kPageSize; 775 776 p->InvalidateWatermark(true); 777 if (p->WasInUseBeforeMC()) { 778 *last_page_in_use = p; 779 } 780 } 781 782 // Set the next page of the last page to 0. 783 Page* last_page = Page::FromAddress(page_addr - Page::kPageSize); 784 last_page->opaque_header = OffsetFrom(0) | chunk_id; 785 786 if (last_page->WasInUseBeforeMC()) { 787 *last_page_in_use = last_page; 788 } 789 790 return last_page; 791 } 792 793 794 // ----------------------------------------------------------------------------- 795 // PagedSpace implementation 796 797 PagedSpace::PagedSpace(Heap* heap, 798 intptr_t max_capacity, 799 AllocationSpace id, 800 Executability executable) 801 : Space(heap, id, executable) { 802 max_capacity_ = (RoundDown(max_capacity, Page::kPageSize) / Page::kPageSize) 803 * Page::kObjectAreaSize; 804 accounting_stats_.Clear(); 805 806 allocation_info_.top = NULL; 807 allocation_info_.limit = NULL; 808 809 mc_forwarding_info_.top = NULL; 810 mc_forwarding_info_.limit = NULL; 811 } 812 813 814 bool PagedSpace::Setup(Address start, size_t size) { 815 if (HasBeenSetup()) return false; 816 817 int num_pages = 0; 818 // Try to use the virtual memory range passed to us. If it is too small to 819 // contain at least one page, ignore it and allocate instead. 820 int pages_in_chunk = PagesInChunk(start, size); 821 if (pages_in_chunk > 0) { 822 first_page_ = Isolate::Current()->memory_allocator()->CommitPages( 823 RoundUp(start, Page::kPageSize), 824 Page::kPageSize * pages_in_chunk, 825 this, &num_pages); 826 } else { 827 int requested_pages = 828 Min(MemoryAllocator::kPagesPerChunk, 829 static_cast<int>(max_capacity_ / Page::kObjectAreaSize)); 830 first_page_ = 831 Isolate::Current()->memory_allocator()->AllocatePages( 832 requested_pages, &num_pages, this); 833 if (!first_page_->is_valid()) return false; 834 } 835 836 // We are sure that the first page is valid and that we have at least one 837 // page. 838 ASSERT(first_page_->is_valid()); 839 ASSERT(num_pages > 0); 840 accounting_stats_.ExpandSpace(num_pages * Page::kObjectAreaSize); 841 ASSERT(Capacity() <= max_capacity_); 842 843 // Sequentially clear region marks in the newly allocated 844 // pages and cache the current last page in the space. 845 for (Page* p = first_page_; p->is_valid(); p = p->next_page()) { 846 p->SetRegionMarks(Page::kAllRegionsCleanMarks); 847 last_page_ = p; 848 } 849 850 // Use first_page_ for allocation. 851 SetAllocationInfo(&allocation_info_, first_page_); 852 853 page_list_is_chunk_ordered_ = true; 854 855 return true; 856 } 857 858 859 bool PagedSpace::HasBeenSetup() { 860 return (Capacity() > 0); 861 } 862 863 864 void PagedSpace::TearDown() { 865 Isolate::Current()->memory_allocator()->FreeAllPages(this); 866 first_page_ = NULL; 867 accounting_stats_.Clear(); 868 } 869 870 871 #ifdef ENABLE_HEAP_PROTECTION 872 873 void PagedSpace::Protect() { 874 Page* page = first_page_; 875 while (page->is_valid()) { 876 Isolate::Current()->memory_allocator()->ProtectChunkFromPage(page); 877 page = Isolate::Current()->memory_allocator()-> 878 FindLastPageInSameChunk(page)->next_page(); 879 } 880 } 881 882 883 void PagedSpace::Unprotect() { 884 Page* page = first_page_; 885 while (page->is_valid()) { 886 Isolate::Current()->memory_allocator()->UnprotectChunkFromPage(page); 887 page = Isolate::Current()->memory_allocator()-> 888 FindLastPageInSameChunk(page)->next_page(); 889 } 890 } 891 892 #endif 893 894 895 void PagedSpace::MarkAllPagesClean() { 896 PageIterator it(this, PageIterator::ALL_PAGES); 897 while (it.has_next()) { 898 it.next()->SetRegionMarks(Page::kAllRegionsCleanMarks); 899 } 900 } 901 902 903 MaybeObject* PagedSpace::FindObject(Address addr) { 904 // Note: this function can only be called before or after mark-compact GC 905 // because it accesses map pointers. 906 ASSERT(!heap()->mark_compact_collector()->in_use()); 907 908 if (!Contains(addr)) return Failure::Exception(); 909 910 Page* p = Page::FromAddress(addr); 911 ASSERT(IsUsed(p)); 912 Address cur = p->ObjectAreaStart(); 913 Address end = p->AllocationTop(); 914 while (cur < end) { 915 HeapObject* obj = HeapObject::FromAddress(cur); 916 Address next = cur + obj->Size(); 917 if ((cur <= addr) && (addr < next)) return obj; 918 cur = next; 919 } 920 921 UNREACHABLE(); 922 return Failure::Exception(); 923 } 924 925 926 bool PagedSpace::IsUsed(Page* page) { 927 PageIterator it(this, PageIterator::PAGES_IN_USE); 928 while (it.has_next()) { 929 if (page == it.next()) return true; 930 } 931 return false; 932 } 933 934 935 void PagedSpace::SetAllocationInfo(AllocationInfo* alloc_info, Page* p) { 936 alloc_info->top = p->ObjectAreaStart(); 937 alloc_info->limit = p->ObjectAreaEnd(); 938 ASSERT(alloc_info->VerifyPagedAllocation()); 939 } 940 941 942 void PagedSpace::MCResetRelocationInfo() { 943 // Set page indexes. 944 int i = 0; 945 PageIterator it(this, PageIterator::ALL_PAGES); 946 while (it.has_next()) { 947 Page* p = it.next(); 948 p->mc_page_index = i++; 949 } 950 951 // Set mc_forwarding_info_ to the first page in the space. 952 SetAllocationInfo(&mc_forwarding_info_, first_page_); 953 // All the bytes in the space are 'available'. We will rediscover 954 // allocated and wasted bytes during GC. 955 accounting_stats_.Reset(); 956 } 957 958 959 int PagedSpace::MCSpaceOffsetForAddress(Address addr) { 960 #ifdef DEBUG 961 // The Contains function considers the address at the beginning of a 962 // page in the page, MCSpaceOffsetForAddress considers it is in the 963 // previous page. 964 if (Page::IsAlignedToPageSize(addr)) { 965 ASSERT(Contains(addr - kPointerSize)); 966 } else { 967 ASSERT(Contains(addr)); 968 } 969 #endif 970 971 // If addr is at the end of a page, it belongs to previous page 972 Page* p = Page::IsAlignedToPageSize(addr) 973 ? Page::FromAllocationTop(addr) 974 : Page::FromAddress(addr); 975 int index = p->mc_page_index; 976 return (index * Page::kPageSize) + p->Offset(addr); 977 } 978 979 980 // Slow case for reallocating and promoting objects during a compacting 981 // collection. This function is not space-specific. 982 HeapObject* PagedSpace::SlowMCAllocateRaw(int size_in_bytes) { 983 Page* current_page = TopPageOf(mc_forwarding_info_); 984 if (!current_page->next_page()->is_valid()) { 985 if (!Expand(current_page)) { 986 return NULL; 987 } 988 } 989 990 // There are surely more pages in the space now. 991 ASSERT(current_page->next_page()->is_valid()); 992 // We do not add the top of page block for current page to the space's 993 // free list---the block may contain live objects so we cannot write 994 // bookkeeping information to it. Instead, we will recover top of page 995 // blocks when we move objects to their new locations. 996 // 997 // We do however write the allocation pointer to the page. The encoding 998 // of forwarding addresses is as an offset in terms of live bytes, so we 999 // need quick access to the allocation top of each page to decode 1000 // forwarding addresses. 1001 current_page->SetAllocationWatermark(mc_forwarding_info_.top); 1002 current_page->next_page()->InvalidateWatermark(true); 1003 SetAllocationInfo(&mc_forwarding_info_, current_page->next_page()); 1004 return AllocateLinearly(&mc_forwarding_info_, size_in_bytes); 1005 } 1006 1007 1008 bool PagedSpace::Expand(Page* last_page) { 1009 ASSERT(max_capacity_ % Page::kObjectAreaSize == 0); 1010 ASSERT(Capacity() % Page::kObjectAreaSize == 0); 1011 1012 if (Capacity() == max_capacity_) return false; 1013 1014 ASSERT(Capacity() < max_capacity_); 1015 // Last page must be valid and its next page is invalid. 1016 ASSERT(last_page->is_valid() && !last_page->next_page()->is_valid()); 1017 1018 int available_pages = 1019 static_cast<int>((max_capacity_ - Capacity()) / Page::kObjectAreaSize); 1020 // We don't want to have to handle small chunks near the end so if there are 1021 // not kPagesPerChunk pages available without exceeding the max capacity then 1022 // act as if memory has run out. 1023 if (available_pages < MemoryAllocator::kPagesPerChunk) return false; 1024 1025 int desired_pages = Min(available_pages, MemoryAllocator::kPagesPerChunk); 1026 Page* p = heap()->isolate()->memory_allocator()->AllocatePages( 1027 desired_pages, &desired_pages, this); 1028 if (!p->is_valid()) return false; 1029 1030 accounting_stats_.ExpandSpace(desired_pages * Page::kObjectAreaSize); 1031 ASSERT(Capacity() <= max_capacity_); 1032 1033 heap()->isolate()->memory_allocator()->SetNextPage(last_page, p); 1034 1035 // Sequentially clear region marks of new pages and and cache the 1036 // new last page in the space. 1037 while (p->is_valid()) { 1038 p->SetRegionMarks(Page::kAllRegionsCleanMarks); 1039 last_page_ = p; 1040 p = p->next_page(); 1041 } 1042 1043 return true; 1044 } 1045 1046 1047 #ifdef DEBUG 1048 int PagedSpace::CountTotalPages() { 1049 int count = 0; 1050 for (Page* p = first_page_; p->is_valid(); p = p->next_page()) { 1051 count++; 1052 } 1053 return count; 1054 } 1055 #endif 1056 1057 1058 void PagedSpace::Shrink() { 1059 if (!page_list_is_chunk_ordered_) { 1060 // We can't shrink space if pages is not chunk-ordered 1061 // (see comment for class MemoryAllocator for definition). 1062 return; 1063 } 1064 1065 // Release half of free pages. 1066 Page* top_page = AllocationTopPage(); 1067 ASSERT(top_page->is_valid()); 1068 1069 // Count the number of pages we would like to free. 1070 int pages_to_free = 0; 1071 for (Page* p = top_page->next_page(); p->is_valid(); p = p->next_page()) { 1072 pages_to_free++; 1073 } 1074 1075 // Free pages after top_page. 1076 Page* p = heap()->isolate()->memory_allocator()-> 1077 FreePages(top_page->next_page()); 1078 heap()->isolate()->memory_allocator()->SetNextPage(top_page, p); 1079 1080 // Find out how many pages we failed to free and update last_page_. 1081 // Please note pages can only be freed in whole chunks. 1082 last_page_ = top_page; 1083 for (Page* p = top_page->next_page(); p->is_valid(); p = p->next_page()) { 1084 pages_to_free--; 1085 last_page_ = p; 1086 } 1087 1088 accounting_stats_.ShrinkSpace(pages_to_free * Page::kObjectAreaSize); 1089 ASSERT(Capacity() == CountTotalPages() * Page::kObjectAreaSize); 1090 } 1091 1092 1093 bool PagedSpace::EnsureCapacity(int capacity) { 1094 if (Capacity() >= capacity) return true; 1095 1096 // Start from the allocation top and loop to the last page in the space. 1097 Page* last_page = AllocationTopPage(); 1098 Page* next_page = last_page->next_page(); 1099 while (next_page->is_valid()) { 1100 last_page = heap()->isolate()->memory_allocator()-> 1101 FindLastPageInSameChunk(next_page); 1102 next_page = last_page->next_page(); 1103 } 1104 1105 // Expand the space until it has the required capacity or expansion fails. 1106 do { 1107 if (!Expand(last_page)) return false; 1108 ASSERT(last_page->next_page()->is_valid()); 1109 last_page = 1110 heap()->isolate()->memory_allocator()->FindLastPageInSameChunk( 1111 last_page->next_page()); 1112 } while (Capacity() < capacity); 1113 1114 return true; 1115 } 1116 1117 1118 #ifdef DEBUG 1119 void PagedSpace::Print() { } 1120 #endif 1121 1122 1123 #ifdef DEBUG 1124 // We do not assume that the PageIterator works, because it depends on the 1125 // invariants we are checking during verification. 1126 void PagedSpace::Verify(ObjectVisitor* visitor) { 1127 // The allocation pointer should be valid, and it should be in a page in the 1128 // space. 1129 ASSERT(allocation_info_.VerifyPagedAllocation()); 1130 Page* top_page = Page::FromAllocationTop(allocation_info_.top); 1131 ASSERT(heap()->isolate()->memory_allocator()->IsPageInSpace(top_page, this)); 1132 1133 // Loop over all the pages. 1134 bool above_allocation_top = false; 1135 Page* current_page = first_page_; 1136 while (current_page->is_valid()) { 1137 if (above_allocation_top) { 1138 // We don't care what's above the allocation top. 1139 } else { 1140 Address top = current_page->AllocationTop(); 1141 if (current_page == top_page) { 1142 ASSERT(top == allocation_info_.top); 1143 // The next page will be above the allocation top. 1144 above_allocation_top = true; 1145 } 1146 1147 // It should be packed with objects from the bottom to the top. 1148 Address current = current_page->ObjectAreaStart(); 1149 while (current < top) { 1150 HeapObject* object = HeapObject::FromAddress(current); 1151 1152 // The first word should be a map, and we expect all map pointers to 1153 // be in map space. 1154 Map* map = object->map(); 1155 ASSERT(map->IsMap()); 1156 ASSERT(heap()->map_space()->Contains(map)); 1157 1158 // Perform space-specific object verification. 1159 VerifyObject(object); 1160 1161 // The object itself should look OK. 1162 object->Verify(); 1163 1164 // All the interior pointers should be contained in the heap and 1165 // have page regions covering intergenerational references should be 1166 // marked dirty. 1167 int size = object->Size(); 1168 object->IterateBody(map->instance_type(), size, visitor); 1169 1170 current += size; 1171 } 1172 1173 // The allocation pointer should not be in the middle of an object. 1174 ASSERT(current == top); 1175 } 1176 1177 current_page = current_page->next_page(); 1178 } 1179 } 1180 #endif 1181 1182 1183 // ----------------------------------------------------------------------------- 1184 // NewSpace implementation 1185 1186 1187 bool NewSpace::Setup(Address start, int size) { 1188 // Setup new space based on the preallocated memory block defined by 1189 // start and size. The provided space is divided into two semi-spaces. 1190 // To support fast containment testing in the new space, the size of 1191 // this chunk must be a power of two and it must be aligned to its size. 1192 int initial_semispace_capacity = heap()->InitialSemiSpaceSize(); 1193 int maximum_semispace_capacity = heap()->MaxSemiSpaceSize(); 1194 1195 ASSERT(initial_semispace_capacity <= maximum_semispace_capacity); 1196 ASSERT(IsPowerOf2(maximum_semispace_capacity)); 1197 1198 // Allocate and setup the histogram arrays if necessary. 1199 #if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING) 1200 allocated_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1); 1201 promoted_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1); 1202 1203 #define SET_NAME(name) allocated_histogram_[name].set_name(#name); \ 1204 promoted_histogram_[name].set_name(#name); 1205 INSTANCE_TYPE_LIST(SET_NAME) 1206 #undef SET_NAME 1207 #endif 1208 1209 ASSERT(size == 2 * heap()->ReservedSemiSpaceSize()); 1210 ASSERT(IsAddressAligned(start, size, 0)); 1211 1212 if (!to_space_.Setup(start, 1213 initial_semispace_capacity, 1214 maximum_semispace_capacity)) { 1215 return false; 1216 } 1217 if (!from_space_.Setup(start + maximum_semispace_capacity, 1218 initial_semispace_capacity, 1219 maximum_semispace_capacity)) { 1220 return false; 1221 } 1222 1223 start_ = start; 1224 address_mask_ = ~(size - 1); 1225 object_mask_ = address_mask_ | kHeapObjectTagMask; 1226 object_expected_ = reinterpret_cast<uintptr_t>(start) | kHeapObjectTag; 1227 1228 allocation_info_.top = to_space_.low(); 1229 allocation_info_.limit = to_space_.high(); 1230 mc_forwarding_info_.top = NULL; 1231 mc_forwarding_info_.limit = NULL; 1232 1233 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); 1234 return true; 1235 } 1236 1237 1238 void NewSpace::TearDown() { 1239 #if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING) 1240 if (allocated_histogram_) { 1241 DeleteArray(allocated_histogram_); 1242 allocated_histogram_ = NULL; 1243 } 1244 if (promoted_histogram_) { 1245 DeleteArray(promoted_histogram_); 1246 promoted_histogram_ = NULL; 1247 } 1248 #endif 1249 1250 start_ = NULL; 1251 allocation_info_.top = NULL; 1252 allocation_info_.limit = NULL; 1253 mc_forwarding_info_.top = NULL; 1254 mc_forwarding_info_.limit = NULL; 1255 1256 to_space_.TearDown(); 1257 from_space_.TearDown(); 1258 } 1259 1260 1261 #ifdef ENABLE_HEAP_PROTECTION 1262 1263 void NewSpace::Protect() { 1264 heap()->isolate()->memory_allocator()->Protect(ToSpaceLow(), Capacity()); 1265 heap()->isolate()->memory_allocator()->Protect(FromSpaceLow(), Capacity()); 1266 } 1267 1268 1269 void NewSpace::Unprotect() { 1270 heap()->isolate()->memory_allocator()->Unprotect(ToSpaceLow(), Capacity(), 1271 to_space_.executable()); 1272 heap()->isolate()->memory_allocator()->Unprotect(FromSpaceLow(), Capacity(), 1273 from_space_.executable()); 1274 } 1275 1276 #endif 1277 1278 1279 void NewSpace::Flip() { 1280 SemiSpace tmp = from_space_; 1281 from_space_ = to_space_; 1282 to_space_ = tmp; 1283 } 1284 1285 1286 void NewSpace::Grow() { 1287 ASSERT(Capacity() < MaximumCapacity()); 1288 if (to_space_.Grow()) { 1289 // Only grow from space if we managed to grow to space. 1290 if (!from_space_.Grow()) { 1291 // If we managed to grow to space but couldn't grow from space, 1292 // attempt to shrink to space. 1293 if (!to_space_.ShrinkTo(from_space_.Capacity())) { 1294 // We are in an inconsistent state because we could not 1295 // commit/uncommit memory from new space. 1296 V8::FatalProcessOutOfMemory("Failed to grow new space."); 1297 } 1298 } 1299 } 1300 allocation_info_.limit = to_space_.high(); 1301 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); 1302 } 1303 1304 1305 void NewSpace::Shrink() { 1306 int new_capacity = Max(InitialCapacity(), 2 * SizeAsInt()); 1307 int rounded_new_capacity = 1308 RoundUp(new_capacity, static_cast<int>(OS::AllocateAlignment())); 1309 if (rounded_new_capacity < Capacity() && 1310 to_space_.ShrinkTo(rounded_new_capacity)) { 1311 // Only shrink from space if we managed to shrink to space. 1312 if (!from_space_.ShrinkTo(rounded_new_capacity)) { 1313 // If we managed to shrink to space but couldn't shrink from 1314 // space, attempt to grow to space again. 1315 if (!to_space_.GrowTo(from_space_.Capacity())) { 1316 // We are in an inconsistent state because we could not 1317 // commit/uncommit memory from new space. 1318 V8::FatalProcessOutOfMemory("Failed to shrink new space."); 1319 } 1320 } 1321 } 1322 allocation_info_.limit = to_space_.high(); 1323 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); 1324 } 1325 1326 1327 void NewSpace::ResetAllocationInfo() { 1328 allocation_info_.top = to_space_.low(); 1329 allocation_info_.limit = to_space_.high(); 1330 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); 1331 } 1332 1333 1334 void NewSpace::MCResetRelocationInfo() { 1335 mc_forwarding_info_.top = from_space_.low(); 1336 mc_forwarding_info_.limit = from_space_.high(); 1337 ASSERT_SEMISPACE_ALLOCATION_INFO(mc_forwarding_info_, from_space_); 1338 } 1339 1340 1341 void NewSpace::MCCommitRelocationInfo() { 1342 // Assumes that the spaces have been flipped so that mc_forwarding_info_ is 1343 // valid allocation info for the to space. 1344 allocation_info_.top = mc_forwarding_info_.top; 1345 allocation_info_.limit = to_space_.high(); 1346 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); 1347 } 1348 1349 1350 #ifdef DEBUG 1351 // We do not use the SemispaceIterator because verification doesn't assume 1352 // that it works (it depends on the invariants we are checking). 1353 void NewSpace::Verify() { 1354 // The allocation pointer should be in the space or at the very end. 1355 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); 1356 1357 // There should be objects packed in from the low address up to the 1358 // allocation pointer. 1359 Address current = to_space_.low(); 1360 while (current < top()) { 1361 HeapObject* object = HeapObject::FromAddress(current); 1362 1363 // The first word should be a map, and we expect all map pointers to 1364 // be in map space. 1365 Map* map = object->map(); 1366 ASSERT(map->IsMap()); 1367 ASSERT(heap()->map_space()->Contains(map)); 1368 1369 // The object should not be code or a map. 1370 ASSERT(!object->IsMap()); 1371 ASSERT(!object->IsCode()); 1372 1373 // The object itself should look OK. 1374 object->Verify(); 1375 1376 // All the interior pointers should be contained in the heap. 1377 VerifyPointersVisitor visitor; 1378 int size = object->Size(); 1379 object->IterateBody(map->instance_type(), size, &visitor); 1380 1381 current += size; 1382 } 1383 1384 // The allocation pointer should not be in the middle of an object. 1385 ASSERT(current == top()); 1386 } 1387 #endif 1388 1389 1390 bool SemiSpace::Commit() { 1391 ASSERT(!is_committed()); 1392 if (!heap()->isolate()->memory_allocator()->CommitBlock( 1393 start_, capacity_, executable())) { 1394 return false; 1395 } 1396 committed_ = true; 1397 return true; 1398 } 1399 1400 1401 bool SemiSpace::Uncommit() { 1402 ASSERT(is_committed()); 1403 if (!heap()->isolate()->memory_allocator()->UncommitBlock( 1404 start_, capacity_)) { 1405 return false; 1406 } 1407 committed_ = false; 1408 return true; 1409 } 1410 1411 1412 // ----------------------------------------------------------------------------- 1413 // SemiSpace implementation 1414 1415 bool SemiSpace::Setup(Address start, 1416 int initial_capacity, 1417 int maximum_capacity) { 1418 // Creates a space in the young generation. The constructor does not 1419 // allocate memory from the OS. A SemiSpace is given a contiguous chunk of 1420 // memory of size 'capacity' when set up, and does not grow or shrink 1421 // otherwise. In the mark-compact collector, the memory region of the from 1422 // space is used as the marking stack. It requires contiguous memory 1423 // addresses. 1424 initial_capacity_ = initial_capacity; 1425 capacity_ = initial_capacity; 1426 maximum_capacity_ = maximum_capacity; 1427 committed_ = false; 1428 1429 start_ = start; 1430 address_mask_ = ~(maximum_capacity - 1); 1431 object_mask_ = address_mask_ | kHeapObjectTagMask; 1432 object_expected_ = reinterpret_cast<uintptr_t>(start) | kHeapObjectTag; 1433 age_mark_ = start_; 1434 1435 return Commit(); 1436 } 1437 1438 1439 void SemiSpace::TearDown() { 1440 start_ = NULL; 1441 capacity_ = 0; 1442 } 1443 1444 1445 bool SemiSpace::Grow() { 1446 // Double the semispace size but only up to maximum capacity. 1447 int maximum_extra = maximum_capacity_ - capacity_; 1448 int extra = Min(RoundUp(capacity_, static_cast<int>(OS::AllocateAlignment())), 1449 maximum_extra); 1450 if (!heap()->isolate()->memory_allocator()->CommitBlock( 1451 high(), extra, executable())) { 1452 return false; 1453 } 1454 capacity_ += extra; 1455 return true; 1456 } 1457 1458 1459 bool SemiSpace::GrowTo(int new_capacity) { 1460 ASSERT(new_capacity <= maximum_capacity_); 1461 ASSERT(new_capacity > capacity_); 1462 size_t delta = new_capacity - capacity_; 1463 ASSERT(IsAligned(delta, OS::AllocateAlignment())); 1464 if (!heap()->isolate()->memory_allocator()->CommitBlock( 1465 high(), delta, executable())) { 1466 return false; 1467 } 1468 capacity_ = new_capacity; 1469 return true; 1470 } 1471 1472 1473 bool SemiSpace::ShrinkTo(int new_capacity) { 1474 ASSERT(new_capacity >= initial_capacity_); 1475 ASSERT(new_capacity < capacity_); 1476 size_t delta = capacity_ - new_capacity; 1477 ASSERT(IsAligned(delta, OS::AllocateAlignment())); 1478 if (!heap()->isolate()->memory_allocator()->UncommitBlock( 1479 high() - delta, delta)) { 1480 return false; 1481 } 1482 capacity_ = new_capacity; 1483 return true; 1484 } 1485 1486 1487 #ifdef DEBUG 1488 void SemiSpace::Print() { } 1489 1490 1491 void SemiSpace::Verify() { } 1492 #endif 1493 1494 1495 // ----------------------------------------------------------------------------- 1496 // SemiSpaceIterator implementation. 1497 SemiSpaceIterator::SemiSpaceIterator(NewSpace* space) { 1498 Initialize(space, space->bottom(), space->top(), NULL); 1499 } 1500 1501 1502 SemiSpaceIterator::SemiSpaceIterator(NewSpace* space, 1503 HeapObjectCallback size_func) { 1504 Initialize(space, space->bottom(), space->top(), size_func); 1505 } 1506 1507 1508 SemiSpaceIterator::SemiSpaceIterator(NewSpace* space, Address start) { 1509 Initialize(space, start, space->top(), NULL); 1510 } 1511 1512 1513 void SemiSpaceIterator::Initialize(NewSpace* space, Address start, 1514 Address end, 1515 HeapObjectCallback size_func) { 1516 ASSERT(space->ToSpaceContains(start)); 1517 ASSERT(space->ToSpaceLow() <= end 1518 && end <= space->ToSpaceHigh()); 1519 space_ = &space->to_space_; 1520 current_ = start; 1521 limit_ = end; 1522 size_func_ = size_func; 1523 } 1524 1525 1526 #ifdef DEBUG 1527 // heap_histograms is shared, always clear it before using it. 1528 static void ClearHistograms() { 1529 Isolate* isolate = Isolate::Current(); 1530 // We reset the name each time, though it hasn't changed. 1531 #define DEF_TYPE_NAME(name) isolate->heap_histograms()[name].set_name(#name); 1532 INSTANCE_TYPE_LIST(DEF_TYPE_NAME) 1533 #undef DEF_TYPE_NAME 1534 1535 #define CLEAR_HISTOGRAM(name) isolate->heap_histograms()[name].clear(); 1536 INSTANCE_TYPE_LIST(CLEAR_HISTOGRAM) 1537 #undef CLEAR_HISTOGRAM 1538 1539 isolate->js_spill_information()->Clear(); 1540 } 1541 1542 1543 static void ClearCodeKindStatistics() { 1544 Isolate* isolate = Isolate::Current(); 1545 for (int i = 0; i < Code::NUMBER_OF_KINDS; i++) { 1546 isolate->code_kind_statistics()[i] = 0; 1547 } 1548 } 1549 1550 1551 static void ReportCodeKindStatistics() { 1552 Isolate* isolate = Isolate::Current(); 1553 const char* table[Code::NUMBER_OF_KINDS] = { NULL }; 1554 1555 #define CASE(name) \ 1556 case Code::name: table[Code::name] = #name; \ 1557 break 1558 1559 for (int i = 0; i < Code::NUMBER_OF_KINDS; i++) { 1560 switch (static_cast<Code::Kind>(i)) { 1561 CASE(FUNCTION); 1562 CASE(OPTIMIZED_FUNCTION); 1563 CASE(STUB); 1564 CASE(BUILTIN); 1565 CASE(LOAD_IC); 1566 CASE(KEYED_LOAD_IC); 1567 CASE(KEYED_EXTERNAL_ARRAY_LOAD_IC); 1568 CASE(STORE_IC); 1569 CASE(KEYED_STORE_IC); 1570 CASE(KEYED_EXTERNAL_ARRAY_STORE_IC); 1571 CASE(CALL_IC); 1572 CASE(KEYED_CALL_IC); 1573 CASE(TYPE_RECORDING_BINARY_OP_IC); 1574 CASE(COMPARE_IC); 1575 } 1576 } 1577 1578 #undef CASE 1579 1580 PrintF("\n Code kind histograms: \n"); 1581 for (int i = 0; i < Code::NUMBER_OF_KINDS; i++) { 1582 if (isolate->code_kind_statistics()[i] > 0) { 1583 PrintF(" %-20s: %10d bytes\n", table[i], 1584 isolate->code_kind_statistics()[i]); 1585 } 1586 } 1587 PrintF("\n"); 1588 } 1589 1590 1591 static int CollectHistogramInfo(HeapObject* obj) { 1592 Isolate* isolate = Isolate::Current(); 1593 InstanceType type = obj->map()->instance_type(); 1594 ASSERT(0 <= type && type <= LAST_TYPE); 1595 ASSERT(isolate->heap_histograms()[type].name() != NULL); 1596 isolate->heap_histograms()[type].increment_number(1); 1597 isolate->heap_histograms()[type].increment_bytes(obj->Size()); 1598 1599 if (FLAG_collect_heap_spill_statistics && obj->IsJSObject()) { 1600 JSObject::cast(obj)->IncrementSpillStatistics( 1601 isolate->js_spill_information()); 1602 } 1603 1604 return obj->Size(); 1605 } 1606 1607 1608 static void ReportHistogram(bool print_spill) { 1609 Isolate* isolate = Isolate::Current(); 1610 PrintF("\n Object Histogram:\n"); 1611 for (int i = 0; i <= LAST_TYPE; i++) { 1612 if (isolate->heap_histograms()[i].number() > 0) { 1613 PrintF(" %-34s%10d (%10d bytes)\n", 1614 isolate->heap_histograms()[i].name(), 1615 isolate->heap_histograms()[i].number(), 1616 isolate->heap_histograms()[i].bytes()); 1617 } 1618 } 1619 PrintF("\n"); 1620 1621 // Summarize string types. 1622 int string_number = 0; 1623 int string_bytes = 0; 1624 #define INCREMENT(type, size, name, camel_name) \ 1625 string_number += isolate->heap_histograms()[type].number(); \ 1626 string_bytes += isolate->heap_histograms()[type].bytes(); 1627 STRING_TYPE_LIST(INCREMENT) 1628 #undef INCREMENT 1629 if (string_number > 0) { 1630 PrintF(" %-34s%10d (%10d bytes)\n\n", "STRING_TYPE", string_number, 1631 string_bytes); 1632 } 1633 1634 if (FLAG_collect_heap_spill_statistics && print_spill) { 1635 isolate->js_spill_information()->Print(); 1636 } 1637 } 1638 #endif // DEBUG 1639 1640 1641 // Support for statistics gathering for --heap-stats and --log-gc. 1642 #if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING) 1643 void NewSpace::ClearHistograms() { 1644 for (int i = 0; i <= LAST_TYPE; i++) { 1645 allocated_histogram_[i].clear(); 1646 promoted_histogram_[i].clear(); 1647 } 1648 } 1649 1650 // Because the copying collector does not touch garbage objects, we iterate 1651 // the new space before a collection to get a histogram of allocated objects. 1652 // This only happens (1) when compiled with DEBUG and the --heap-stats flag is 1653 // set, or when compiled with ENABLE_LOGGING_AND_PROFILING and the --log-gc 1654 // flag is set. 1655 void NewSpace::CollectStatistics() { 1656 ClearHistograms(); 1657 SemiSpaceIterator it(this); 1658 for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) 1659 RecordAllocation(obj); 1660 } 1661 1662 1663 #ifdef ENABLE_LOGGING_AND_PROFILING 1664 static void DoReportStatistics(Isolate* isolate, 1665 HistogramInfo* info, const char* description) { 1666 LOG(isolate, HeapSampleBeginEvent("NewSpace", description)); 1667 // Lump all the string types together. 1668 int string_number = 0; 1669 int string_bytes = 0; 1670 #define INCREMENT(type, size, name, camel_name) \ 1671 string_number += info[type].number(); \ 1672 string_bytes += info[type].bytes(); 1673 STRING_TYPE_LIST(INCREMENT) 1674 #undef INCREMENT 1675 if (string_number > 0) { 1676 LOG(isolate, 1677 HeapSampleItemEvent("STRING_TYPE", string_number, string_bytes)); 1678 } 1679 1680 // Then do the other types. 1681 for (int i = FIRST_NONSTRING_TYPE; i <= LAST_TYPE; ++i) { 1682 if (info[i].number() > 0) { 1683 LOG(isolate, 1684 HeapSampleItemEvent(info[i].name(), info[i].number(), 1685 info[i].bytes())); 1686 } 1687 } 1688 LOG(isolate, HeapSampleEndEvent("NewSpace", description)); 1689 } 1690 #endif // ENABLE_LOGGING_AND_PROFILING 1691 1692 1693 void NewSpace::ReportStatistics() { 1694 #ifdef DEBUG 1695 if (FLAG_heap_stats) { 1696 float pct = static_cast<float>(Available()) / Capacity(); 1697 PrintF(" capacity: %" V8_PTR_PREFIX "d" 1698 ", available: %" V8_PTR_PREFIX "d, %%%d\n", 1699 Capacity(), Available(), static_cast<int>(pct*100)); 1700 PrintF("\n Object Histogram:\n"); 1701 for (int i = 0; i <= LAST_TYPE; i++) { 1702 if (allocated_histogram_[i].number() > 0) { 1703 PrintF(" %-34s%10d (%10d bytes)\n", 1704 allocated_histogram_[i].name(), 1705 allocated_histogram_[i].number(), 1706 allocated_histogram_[i].bytes()); 1707 } 1708 } 1709 PrintF("\n"); 1710 } 1711 #endif // DEBUG 1712 1713 #ifdef ENABLE_LOGGING_AND_PROFILING 1714 if (FLAG_log_gc) { 1715 Isolate* isolate = ISOLATE; 1716 DoReportStatistics(isolate, allocated_histogram_, "allocated"); 1717 DoReportStatistics(isolate, promoted_histogram_, "promoted"); 1718 } 1719 #endif // ENABLE_LOGGING_AND_PROFILING 1720 } 1721 1722 1723 void NewSpace::RecordAllocation(HeapObject* obj) { 1724 InstanceType type = obj->map()->instance_type(); 1725 ASSERT(0 <= type && type <= LAST_TYPE); 1726 allocated_histogram_[type].increment_number(1); 1727 allocated_histogram_[type].increment_bytes(obj->Size()); 1728 } 1729 1730 1731 void NewSpace::RecordPromotion(HeapObject* obj) { 1732 InstanceType type = obj->map()->instance_type(); 1733 ASSERT(0 <= type && type <= LAST_TYPE); 1734 promoted_histogram_[type].increment_number(1); 1735 promoted_histogram_[type].increment_bytes(obj->Size()); 1736 } 1737 #endif // defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING) 1738 1739 1740 // ----------------------------------------------------------------------------- 1741 // Free lists for old object spaces implementation 1742 1743 void FreeListNode::set_size(Heap* heap, int size_in_bytes) { 1744 ASSERT(size_in_bytes > 0); 1745 ASSERT(IsAligned(size_in_bytes, kPointerSize)); 1746 1747 // We write a map and possibly size information to the block. If the block 1748 // is big enough to be a ByteArray with at least one extra word (the next 1749 // pointer), we set its map to be the byte array map and its size to an 1750 // appropriate array length for the desired size from HeapObject::Size(). 1751 // If the block is too small (eg, one or two words), to hold both a size 1752 // field and a next pointer, we give it a filler map that gives it the 1753 // correct size. 1754 if (size_in_bytes > ByteArray::kHeaderSize) { 1755 set_map(heap->raw_unchecked_byte_array_map()); 1756 // Can't use ByteArray::cast because it fails during deserialization. 1757 ByteArray* this_as_byte_array = reinterpret_cast<ByteArray*>(this); 1758 this_as_byte_array->set_length(ByteArray::LengthFor(size_in_bytes)); 1759 } else if (size_in_bytes == kPointerSize) { 1760 set_map(heap->raw_unchecked_one_pointer_filler_map()); 1761 } else if (size_in_bytes == 2 * kPointerSize) { 1762 set_map(heap->raw_unchecked_two_pointer_filler_map()); 1763 } else { 1764 UNREACHABLE(); 1765 } 1766 // We would like to ASSERT(Size() == size_in_bytes) but this would fail during 1767 // deserialization because the byte array map is not done yet. 1768 } 1769 1770 1771 Address FreeListNode::next(Heap* heap) { 1772 ASSERT(IsFreeListNode(this)); 1773 if (map() == heap->raw_unchecked_byte_array_map()) { 1774 ASSERT(Size() >= kNextOffset + kPointerSize); 1775 return Memory::Address_at(address() + kNextOffset); 1776 } else { 1777 return Memory::Address_at(address() + kPointerSize); 1778 } 1779 } 1780 1781 1782 void FreeListNode::set_next(Heap* heap, Address next) { 1783 ASSERT(IsFreeListNode(this)); 1784 if (map() == heap->raw_unchecked_byte_array_map()) { 1785 ASSERT(Size() >= kNextOffset + kPointerSize); 1786 Memory::Address_at(address() + kNextOffset) = next; 1787 } else { 1788 Memory::Address_at(address() + kPointerSize) = next; 1789 } 1790 } 1791 1792 1793 OldSpaceFreeList::OldSpaceFreeList(Heap* heap, AllocationSpace owner) 1794 : heap_(heap), 1795 owner_(owner) { 1796 Reset(); 1797 } 1798 1799 1800 void OldSpaceFreeList::Reset() { 1801 available_ = 0; 1802 for (int i = 0; i < kFreeListsLength; i++) { 1803 free_[i].head_node_ = NULL; 1804 } 1805 needs_rebuild_ = false; 1806 finger_ = kHead; 1807 free_[kHead].next_size_ = kEnd; 1808 } 1809 1810 1811 void OldSpaceFreeList::RebuildSizeList() { 1812 ASSERT(needs_rebuild_); 1813 int cur = kHead; 1814 for (int i = cur + 1; i < kFreeListsLength; i++) { 1815 if (free_[i].head_node_ != NULL) { 1816 free_[cur].next_size_ = i; 1817 cur = i; 1818 } 1819 } 1820 free_[cur].next_size_ = kEnd; 1821 needs_rebuild_ = false; 1822 } 1823 1824 1825 int OldSpaceFreeList::Free(Address start, int size_in_bytes) { 1826 #ifdef DEBUG 1827 Isolate::Current()->memory_allocator()->ZapBlock(start, size_in_bytes); 1828 #endif 1829 FreeListNode* node = FreeListNode::FromAddress(start); 1830 node->set_size(heap_, size_in_bytes); 1831 1832 // We don't use the freelists in compacting mode. This makes it more like a 1833 // GC that only has mark-sweep-compact and doesn't have a mark-sweep 1834 // collector. 1835 if (FLAG_always_compact) { 1836 return size_in_bytes; 1837 } 1838 1839 // Early return to drop too-small blocks on the floor (one or two word 1840 // blocks cannot hold a map pointer, a size field, and a pointer to the 1841 // next block in the free list). 1842 if (size_in_bytes < kMinBlockSize) { 1843 return size_in_bytes; 1844 } 1845 1846 // Insert other blocks at the head of an exact free list. 1847 int index = size_in_bytes >> kPointerSizeLog2; 1848 node->set_next(heap_, free_[index].head_node_); 1849 free_[index].head_node_ = node->address(); 1850 available_ += size_in_bytes; 1851 needs_rebuild_ = true; 1852 return 0; 1853 } 1854 1855 1856 MaybeObject* OldSpaceFreeList::Allocate(int size_in_bytes, int* wasted_bytes) { 1857 ASSERT(0 < size_in_bytes); 1858 ASSERT(size_in_bytes <= kMaxBlockSize); 1859 ASSERT(IsAligned(size_in_bytes, kPointerSize)); 1860 1861 if (needs_rebuild_) RebuildSizeList(); 1862 int index = size_in_bytes >> kPointerSizeLog2; 1863 // Check for a perfect fit. 1864 if (free_[index].head_node_ != NULL) { 1865 FreeListNode* node = FreeListNode::FromAddress(free_[index].head_node_); 1866 // If this was the last block of its size, remove the size. 1867 if ((free_[index].head_node_ = node->next(heap_)) == NULL) 1868 RemoveSize(index); 1869 available_ -= size_in_bytes; 1870 *wasted_bytes = 0; 1871 ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep. 1872 return node; 1873 } 1874 // Search the size list for the best fit. 1875 int prev = finger_ < index ? finger_ : kHead; 1876 int cur = FindSize(index, &prev); 1877 ASSERT(index < cur); 1878 if (cur == kEnd) { 1879 // No large enough size in list. 1880 *wasted_bytes = 0; 1881 return Failure::RetryAfterGC(owner_); 1882 } 1883 ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep. 1884 int rem = cur - index; 1885 int rem_bytes = rem << kPointerSizeLog2; 1886 FreeListNode* cur_node = FreeListNode::FromAddress(free_[cur].head_node_); 1887 ASSERT(cur_node->Size() == (cur << kPointerSizeLog2)); 1888 FreeListNode* rem_node = FreeListNode::FromAddress(free_[cur].head_node_ + 1889 size_in_bytes); 1890 // Distinguish the cases prev < rem < cur and rem <= prev < cur 1891 // to avoid many redundant tests and calls to Insert/RemoveSize. 1892 if (prev < rem) { 1893 // Simple case: insert rem between prev and cur. 1894 finger_ = prev; 1895 free_[prev].next_size_ = rem; 1896 // If this was the last block of size cur, remove the size. 1897 if ((free_[cur].head_node_ = cur_node->next(heap_)) == NULL) { 1898 free_[rem].next_size_ = free_[cur].next_size_; 1899 } else { 1900 free_[rem].next_size_ = cur; 1901 } 1902 // Add the remainder block. 1903 rem_node->set_size(heap_, rem_bytes); 1904 rem_node->set_next(heap_, free_[rem].head_node_); 1905 free_[rem].head_node_ = rem_node->address(); 1906 } else { 1907 // If this was the last block of size cur, remove the size. 1908 if ((free_[cur].head_node_ = cur_node->next(heap_)) == NULL) { 1909 finger_ = prev; 1910 free_[prev].next_size_ = free_[cur].next_size_; 1911 } 1912 if (rem_bytes < kMinBlockSize) { 1913 // Too-small remainder is wasted. 1914 rem_node->set_size(heap_, rem_bytes); 1915 available_ -= size_in_bytes + rem_bytes; 1916 *wasted_bytes = rem_bytes; 1917 return cur_node; 1918 } 1919 // Add the remainder block and, if needed, insert its size. 1920 rem_node->set_size(heap_, rem_bytes); 1921 rem_node->set_next(heap_, free_[rem].head_node_); 1922 free_[rem].head_node_ = rem_node->address(); 1923 if (rem_node->next(heap_) == NULL) InsertSize(rem); 1924 } 1925 available_ -= size_in_bytes; 1926 *wasted_bytes = 0; 1927 return cur_node; 1928 } 1929 1930 1931 void OldSpaceFreeList::MarkNodes() { 1932 for (int i = 0; i < kFreeListsLength; i++) { 1933 Address cur_addr = free_[i].head_node_; 1934 while (cur_addr != NULL) { 1935 FreeListNode* cur_node = FreeListNode::FromAddress(cur_addr); 1936 cur_addr = cur_node->next(heap_); 1937 cur_node->SetMark(); 1938 } 1939 } 1940 } 1941 1942 1943 #ifdef DEBUG 1944 bool OldSpaceFreeList::Contains(FreeListNode* node) { 1945 for (int i = 0; i < kFreeListsLength; i++) { 1946 Address cur_addr = free_[i].head_node_; 1947 while (cur_addr != NULL) { 1948 FreeListNode* cur_node = FreeListNode::FromAddress(cur_addr); 1949 if (cur_node == node) return true; 1950 cur_addr = cur_node->next(heap_); 1951 } 1952 } 1953 return false; 1954 } 1955 #endif 1956 1957 1958 FixedSizeFreeList::FixedSizeFreeList(Heap* heap, 1959 AllocationSpace owner, 1960 int object_size) 1961 : heap_(heap), owner_(owner), object_size_(object_size) { 1962 Reset(); 1963 } 1964 1965 1966 void FixedSizeFreeList::Reset() { 1967 available_ = 0; 1968 head_ = tail_ = NULL; 1969 } 1970 1971 1972 void FixedSizeFreeList::Free(Address start) { 1973 #ifdef DEBUG 1974 Isolate::Current()->memory_allocator()->ZapBlock(start, object_size_); 1975 #endif 1976 // We only use the freelists with mark-sweep. 1977 ASSERT(!HEAP->mark_compact_collector()->IsCompacting()); 1978 FreeListNode* node = FreeListNode::FromAddress(start); 1979 node->set_size(heap_, object_size_); 1980 node->set_next(heap_, NULL); 1981 if (head_ == NULL) { 1982 tail_ = head_ = node->address(); 1983 } else { 1984 FreeListNode::FromAddress(tail_)->set_next(heap_, node->address()); 1985 tail_ = node->address(); 1986 } 1987 available_ += object_size_; 1988 } 1989 1990 1991 MaybeObject* FixedSizeFreeList::Allocate() { 1992 if (head_ == NULL) { 1993 return Failure::RetryAfterGC(owner_); 1994 } 1995 1996 ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep. 1997 FreeListNode* node = FreeListNode::FromAddress(head_); 1998 head_ = node->next(heap_); 1999 available_ -= object_size_; 2000 return node; 2001 } 2002 2003 2004 void FixedSizeFreeList::MarkNodes() { 2005 Address cur_addr = head_; 2006 while (cur_addr != NULL && cur_addr != tail_) { 2007 FreeListNode* cur_node = FreeListNode::FromAddress(cur_addr); 2008 cur_addr = cur_node->next(heap_); 2009 cur_node->SetMark(); 2010 } 2011 } 2012 2013 2014 // ----------------------------------------------------------------------------- 2015 // OldSpace implementation 2016 2017 void OldSpace::PrepareForMarkCompact(bool will_compact) { 2018 // Call prepare of the super class. 2019 PagedSpace::PrepareForMarkCompact(will_compact); 2020 2021 if (will_compact) { 2022 // Reset relocation info. During a compacting collection, everything in 2023 // the space is considered 'available' and we will rediscover live data 2024 // and waste during the collection. 2025 MCResetRelocationInfo(); 2026 ASSERT(Available() == Capacity()); 2027 } else { 2028 // During a non-compacting collection, everything below the linear 2029 // allocation pointer is considered allocated (everything above is 2030 // available) and we will rediscover available and wasted bytes during 2031 // the collection. 2032 accounting_stats_.AllocateBytes(free_list_.available()); 2033 accounting_stats_.FillWastedBytes(Waste()); 2034 } 2035 2036 // Clear the free list before a full GC---it will be rebuilt afterward. 2037 free_list_.Reset(); 2038 } 2039 2040 2041 void OldSpace::MCCommitRelocationInfo() { 2042 // Update fast allocation info. 2043 allocation_info_.top = mc_forwarding_info_.top; 2044 allocation_info_.limit = mc_forwarding_info_.limit; 2045 ASSERT(allocation_info_.VerifyPagedAllocation()); 2046 2047 // The space is compacted and we haven't yet built free lists or 2048 // wasted any space. 2049 ASSERT(Waste() == 0); 2050 ASSERT(AvailableFree() == 0); 2051 2052 // Build the free list for the space. 2053 int computed_size = 0; 2054 PageIterator it(this, PageIterator::PAGES_USED_BY_MC); 2055 while (it.has_next()) { 2056 Page* p = it.next(); 2057 // Space below the relocation pointer is allocated. 2058 computed_size += 2059 static_cast<int>(p->AllocationWatermark() - p->ObjectAreaStart()); 2060 if (it.has_next()) { 2061 // Free the space at the top of the page. 2062 int extra_size = 2063 static_cast<int>(p->ObjectAreaEnd() - p->AllocationWatermark()); 2064 if (extra_size > 0) { 2065 int wasted_bytes = free_list_.Free(p->AllocationWatermark(), 2066 extra_size); 2067 // The bytes we have just "freed" to add to the free list were 2068 // already accounted as available. 2069 accounting_stats_.WasteBytes(wasted_bytes); 2070 } 2071 } 2072 } 2073 2074 // Make sure the computed size - based on the used portion of the pages in 2075 // use - matches the size obtained while computing forwarding addresses. 2076 ASSERT(computed_size == Size()); 2077 } 2078 2079 2080 bool NewSpace::ReserveSpace(int bytes) { 2081 // We can't reliably unpack a partial snapshot that needs more new space 2082 // space than the minimum NewSpace size. 2083 ASSERT(bytes <= InitialCapacity()); 2084 Address limit = allocation_info_.limit; 2085 Address top = allocation_info_.top; 2086 return limit - top >= bytes; 2087 } 2088 2089 2090 void PagedSpace::FreePages(Page* prev, Page* last) { 2091 if (last == AllocationTopPage()) { 2092 // Pages are already at the end of used pages. 2093 return; 2094 } 2095 2096 Page* first = NULL; 2097 2098 // Remove pages from the list. 2099 if (prev == NULL) { 2100 first = first_page_; 2101 first_page_ = last->next_page(); 2102 } else { 2103 first = prev->next_page(); 2104 heap()->isolate()->memory_allocator()->SetNextPage( 2105 prev, last->next_page()); 2106 } 2107 2108 // Attach it after the last page. 2109 heap()->isolate()->memory_allocator()->SetNextPage(last_page_, first); 2110 last_page_ = last; 2111 heap()->isolate()->memory_allocator()->SetNextPage(last, NULL); 2112 2113 // Clean them up. 2114 do { 2115 first->InvalidateWatermark(true); 2116 first->SetAllocationWatermark(first->ObjectAreaStart()); 2117 first->SetCachedAllocationWatermark(first->ObjectAreaStart()); 2118 first->SetRegionMarks(Page::kAllRegionsCleanMarks); 2119 first = first->next_page(); 2120 } while (first != NULL); 2121 2122 // Order of pages in this space might no longer be consistent with 2123 // order of pages in chunks. 2124 page_list_is_chunk_ordered_ = false; 2125 } 2126 2127 2128 void PagedSpace::RelinkPageListInChunkOrder(bool deallocate_blocks) { 2129 const bool add_to_freelist = true; 2130 2131 // Mark used and unused pages to properly fill unused pages 2132 // after reordering. 2133 PageIterator all_pages_iterator(this, PageIterator::ALL_PAGES); 2134 Page* last_in_use = AllocationTopPage(); 2135 bool in_use = true; 2136 2137 while (all_pages_iterator.has_next()) { 2138 Page* p = all_pages_iterator.next(); 2139 p->SetWasInUseBeforeMC(in_use); 2140 if (p == last_in_use) { 2141 // We passed a page containing allocation top. All consequent 2142 // pages are not used. 2143 in_use = false; 2144 } 2145 } 2146 2147 if (page_list_is_chunk_ordered_) return; 2148 2149 Page* new_last_in_use = Page::FromAddress(NULL); 2150 heap()->isolate()->memory_allocator()->RelinkPageListInChunkOrder( 2151 this, &first_page_, &last_page_, &new_last_in_use); 2152 ASSERT(new_last_in_use->is_valid()); 2153 2154 if (new_last_in_use != last_in_use) { 2155 // Current allocation top points to a page which is now in the middle 2156 // of page list. We should move allocation top forward to the new last 2157 // used page so various object iterators will continue to work properly. 2158 int size_in_bytes = static_cast<int>(PageAllocationLimit(last_in_use) - 2159 last_in_use->AllocationTop()); 2160 2161 last_in_use->SetAllocationWatermark(last_in_use->AllocationTop()); 2162 if (size_in_bytes > 0) { 2163 Address start = last_in_use->AllocationTop(); 2164 if (deallocate_blocks) { 2165 accounting_stats_.AllocateBytes(size_in_bytes); 2166 DeallocateBlock(start, size_in_bytes, add_to_freelist); 2167 } else { 2168 heap()->CreateFillerObjectAt(start, size_in_bytes); 2169 } 2170 } 2171 2172 // New last in use page was in the middle of the list before 2173 // sorting so it full. 2174 SetTop(new_last_in_use->AllocationTop()); 2175 2176 ASSERT(AllocationTopPage() == new_last_in_use); 2177 ASSERT(AllocationTopPage()->WasInUseBeforeMC()); 2178 } 2179 2180 PageIterator pages_in_use_iterator(this, PageIterator::PAGES_IN_USE); 2181 while (pages_in_use_iterator.has_next()) { 2182 Page* p = pages_in_use_iterator.next(); 2183 if (!p->WasInUseBeforeMC()) { 2184 // Empty page is in the middle of a sequence of used pages. 2185 // Allocate it as a whole and deallocate immediately. 2186 int size_in_bytes = static_cast<int>(PageAllocationLimit(p) - 2187 p->ObjectAreaStart()); 2188 2189 p->SetAllocationWatermark(p->ObjectAreaStart()); 2190 Address start = p->ObjectAreaStart(); 2191 if (deallocate_blocks) { 2192 accounting_stats_.AllocateBytes(size_in_bytes); 2193 DeallocateBlock(start, size_in_bytes, add_to_freelist); 2194 } else { 2195 heap()->CreateFillerObjectAt(start, size_in_bytes); 2196 } 2197 } 2198 } 2199 2200 page_list_is_chunk_ordered_ = true; 2201 } 2202 2203 2204 void PagedSpace::PrepareForMarkCompact(bool will_compact) { 2205 if (will_compact) { 2206 RelinkPageListInChunkOrder(false); 2207 } 2208 } 2209 2210 2211 bool PagedSpace::ReserveSpace(int bytes) { 2212 Address limit = allocation_info_.limit; 2213 Address top = allocation_info_.top; 2214 if (limit - top >= bytes) return true; 2215 2216 // There wasn't enough space in the current page. Lets put the rest 2217 // of the page on the free list and start a fresh page. 2218 PutRestOfCurrentPageOnFreeList(TopPageOf(allocation_info_)); 2219 2220 Page* reserved_page = TopPageOf(allocation_info_); 2221 int bytes_left_to_reserve = bytes; 2222 while (bytes_left_to_reserve > 0) { 2223 if (!reserved_page->next_page()->is_valid()) { 2224 if (heap()->OldGenerationAllocationLimitReached()) return false; 2225 Expand(reserved_page); 2226 } 2227 bytes_left_to_reserve -= Page::kPageSize; 2228 reserved_page = reserved_page->next_page(); 2229 if (!reserved_page->is_valid()) return false; 2230 } 2231 ASSERT(TopPageOf(allocation_info_)->next_page()->is_valid()); 2232 TopPageOf(allocation_info_)->next_page()->InvalidateWatermark(true); 2233 SetAllocationInfo(&allocation_info_, 2234 TopPageOf(allocation_info_)->next_page()); 2235 return true; 2236 } 2237 2238 2239 // You have to call this last, since the implementation from PagedSpace 2240 // doesn't know that memory was 'promised' to large object space. 2241 bool LargeObjectSpace::ReserveSpace(int bytes) { 2242 return heap()->OldGenerationSpaceAvailable() >= bytes; 2243 } 2244 2245 2246 // Slow case for normal allocation. Try in order: (1) allocate in the next 2247 // page in the space, (2) allocate off the space's free list, (3) expand the 2248 // space, (4) fail. 2249 HeapObject* OldSpace::SlowAllocateRaw(int size_in_bytes) { 2250 // Linear allocation in this space has failed. If there is another page 2251 // in the space, move to that page and allocate there. This allocation 2252 // should succeed (size_in_bytes should not be greater than a page's 2253 // object area size). 2254 Page* current_page = TopPageOf(allocation_info_); 2255 if (current_page->next_page()->is_valid()) { 2256 return AllocateInNextPage(current_page, size_in_bytes); 2257 } 2258 2259 // There is no next page in this space. Try free list allocation unless that 2260 // is currently forbidden. 2261 if (!heap()->linear_allocation()) { 2262 int wasted_bytes; 2263 Object* result; 2264 MaybeObject* maybe = free_list_.Allocate(size_in_bytes, &wasted_bytes); 2265 accounting_stats_.WasteBytes(wasted_bytes); 2266 if (maybe->ToObject(&result)) { 2267 accounting_stats_.AllocateBytes(size_in_bytes); 2268 2269 HeapObject* obj = HeapObject::cast(result); 2270 Page* p = Page::FromAddress(obj->address()); 2271 2272 if (obj->address() >= p->AllocationWatermark()) { 2273 // There should be no hole between the allocation watermark 2274 // and allocated object address. 2275 // Memory above the allocation watermark was not swept and 2276 // might contain garbage pointers to new space. 2277 ASSERT(obj->address() == p->AllocationWatermark()); 2278 p->SetAllocationWatermark(obj->address() + size_in_bytes); 2279 } 2280 2281 return obj; 2282 } 2283 } 2284 2285 // Free list allocation failed and there is no next page. Fail if we have 2286 // hit the old generation size limit that should cause a garbage 2287 // collection. 2288 if (!heap()->always_allocate() && 2289 heap()->OldGenerationAllocationLimitReached()) { 2290 return NULL; 2291 } 2292 2293 // Try to expand the space and allocate in the new next page. 2294 ASSERT(!current_page->next_page()->is_valid()); 2295 if (Expand(current_page)) { 2296 return AllocateInNextPage(current_page, size_in_bytes); 2297 } 2298 2299 // Finally, fail. 2300 return NULL; 2301 } 2302 2303 2304 void OldSpace::PutRestOfCurrentPageOnFreeList(Page* current_page) { 2305 current_page->SetAllocationWatermark(allocation_info_.top); 2306 int free_size = 2307 static_cast<int>(current_page->ObjectAreaEnd() - allocation_info_.top); 2308 if (free_size > 0) { 2309 int wasted_bytes = free_list_.Free(allocation_info_.top, free_size); 2310 accounting_stats_.WasteBytes(wasted_bytes); 2311 } 2312 } 2313 2314 2315 void FixedSpace::PutRestOfCurrentPageOnFreeList(Page* current_page) { 2316 current_page->SetAllocationWatermark(allocation_info_.top); 2317 int free_size = 2318 static_cast<int>(current_page->ObjectAreaEnd() - allocation_info_.top); 2319 // In the fixed space free list all the free list items have the right size. 2320 // We use up the rest of the page while preserving this invariant. 2321 while (free_size >= object_size_in_bytes_) { 2322 free_list_.Free(allocation_info_.top); 2323 allocation_info_.top += object_size_in_bytes_; 2324 free_size -= object_size_in_bytes_; 2325 accounting_stats_.WasteBytes(object_size_in_bytes_); 2326 } 2327 } 2328 2329 2330 // Add the block at the top of the page to the space's free list, set the 2331 // allocation info to the next page (assumed to be one), and allocate 2332 // linearly there. 2333 HeapObject* OldSpace::AllocateInNextPage(Page* current_page, 2334 int size_in_bytes) { 2335 ASSERT(current_page->next_page()->is_valid()); 2336 Page* next_page = current_page->next_page(); 2337 next_page->ClearGCFields(); 2338 PutRestOfCurrentPageOnFreeList(current_page); 2339 SetAllocationInfo(&allocation_info_, next_page); 2340 return AllocateLinearly(&allocation_info_, size_in_bytes); 2341 } 2342 2343 2344 void OldSpace::DeallocateBlock(Address start, 2345 int size_in_bytes, 2346 bool add_to_freelist) { 2347 Free(start, size_in_bytes, add_to_freelist); 2348 } 2349 2350 2351 #ifdef DEBUG 2352 void PagedSpace::ReportCodeStatistics() { 2353 Isolate* isolate = Isolate::Current(); 2354 CommentStatistic* comments_statistics = 2355 isolate->paged_space_comments_statistics(); 2356 ReportCodeKindStatistics(); 2357 PrintF("Code comment statistics (\" [ comment-txt : size/ " 2358 "count (average)\"):\n"); 2359 for (int i = 0; i <= CommentStatistic::kMaxComments; i++) { 2360 const CommentStatistic& cs = comments_statistics[i]; 2361 if (cs.size > 0) { 2362 PrintF(" %-30s: %10d/%6d (%d)\n", cs.comment, cs.size, cs.count, 2363 cs.size/cs.count); 2364 } 2365 } 2366 PrintF("\n"); 2367 } 2368 2369 2370 void PagedSpace::ResetCodeStatistics() { 2371 Isolate* isolate = Isolate::Current(); 2372 CommentStatistic* comments_statistics = 2373 isolate->paged_space_comments_statistics(); 2374 ClearCodeKindStatistics(); 2375 for (int i = 0; i < CommentStatistic::kMaxComments; i++) { 2376 comments_statistics[i].Clear(); 2377 } 2378 comments_statistics[CommentStatistic::kMaxComments].comment = "Unknown"; 2379 comments_statistics[CommentStatistic::kMaxComments].size = 0; 2380 comments_statistics[CommentStatistic::kMaxComments].count = 0; 2381 } 2382 2383 2384 // Adds comment to 'comment_statistics' table. Performance OK as long as 2385 // 'kMaxComments' is small 2386 static void EnterComment(Isolate* isolate, const char* comment, int delta) { 2387 CommentStatistic* comments_statistics = 2388 isolate->paged_space_comments_statistics(); 2389 // Do not count empty comments 2390 if (delta <= 0) return; 2391 CommentStatistic* cs = &comments_statistics[CommentStatistic::kMaxComments]; 2392 // Search for a free or matching entry in 'comments_statistics': 'cs' 2393 // points to result. 2394 for (int i = 0; i < CommentStatistic::kMaxComments; i++) { 2395 if (comments_statistics[i].comment == NULL) { 2396 cs = &comments_statistics[i]; 2397 cs->comment = comment; 2398 break; 2399 } else if (strcmp(comments_statistics[i].comment, comment) == 0) { 2400 cs = &comments_statistics[i]; 2401 break; 2402 } 2403 } 2404 // Update entry for 'comment' 2405 cs->size += delta; 2406 cs->count += 1; 2407 } 2408 2409 2410 // Call for each nested comment start (start marked with '[ xxx', end marked 2411 // with ']'. RelocIterator 'it' must point to a comment reloc info. 2412 static void CollectCommentStatistics(Isolate* isolate, RelocIterator* it) { 2413 ASSERT(!it->done()); 2414 ASSERT(it->rinfo()->rmode() == RelocInfo::COMMENT); 2415 const char* tmp = reinterpret_cast<const char*>(it->rinfo()->data()); 2416 if (tmp[0] != '[') { 2417 // Not a nested comment; skip 2418 return; 2419 } 2420 2421 // Search for end of nested comment or a new nested comment 2422 const char* const comment_txt = 2423 reinterpret_cast<const char*>(it->rinfo()->data()); 2424 const byte* prev_pc = it->rinfo()->pc(); 2425 int flat_delta = 0; 2426 it->next(); 2427 while (true) { 2428 // All nested comments must be terminated properly, and therefore exit 2429 // from loop. 2430 ASSERT(!it->done()); 2431 if (it->rinfo()->rmode() == RelocInfo::COMMENT) { 2432 const char* const txt = 2433 reinterpret_cast<const char*>(it->rinfo()->data()); 2434 flat_delta += static_cast<int>(it->rinfo()->pc() - prev_pc); 2435 if (txt[0] == ']') break; // End of nested comment 2436 // A new comment 2437 CollectCommentStatistics(isolate, it); 2438 // Skip code that was covered with previous comment 2439 prev_pc = it->rinfo()->pc(); 2440 } 2441 it->next(); 2442 } 2443 EnterComment(isolate, comment_txt, flat_delta); 2444 } 2445 2446 2447 // Collects code size statistics: 2448 // - by code kind 2449 // - by code comment 2450 void PagedSpace::CollectCodeStatistics() { 2451 Isolate* isolate = heap()->isolate(); 2452 HeapObjectIterator obj_it(this); 2453 for (HeapObject* obj = obj_it.next(); obj != NULL; obj = obj_it.next()) { 2454 if (obj->IsCode()) { 2455 Code* code = Code::cast(obj); 2456 isolate->code_kind_statistics()[code->kind()] += code->Size(); 2457 RelocIterator it(code); 2458 int delta = 0; 2459 const byte* prev_pc = code->instruction_start(); 2460 while (!it.done()) { 2461 if (it.rinfo()->rmode() == RelocInfo::COMMENT) { 2462 delta += static_cast<int>(it.rinfo()->pc() - prev_pc); 2463 CollectCommentStatistics(isolate, &it); 2464 prev_pc = it.rinfo()->pc(); 2465 } 2466 it.next(); 2467 } 2468 2469 ASSERT(code->instruction_start() <= prev_pc && 2470 prev_pc <= code->instruction_end()); 2471 delta += static_cast<int>(code->instruction_end() - prev_pc); 2472 EnterComment(isolate, "NoComment", delta); 2473 } 2474 } 2475 } 2476 2477 2478 void OldSpace::ReportStatistics() { 2479 int pct = static_cast<int>(Available() * 100 / Capacity()); 2480 PrintF(" capacity: %" V8_PTR_PREFIX "d" 2481 ", waste: %" V8_PTR_PREFIX "d" 2482 ", available: %" V8_PTR_PREFIX "d, %%%d\n", 2483 Capacity(), Waste(), Available(), pct); 2484 2485 ClearHistograms(); 2486 HeapObjectIterator obj_it(this); 2487 for (HeapObject* obj = obj_it.next(); obj != NULL; obj = obj_it.next()) 2488 CollectHistogramInfo(obj); 2489 ReportHistogram(true); 2490 } 2491 #endif 2492 2493 // ----------------------------------------------------------------------------- 2494 // FixedSpace implementation 2495 2496 void FixedSpace::PrepareForMarkCompact(bool will_compact) { 2497 // Call prepare of the super class. 2498 PagedSpace::PrepareForMarkCompact(will_compact); 2499 2500 if (will_compact) { 2501 // Reset relocation info. 2502 MCResetRelocationInfo(); 2503 2504 // During a compacting collection, everything in the space is considered 2505 // 'available' (set by the call to MCResetRelocationInfo) and we will 2506 // rediscover live and wasted bytes during the collection. 2507 ASSERT(Available() == Capacity()); 2508 } else { 2509 // During a non-compacting collection, everything below the linear 2510 // allocation pointer except wasted top-of-page blocks is considered 2511 // allocated and we will rediscover available bytes during the 2512 // collection. 2513 accounting_stats_.AllocateBytes(free_list_.available()); 2514 } 2515 2516 // Clear the free list before a full GC---it will be rebuilt afterward. 2517 free_list_.Reset(); 2518 } 2519 2520 2521 void FixedSpace::MCCommitRelocationInfo() { 2522 // Update fast allocation info. 2523 allocation_info_.top = mc_forwarding_info_.top; 2524 allocation_info_.limit = mc_forwarding_info_.limit; 2525 ASSERT(allocation_info_.VerifyPagedAllocation()); 2526 2527 // The space is compacted and we haven't yet wasted any space. 2528 ASSERT(Waste() == 0); 2529 2530 // Update allocation_top of each page in use and compute waste. 2531 int computed_size = 0; 2532 PageIterator it(this, PageIterator::PAGES_USED_BY_MC); 2533 while (it.has_next()) { 2534 Page* page = it.next(); 2535 Address page_top = page->AllocationTop(); 2536 computed_size += static_cast<int>(page_top - page->ObjectAreaStart()); 2537 if (it.has_next()) { 2538 accounting_stats_.WasteBytes( 2539 static_cast<int>(page->ObjectAreaEnd() - page_top)); 2540 page->SetAllocationWatermark(page_top); 2541 } 2542 } 2543 2544 // Make sure the computed size - based on the used portion of the 2545 // pages in use - matches the size we adjust during allocation. 2546 ASSERT(computed_size == Size()); 2547 } 2548 2549 2550 // Slow case for normal allocation. Try in order: (1) allocate in the next 2551 // page in the space, (2) allocate off the space's free list, (3) expand the 2552 // space, (4) fail. 2553 HeapObject* FixedSpace::SlowAllocateRaw(int size_in_bytes) { 2554 ASSERT_EQ(object_size_in_bytes_, size_in_bytes); 2555 // Linear allocation in this space has failed. If there is another page 2556 // in the space, move to that page and allocate there. This allocation 2557 // should succeed. 2558 Page* current_page = TopPageOf(allocation_info_); 2559 if (current_page->next_page()->is_valid()) { 2560 return AllocateInNextPage(current_page, size_in_bytes); 2561 } 2562 2563 // There is no next page in this space. Try free list allocation unless 2564 // that is currently forbidden. The fixed space free list implicitly assumes 2565 // that all free blocks are of the fixed size. 2566 if (!heap()->linear_allocation()) { 2567 Object* result; 2568 MaybeObject* maybe = free_list_.Allocate(); 2569 if (maybe->ToObject(&result)) { 2570 accounting_stats_.AllocateBytes(size_in_bytes); 2571 HeapObject* obj = HeapObject::cast(result); 2572 Page* p = Page::FromAddress(obj->address()); 2573 2574 if (obj->address() >= p->AllocationWatermark()) { 2575 // There should be no hole between the allocation watermark 2576 // and allocated object address. 2577 // Memory above the allocation watermark was not swept and 2578 // might contain garbage pointers to new space. 2579 ASSERT(obj->address() == p->AllocationWatermark()); 2580 p->SetAllocationWatermark(obj->address() + size_in_bytes); 2581 } 2582 2583 return obj; 2584 } 2585 } 2586 2587 // Free list allocation failed and there is no next page. Fail if we have 2588 // hit the old generation size limit that should cause a garbage 2589 // collection. 2590 if (!heap()->always_allocate() && 2591 heap()->OldGenerationAllocationLimitReached()) { 2592 return NULL; 2593 } 2594 2595 // Try to expand the space and allocate in the new next page. 2596 ASSERT(!current_page->next_page()->is_valid()); 2597 if (Expand(current_page)) { 2598 return AllocateInNextPage(current_page, size_in_bytes); 2599 } 2600 2601 // Finally, fail. 2602 return NULL; 2603 } 2604 2605 2606 // Move to the next page (there is assumed to be one) and allocate there. 2607 // The top of page block is always wasted, because it is too small to hold a 2608 // map. 2609 HeapObject* FixedSpace::AllocateInNextPage(Page* current_page, 2610 int size_in_bytes) { 2611 ASSERT(current_page->next_page()->is_valid()); 2612 ASSERT(allocation_info_.top == PageAllocationLimit(current_page)); 2613 ASSERT_EQ(object_size_in_bytes_, size_in_bytes); 2614 Page* next_page = current_page->next_page(); 2615 next_page->ClearGCFields(); 2616 current_page->SetAllocationWatermark(allocation_info_.top); 2617 accounting_stats_.WasteBytes(page_extra_); 2618 SetAllocationInfo(&allocation_info_, next_page); 2619 return AllocateLinearly(&allocation_info_, size_in_bytes); 2620 } 2621 2622 2623 void FixedSpace::DeallocateBlock(Address start, 2624 int size_in_bytes, 2625 bool add_to_freelist) { 2626 // Free-list elements in fixed space are assumed to have a fixed size. 2627 // We break the free block into chunks and add them to the free list 2628 // individually. 2629 int size = object_size_in_bytes(); 2630 ASSERT(size_in_bytes % size == 0); 2631 Address end = start + size_in_bytes; 2632 for (Address a = start; a < end; a += size) { 2633 Free(a, add_to_freelist); 2634 } 2635 } 2636 2637 2638 #ifdef DEBUG 2639 void FixedSpace::ReportStatistics() { 2640 int pct = static_cast<int>(Available() * 100 / Capacity()); 2641 PrintF(" capacity: %" V8_PTR_PREFIX "d" 2642 ", waste: %" V8_PTR_PREFIX "d" 2643 ", available: %" V8_PTR_PREFIX "d, %%%d\n", 2644 Capacity(), Waste(), Available(), pct); 2645 2646 ClearHistograms(); 2647 HeapObjectIterator obj_it(this); 2648 for (HeapObject* obj = obj_it.next(); obj != NULL; obj = obj_it.next()) 2649 CollectHistogramInfo(obj); 2650 ReportHistogram(false); 2651 } 2652 #endif 2653 2654 2655 // ----------------------------------------------------------------------------- 2656 // MapSpace implementation 2657 2658 void MapSpace::PrepareForMarkCompact(bool will_compact) { 2659 // Call prepare of the super class. 2660 FixedSpace::PrepareForMarkCompact(will_compact); 2661 2662 if (will_compact) { 2663 // Initialize map index entry. 2664 int page_count = 0; 2665 PageIterator it(this, PageIterator::ALL_PAGES); 2666 while (it.has_next()) { 2667 ASSERT_MAP_PAGE_INDEX(page_count); 2668 2669 Page* p = it.next(); 2670 ASSERT(p->mc_page_index == page_count); 2671 2672 page_addresses_[page_count++] = p->address(); 2673 } 2674 } 2675 } 2676 2677 2678 #ifdef DEBUG 2679 void MapSpace::VerifyObject(HeapObject* object) { 2680 // The object should be a map or a free-list node. 2681 ASSERT(object->IsMap() || object->IsByteArray()); 2682 } 2683 #endif 2684 2685 2686 // ----------------------------------------------------------------------------- 2687 // GlobalPropertyCellSpace implementation 2688 2689 #ifdef DEBUG 2690 void CellSpace::VerifyObject(HeapObject* object) { 2691 // The object should be a global object property cell or a free-list node. 2692 ASSERT(object->IsJSGlobalPropertyCell() || 2693 object->map() == heap()->two_pointer_filler_map()); 2694 } 2695 #endif 2696 2697 2698 // ----------------------------------------------------------------------------- 2699 // LargeObjectIterator 2700 2701 LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space) { 2702 current_ = space->first_chunk_; 2703 size_func_ = NULL; 2704 } 2705 2706 2707 LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space, 2708 HeapObjectCallback size_func) { 2709 current_ = space->first_chunk_; 2710 size_func_ = size_func; 2711 } 2712 2713 2714 HeapObject* LargeObjectIterator::next() { 2715 if (current_ == NULL) return NULL; 2716 2717 HeapObject* object = current_->GetObject(); 2718 current_ = current_->next(); 2719 return object; 2720 } 2721 2722 2723 // ----------------------------------------------------------------------------- 2724 // LargeObjectChunk 2725 2726 LargeObjectChunk* LargeObjectChunk::New(int size_in_bytes, 2727 Executability executable) { 2728 size_t requested = ChunkSizeFor(size_in_bytes); 2729 size_t size; 2730 Isolate* isolate = Isolate::Current(); 2731 void* mem = isolate->memory_allocator()->AllocateRawMemory( 2732 requested, &size, executable); 2733 if (mem == NULL) return NULL; 2734 2735 // The start of the chunk may be overlayed with a page so we have to 2736 // make sure that the page flags fit in the size field. 2737 ASSERT((size & Page::kPageFlagMask) == 0); 2738 2739 LOG(isolate, NewEvent("LargeObjectChunk", mem, size)); 2740 if (size < requested) { 2741 isolate->memory_allocator()->FreeRawMemory( 2742 mem, size, executable); 2743 LOG(isolate, DeleteEvent("LargeObjectChunk", mem)); 2744 return NULL; 2745 } 2746 2747 ObjectSpace space = (executable == EXECUTABLE) 2748 ? kObjectSpaceCodeSpace 2749 : kObjectSpaceLoSpace; 2750 isolate->memory_allocator()->PerformAllocationCallback( 2751 space, kAllocationActionAllocate, size); 2752 2753 LargeObjectChunk* chunk = reinterpret_cast<LargeObjectChunk*>(mem); 2754 chunk->size_ = size; 2755 Page* page = Page::FromAddress(RoundUp(chunk->address(), Page::kPageSize)); 2756 page->heap_ = isolate->heap(); 2757 return chunk; 2758 } 2759 2760 2761 int LargeObjectChunk::ChunkSizeFor(int size_in_bytes) { 2762 int os_alignment = static_cast<int>(OS::AllocateAlignment()); 2763 if (os_alignment < Page::kPageSize) { 2764 size_in_bytes += (Page::kPageSize - os_alignment); 2765 } 2766 return size_in_bytes + Page::kObjectStartOffset; 2767 } 2768 2769 // ----------------------------------------------------------------------------- 2770 // LargeObjectSpace 2771 2772 LargeObjectSpace::LargeObjectSpace(Heap* heap, AllocationSpace id) 2773 : Space(heap, id, NOT_EXECUTABLE), // Managed on a per-allocation basis 2774 first_chunk_(NULL), 2775 size_(0), 2776 page_count_(0), 2777 objects_size_(0) {} 2778 2779 2780 bool LargeObjectSpace::Setup() { 2781 first_chunk_ = NULL; 2782 size_ = 0; 2783 page_count_ = 0; 2784 objects_size_ = 0; 2785 return true; 2786 } 2787 2788 2789 void LargeObjectSpace::TearDown() { 2790 while (first_chunk_ != NULL) { 2791 LargeObjectChunk* chunk = first_chunk_; 2792 first_chunk_ = first_chunk_->next(); 2793 LOG(heap()->isolate(), DeleteEvent("LargeObjectChunk", chunk->address())); 2794 Page* page = Page::FromAddress(RoundUp(chunk->address(), Page::kPageSize)); 2795 Executability executable = 2796 page->IsPageExecutable() ? EXECUTABLE : NOT_EXECUTABLE; 2797 ObjectSpace space = kObjectSpaceLoSpace; 2798 if (executable == EXECUTABLE) space = kObjectSpaceCodeSpace; 2799 size_t size = chunk->size(); 2800 heap()->isolate()->memory_allocator()->FreeRawMemory(chunk->address(), 2801 size, 2802 executable); 2803 heap()->isolate()->memory_allocator()->PerformAllocationCallback( 2804 space, kAllocationActionFree, size); 2805 } 2806 2807 size_ = 0; 2808 page_count_ = 0; 2809 objects_size_ = 0; 2810 } 2811 2812 2813 #ifdef ENABLE_HEAP_PROTECTION 2814 2815 void LargeObjectSpace::Protect() { 2816 LargeObjectChunk* chunk = first_chunk_; 2817 while (chunk != NULL) { 2818 heap()->isolate()->memory_allocator()->Protect(chunk->address(), 2819 chunk->size()); 2820 chunk = chunk->next(); 2821 } 2822 } 2823 2824 2825 void LargeObjectSpace::Unprotect() { 2826 LargeObjectChunk* chunk = first_chunk_; 2827 while (chunk != NULL) { 2828 bool is_code = chunk->GetObject()->IsCode(); 2829 heap()->isolate()->memory_allocator()->Unprotect(chunk->address(), 2830 chunk->size(), is_code ? EXECUTABLE : NOT_EXECUTABLE); 2831 chunk = chunk->next(); 2832 } 2833 } 2834 2835 #endif 2836 2837 2838 MaybeObject* LargeObjectSpace::AllocateRawInternal(int requested_size, 2839 int object_size, 2840 Executability executable) { 2841 ASSERT(0 < object_size && object_size <= requested_size); 2842 2843 // Check if we want to force a GC before growing the old space further. 2844 // If so, fail the allocation. 2845 if (!heap()->always_allocate() && 2846 heap()->OldGenerationAllocationLimitReached()) { 2847 return Failure::RetryAfterGC(identity()); 2848 } 2849 2850 LargeObjectChunk* chunk = LargeObjectChunk::New(requested_size, executable); 2851 if (chunk == NULL) { 2852 return Failure::RetryAfterGC(identity()); 2853 } 2854 2855 size_ += static_cast<int>(chunk->size()); 2856 objects_size_ += requested_size; 2857 page_count_++; 2858 chunk->set_next(first_chunk_); 2859 first_chunk_ = chunk; 2860 2861 // Initialize page header. 2862 Page* page = Page::FromAddress(RoundUp(chunk->address(), Page::kPageSize)); 2863 Address object_address = page->ObjectAreaStart(); 2864 2865 // Clear the low order bit of the second word in the page to flag it as a 2866 // large object page. If the chunk_size happened to be written there, its 2867 // low order bit should already be clear. 2868 page->SetIsLargeObjectPage(true); 2869 page->SetIsPageExecutable(executable); 2870 page->SetRegionMarks(Page::kAllRegionsCleanMarks); 2871 return HeapObject::FromAddress(object_address); 2872 } 2873 2874 2875 MaybeObject* LargeObjectSpace::AllocateRawCode(int size_in_bytes) { 2876 ASSERT(0 < size_in_bytes); 2877 return AllocateRawInternal(size_in_bytes, 2878 size_in_bytes, 2879 EXECUTABLE); 2880 } 2881 2882 2883 MaybeObject* LargeObjectSpace::AllocateRawFixedArray(int size_in_bytes) { 2884 ASSERT(0 < size_in_bytes); 2885 return AllocateRawInternal(size_in_bytes, 2886 size_in_bytes, 2887 NOT_EXECUTABLE); 2888 } 2889 2890 2891 MaybeObject* LargeObjectSpace::AllocateRaw(int size_in_bytes) { 2892 ASSERT(0 < size_in_bytes); 2893 return AllocateRawInternal(size_in_bytes, 2894 size_in_bytes, 2895 NOT_EXECUTABLE); 2896 } 2897 2898 2899 // GC support 2900 MaybeObject* LargeObjectSpace::FindObject(Address a) { 2901 for (LargeObjectChunk* chunk = first_chunk_; 2902 chunk != NULL; 2903 chunk = chunk->next()) { 2904 Address chunk_address = chunk->address(); 2905 if (chunk_address <= a && a < chunk_address + chunk->size()) { 2906 return chunk->GetObject(); 2907 } 2908 } 2909 return Failure::Exception(); 2910 } 2911 2912 2913 LargeObjectChunk* LargeObjectSpace::FindChunkContainingPc(Address pc) { 2914 // TODO(853): Change this implementation to only find executable 2915 // chunks and use some kind of hash-based approach to speed it up. 2916 for (LargeObjectChunk* chunk = first_chunk_; 2917 chunk != NULL; 2918 chunk = chunk->next()) { 2919 Address chunk_address = chunk->address(); 2920 if (chunk_address <= pc && pc < chunk_address + chunk->size()) { 2921 return chunk; 2922 } 2923 } 2924 return NULL; 2925 } 2926 2927 2928 void LargeObjectSpace::IterateDirtyRegions(ObjectSlotCallback copy_object) { 2929 LargeObjectIterator it(this); 2930 for (HeapObject* object = it.next(); object != NULL; object = it.next()) { 2931 // We only have code, sequential strings, or fixed arrays in large 2932 // object space, and only fixed arrays can possibly contain pointers to 2933 // the young generation. 2934 if (object->IsFixedArray()) { 2935 Page* page = Page::FromAddress(object->address()); 2936 uint32_t marks = page->GetRegionMarks(); 2937 uint32_t newmarks = Page::kAllRegionsCleanMarks; 2938 2939 if (marks != Page::kAllRegionsCleanMarks) { 2940 // For a large page a single dirty mark corresponds to several 2941 // regions (modulo 32). So we treat a large page as a sequence of 2942 // normal pages of size Page::kPageSize having same dirty marks 2943 // and subsequently iterate dirty regions on each of these pages. 2944 Address start = object->address(); 2945 Address end = page->ObjectAreaEnd(); 2946 Address object_end = start + object->Size(); 2947 2948 // Iterate regions of the first normal page covering object. 2949 uint32_t first_region_number = page->GetRegionNumberForAddress(start); 2950 newmarks |= 2951 heap()->IterateDirtyRegions(marks >> first_region_number, 2952 start, 2953 end, 2954 &Heap::IteratePointersInDirtyRegion, 2955 copy_object) << first_region_number; 2956 2957 start = end; 2958 end = start + Page::kPageSize; 2959 while (end <= object_end) { 2960 // Iterate next 32 regions. 2961 newmarks |= 2962 heap()->IterateDirtyRegions(marks, 2963 start, 2964 end, 2965 &Heap::IteratePointersInDirtyRegion, 2966 copy_object); 2967 start = end; 2968 end = start + Page::kPageSize; 2969 } 2970 2971 if (start != object_end) { 2972 // Iterate the last piece of an object which is less than 2973 // Page::kPageSize. 2974 newmarks |= 2975 heap()->IterateDirtyRegions(marks, 2976 start, 2977 object_end, 2978 &Heap::IteratePointersInDirtyRegion, 2979 copy_object); 2980 } 2981 2982 page->SetRegionMarks(newmarks); 2983 } 2984 } 2985 } 2986 } 2987 2988 2989 void LargeObjectSpace::FreeUnmarkedObjects() { 2990 LargeObjectChunk* previous = NULL; 2991 LargeObjectChunk* current = first_chunk_; 2992 while (current != NULL) { 2993 HeapObject* object = current->GetObject(); 2994 if (object->IsMarked()) { 2995 object->ClearMark(); 2996 heap()->mark_compact_collector()->tracer()->decrement_marked_count(); 2997 previous = current; 2998 current = current->next(); 2999 } else { 3000 Page* page = Page::FromAddress(RoundUp(current->address(), 3001 Page::kPageSize)); 3002 Executability executable = 3003 page->IsPageExecutable() ? EXECUTABLE : NOT_EXECUTABLE; 3004 Address chunk_address = current->address(); 3005 size_t chunk_size = current->size(); 3006 3007 // Cut the chunk out from the chunk list. 3008 current = current->next(); 3009 if (previous == NULL) { 3010 first_chunk_ = current; 3011 } else { 3012 previous->set_next(current); 3013 } 3014 3015 // Free the chunk. 3016 heap()->mark_compact_collector()->ReportDeleteIfNeeded( 3017 object, heap()->isolate()); 3018 LiveObjectList::ProcessNonLive(object); 3019 3020 size_ -= static_cast<int>(chunk_size); 3021 objects_size_ -= object->Size(); 3022 page_count_--; 3023 ObjectSpace space = kObjectSpaceLoSpace; 3024 if (executable == EXECUTABLE) space = kObjectSpaceCodeSpace; 3025 heap()->isolate()->memory_allocator()->FreeRawMemory(chunk_address, 3026 chunk_size, 3027 executable); 3028 heap()->isolate()->memory_allocator()->PerformAllocationCallback( 3029 space, kAllocationActionFree, size_); 3030 LOG(heap()->isolate(), DeleteEvent("LargeObjectChunk", chunk_address)); 3031 } 3032 } 3033 } 3034 3035 3036 bool LargeObjectSpace::Contains(HeapObject* object) { 3037 Address address = object->address(); 3038 if (heap()->new_space()->Contains(address)) { 3039 return false; 3040 } 3041 Page* page = Page::FromAddress(address); 3042 3043 SLOW_ASSERT(!page->IsLargeObjectPage() 3044 || !FindObject(address)->IsFailure()); 3045 3046 return page->IsLargeObjectPage(); 3047 } 3048 3049 3050 #ifdef DEBUG 3051 // We do not assume that the large object iterator works, because it depends 3052 // on the invariants we are checking during verification. 3053 void LargeObjectSpace::Verify() { 3054 for (LargeObjectChunk* chunk = first_chunk_; 3055 chunk != NULL; 3056 chunk = chunk->next()) { 3057 // Each chunk contains an object that starts at the large object page's 3058 // object area start. 3059 HeapObject* object = chunk->GetObject(); 3060 Page* page = Page::FromAddress(object->address()); 3061 ASSERT(object->address() == page->ObjectAreaStart()); 3062 3063 // The first word should be a map, and we expect all map pointers to be 3064 // in map space. 3065 Map* map = object->map(); 3066 ASSERT(map->IsMap()); 3067 ASSERT(heap()->map_space()->Contains(map)); 3068 3069 // We have only code, sequential strings, external strings 3070 // (sequential strings that have been morphed into external 3071 // strings), fixed arrays, and byte arrays in large object space. 3072 ASSERT(object->IsCode() || object->IsSeqString() || 3073 object->IsExternalString() || object->IsFixedArray() || 3074 object->IsByteArray()); 3075 3076 // The object itself should look OK. 3077 object->Verify(); 3078 3079 // Byte arrays and strings don't have interior pointers. 3080 if (object->IsCode()) { 3081 VerifyPointersVisitor code_visitor; 3082 object->IterateBody(map->instance_type(), 3083 object->Size(), 3084 &code_visitor); 3085 } else if (object->IsFixedArray()) { 3086 // We loop over fixed arrays ourselves, rather then using the visitor, 3087 // because the visitor doesn't support the start/offset iteration 3088 // needed for IsRegionDirty. 3089 FixedArray* array = FixedArray::cast(object); 3090 for (int j = 0; j < array->length(); j++) { 3091 Object* element = array->get(j); 3092 if (element->IsHeapObject()) { 3093 HeapObject* element_object = HeapObject::cast(element); 3094 ASSERT(heap()->Contains(element_object)); 3095 ASSERT(element_object->map()->IsMap()); 3096 if (heap()->InNewSpace(element_object)) { 3097 Address array_addr = object->address(); 3098 Address element_addr = array_addr + FixedArray::kHeaderSize + 3099 j * kPointerSize; 3100 3101 ASSERT(Page::FromAddress(array_addr)->IsRegionDirty(element_addr)); 3102 } 3103 } 3104 } 3105 } 3106 } 3107 } 3108 3109 3110 void LargeObjectSpace::Print() { 3111 LargeObjectIterator it(this); 3112 for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) { 3113 obj->Print(); 3114 } 3115 } 3116 3117 3118 void LargeObjectSpace::ReportStatistics() { 3119 PrintF(" size: %" V8_PTR_PREFIX "d\n", size_); 3120 int num_objects = 0; 3121 ClearHistograms(); 3122 LargeObjectIterator it(this); 3123 for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) { 3124 num_objects++; 3125 CollectHistogramInfo(obj); 3126 } 3127 3128 PrintF(" number of objects %d, " 3129 "size of objects %" V8_PTR_PREFIX "d\n", num_objects, objects_size_); 3130 if (num_objects > 0) ReportHistogram(false); 3131 } 3132 3133 3134 void LargeObjectSpace::CollectCodeStatistics() { 3135 Isolate* isolate = heap()->isolate(); 3136 LargeObjectIterator obj_it(this); 3137 for (HeapObject* obj = obj_it.next(); obj != NULL; obj = obj_it.next()) { 3138 if (obj->IsCode()) { 3139 Code* code = Code::cast(obj); 3140 isolate->code_kind_statistics()[code->kind()] += code->Size(); 3141 } 3142 } 3143 } 3144 #endif // DEBUG 3145 3146 } } // namespace v8::internal 3147