blob: c69579a9d8319bc6d6ba97b07273eff96d471e9c [file] [log] [blame]
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001// Copyright 2006-2008 the V8 project authors. All rights reserved.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002// 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 "macro-assembler.h"
31#include "mark-compact.h"
32#include "platform.h"
33
kasperl@chromium.org71affb52009-05-26 05:44:31 +000034namespace v8 {
35namespace internal {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000036
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000037// For contiguous spaces, top should be in the space (or at the end) and limit
38// should be the end of the space.
39#define ASSERT_SEMISPACE_ALLOCATION_INFO(info, space) \
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +000040 ASSERT((space).low() <= (info).top \
41 && (info).top <= (space).high() \
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +000042 && (info).limit == (space).high())
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000043
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +000044
45// ----------------------------------------------------------------------------
46// HeapObjectIterator
47
48HeapObjectIterator::HeapObjectIterator(PagedSpace* space) {
49 Initialize(space->bottom(), space->top(), NULL);
50}
51
52
53HeapObjectIterator::HeapObjectIterator(PagedSpace* space,
54 HeapObjectCallback size_func) {
55 Initialize(space->bottom(), space->top(), size_func);
56}
57
58
59HeapObjectIterator::HeapObjectIterator(PagedSpace* space, Address start) {
60 Initialize(start, space->top(), NULL);
61}
62
63
64HeapObjectIterator::HeapObjectIterator(PagedSpace* space, Address start,
65 HeapObjectCallback size_func) {
66 Initialize(start, space->top(), size_func);
67}
68
69
70void HeapObjectIterator::Initialize(Address cur, Address end,
71 HeapObjectCallback size_f) {
72 cur_addr_ = cur;
73 end_addr_ = end;
74 end_page_ = Page::FromAllocationTop(end);
75 size_func_ = size_f;
76 Page* p = Page::FromAllocationTop(cur_addr_);
77 cur_limit_ = (p == end_page_) ? end_addr_ : p->AllocationTop();
78
79#ifdef DEBUG
80 Verify();
81#endif
82}
83
84
85bool HeapObjectIterator::HasNextInNextPage() {
86 if (cur_addr_ == end_addr_) return false;
87
88 Page* cur_page = Page::FromAllocationTop(cur_addr_);
89 cur_page = cur_page->next_page();
90 ASSERT(cur_page->is_valid());
91
92 cur_addr_ = cur_page->ObjectAreaStart();
93 cur_limit_ = (cur_page == end_page_) ? end_addr_ : cur_page->AllocationTop();
94
95 ASSERT(cur_addr_ < cur_limit_);
96#ifdef DEBUG
97 Verify();
98#endif
99 return true;
100}
101
102
103#ifdef DEBUG
104void HeapObjectIterator::Verify() {
105 Page* p = Page::FromAllocationTop(cur_addr_);
106 ASSERT(p == Page::FromAllocationTop(cur_limit_));
107 ASSERT(p->Offset(cur_addr_) <= p->Offset(cur_limit_));
108}
109#endif
110
111
112// -----------------------------------------------------------------------------
113// PageIterator
114
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000115PageIterator::PageIterator(PagedSpace* space, Mode mode) : space_(space) {
116 prev_page_ = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000117 switch (mode) {
118 case PAGES_IN_USE:
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000119 stop_page_ = space->AllocationTopPage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000120 break;
121 case PAGES_USED_BY_MC:
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000122 stop_page_ = space->MCRelocationTopPage();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000123 break;
124 case ALL_PAGES:
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000125#ifdef DEBUG
126 // Verify that the cached last page in the space is actually the
127 // last page.
128 for (Page* p = space->first_page_; p->is_valid(); p = p->next_page()) {
129 if (!p->next_page()->is_valid()) {
130 ASSERT(space->last_page_ == p);
131 }
132 }
133#endif
134 stop_page_ = space->last_page_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000135 break;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000136 }
137}
138
139
140// -----------------------------------------------------------------------------
141// Page
142
143#ifdef DEBUG
144Page::RSetState Page::rset_state_ = Page::IN_USE;
145#endif
146
147// -----------------------------------------------------------------------------
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000148// CodeRange
149
150List<CodeRange::FreeBlock> CodeRange::free_list_(0);
151List<CodeRange::FreeBlock> CodeRange::allocation_list_(0);
152int CodeRange::current_allocation_block_index_ = 0;
153VirtualMemory* CodeRange::code_range_ = NULL;
154
155
156bool CodeRange::Setup(const size_t requested) {
157 ASSERT(code_range_ == NULL);
158
159 code_range_ = new VirtualMemory(requested);
160 CHECK(code_range_ != NULL);
161 if (!code_range_->IsReserved()) {
162 delete code_range_;
163 code_range_ = NULL;
164 return false;
165 }
166
167 // We are sure that we have mapped a block of requested addresses.
168 ASSERT(code_range_->size() == requested);
169 LOG(NewEvent("CodeRange", code_range_->address(), requested));
170 allocation_list_.Add(FreeBlock(code_range_->address(), code_range_->size()));
171 current_allocation_block_index_ = 0;
172 return true;
173}
174
175
176int CodeRange::CompareFreeBlockAddress(const FreeBlock* left,
177 const FreeBlock* right) {
178 // The entire point of CodeRange is that the difference between two
179 // addresses in the range can be represented as a signed 32-bit int,
180 // so the cast is semantically correct.
181 return static_cast<int>(left->start - right->start);
182}
183
184
185void CodeRange::GetNextAllocationBlock(size_t requested) {
186 for (current_allocation_block_index_++;
187 current_allocation_block_index_ < allocation_list_.length();
188 current_allocation_block_index_++) {
189 if (requested <= allocation_list_[current_allocation_block_index_].size) {
190 return; // Found a large enough allocation block.
191 }
192 }
193
194 // Sort and merge the free blocks on the free list and the allocation list.
195 free_list_.AddAll(allocation_list_);
196 allocation_list_.Clear();
197 free_list_.Sort(&CompareFreeBlockAddress);
198 for (int i = 0; i < free_list_.length();) {
199 FreeBlock merged = free_list_[i];
200 i++;
201 // Add adjacent free blocks to the current merged block.
202 while (i < free_list_.length() &&
203 free_list_[i].start == merged.start + merged.size) {
204 merged.size += free_list_[i].size;
205 i++;
206 }
207 if (merged.size > 0) {
208 allocation_list_.Add(merged);
209 }
210 }
211 free_list_.Clear();
212
213 for (current_allocation_block_index_ = 0;
214 current_allocation_block_index_ < allocation_list_.length();
215 current_allocation_block_index_++) {
216 if (requested <= allocation_list_[current_allocation_block_index_].size) {
217 return; // Found a large enough allocation block.
218 }
219 }
220
221 // Code range is full or too fragmented.
222 V8::FatalProcessOutOfMemory("CodeRange::GetNextAllocationBlock");
223}
224
225
226
227void* CodeRange::AllocateRawMemory(const size_t requested, size_t* allocated) {
228 ASSERT(current_allocation_block_index_ < allocation_list_.length());
229 if (requested > allocation_list_[current_allocation_block_index_].size) {
230 // Find an allocation block large enough. This function call may
231 // call V8::FatalProcessOutOfMemory if it cannot find a large enough block.
232 GetNextAllocationBlock(requested);
233 }
234 // Commit the requested memory at the start of the current allocation block.
235 *allocated = RoundUp(requested, Page::kPageSize);
236 FreeBlock current = allocation_list_[current_allocation_block_index_];
237 if (*allocated >= current.size - Page::kPageSize) {
238 // Don't leave a small free block, useless for a large object or chunk.
239 *allocated = current.size;
240 }
241 ASSERT(*allocated <= current.size);
242 if (!code_range_->Commit(current.start, *allocated, true)) {
243 *allocated = 0;
244 return NULL;
245 }
246 allocation_list_[current_allocation_block_index_].start += *allocated;
247 allocation_list_[current_allocation_block_index_].size -= *allocated;
248 if (*allocated == current.size) {
249 GetNextAllocationBlock(0); // This block is used up, get the next one.
250 }
251 return current.start;
252}
253
254
255void CodeRange::FreeRawMemory(void* address, size_t length) {
256 free_list_.Add(FreeBlock(address, length));
257 code_range_->Uncommit(address, length);
258}
259
260
261void CodeRange::TearDown() {
262 delete code_range_; // Frees all memory in the virtual memory range.
263 code_range_ = NULL;
264 free_list_.Free();
265 allocation_list_.Free();
266}
267
268
269// -----------------------------------------------------------------------------
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000270// MemoryAllocator
271//
272int MemoryAllocator::capacity_ = 0;
273int MemoryAllocator::size_ = 0;
274
275VirtualMemory* MemoryAllocator::initial_chunk_ = NULL;
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.
279const int kEstimatedNumberOfChunks = 270;
280List<MemoryAllocator::ChunkInfo> MemoryAllocator::chunks_(
281 kEstimatedNumberOfChunks);
282List<int> MemoryAllocator::free_chunk_ids_(kEstimatedNumberOfChunks);
283int MemoryAllocator::max_nof_chunks_ = 0;
284int MemoryAllocator::top_ = 0;
285
286
287void MemoryAllocator::Push(int free_chunk_id) {
288 ASSERT(max_nof_chunks_ > 0);
289 ASSERT(top_ < max_nof_chunks_);
290 free_chunk_ids_[top_++] = free_chunk_id;
291}
292
293
294int MemoryAllocator::Pop() {
295 ASSERT(top_ > 0);
296 return free_chunk_ids_[--top_];
297}
298
299
300bool MemoryAllocator::Setup(int capacity) {
301 capacity_ = RoundUp(capacity, Page::kPageSize);
302
303 // Over-estimate the size of chunks_ array. It assumes the expansion of old
304 // space is always in the unit of a chunk (kChunkSize) except the last
305 // expansion.
306 //
307 // Due to alignment, allocated space might be one page less than required
308 // number (kPagesPerChunk) of pages for old spaces.
309 //
kasper.lund7276f142008-07-30 08:49:36 +0000310 // Reserve two chunk ids for semispaces, one for map space, one for old
311 // space, and one for code space.
312 max_nof_chunks_ = (capacity_ / (kChunkSize - Page::kPageSize)) + 5;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000313 if (max_nof_chunks_ > kMaxNofChunks) return false;
314
315 size_ = 0;
316 ChunkInfo info; // uninitialized element.
317 for (int i = max_nof_chunks_ - 1; i >= 0; i--) {
318 chunks_.Add(info);
319 free_chunk_ids_.Add(i);
320 }
321 top_ = max_nof_chunks_;
322 return true;
323}
324
325
326void MemoryAllocator::TearDown() {
327 for (int i = 0; i < max_nof_chunks_; i++) {
328 if (chunks_[i].address() != NULL) DeleteChunk(i);
329 }
330 chunks_.Clear();
331 free_chunk_ids_.Clear();
332
333 if (initial_chunk_ != NULL) {
334 LOG(DeleteEvent("InitialChunk", initial_chunk_->address()));
335 delete initial_chunk_;
336 initial_chunk_ = NULL;
337 }
338
339 ASSERT(top_ == max_nof_chunks_); // all chunks are free
340 top_ = 0;
341 capacity_ = 0;
342 size_ = 0;
343 max_nof_chunks_ = 0;
344}
345
346
347void* MemoryAllocator::AllocateRawMemory(const size_t requested,
kasper.lund7276f142008-07-30 08:49:36 +0000348 size_t* allocated,
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000349 Executability executable) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000350 if (size_ + static_cast<int>(requested) > capacity_) return NULL;
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000351 void* mem;
352 if (executable == EXECUTABLE && CodeRange::exists()) {
353 mem = CodeRange::AllocateRawMemory(requested, allocated);
354 } else {
355 mem = OS::Allocate(requested, allocated, (executable == EXECUTABLE));
356 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000357 int alloced = *allocated;
358 size_ += alloced;
359 Counters::memory_allocated.Increment(alloced);
360 return mem;
361}
362
363
364void MemoryAllocator::FreeRawMemory(void* mem, size_t length) {
sgjesse@chromium.orgc5145742009-10-07 09:00:33 +0000365 if (CodeRange::contains(static_cast<Address>(mem))) {
366 CodeRange::FreeRawMemory(mem, length);
367 } else {
368 OS::Free(mem, length);
369 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000370 Counters::memory_allocated.Decrement(length);
371 size_ -= length;
372 ASSERT(size_ >= 0);
373}
374
375
376void* MemoryAllocator::ReserveInitialChunk(const size_t requested) {
377 ASSERT(initial_chunk_ == NULL);
378
379 initial_chunk_ = new VirtualMemory(requested);
380 CHECK(initial_chunk_ != NULL);
381 if (!initial_chunk_->IsReserved()) {
382 delete initial_chunk_;
383 initial_chunk_ = NULL;
384 return NULL;
385 }
386
387 // We are sure that we have mapped a block of requested addresses.
388 ASSERT(initial_chunk_->size() == requested);
389 LOG(NewEvent("InitialChunk", initial_chunk_->address(), requested));
390 size_ += requested;
391 return initial_chunk_->address();
392}
393
394
395static int PagesInChunk(Address start, size_t size) {
396 // The first page starts on the first page-aligned address from start onward
397 // and the last page ends on the last page-aligned address before
398 // start+size. Page::kPageSize is a power of two so we can divide by
399 // shifting.
400 return (RoundDown(start + size, Page::kPageSize)
401 - RoundUp(start, Page::kPageSize)) >> Page::kPageSizeBits;
402}
403
404
405Page* MemoryAllocator::AllocatePages(int requested_pages, int* allocated_pages,
406 PagedSpace* owner) {
407 if (requested_pages <= 0) return Page::FromAddress(NULL);
408 size_t chunk_size = requested_pages * Page::kPageSize;
409
410 // There is not enough space to guarantee the desired number pages can be
411 // allocated.
412 if (size_ + static_cast<int>(chunk_size) > capacity_) {
413 // Request as many pages as we can.
414 chunk_size = capacity_ - size_;
415 requested_pages = chunk_size >> Page::kPageSizeBits;
416
417 if (requested_pages <= 0) return Page::FromAddress(NULL);
418 }
kasper.lund7276f142008-07-30 08:49:36 +0000419 void* chunk = AllocateRawMemory(chunk_size, &chunk_size, owner->executable());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000420 if (chunk == NULL) return Page::FromAddress(NULL);
421 LOG(NewEvent("PagedChunk", chunk, chunk_size));
422
423 *allocated_pages = PagesInChunk(static_cast<Address>(chunk), chunk_size);
424 if (*allocated_pages == 0) {
425 FreeRawMemory(chunk, chunk_size);
426 LOG(DeleteEvent("PagedChunk", chunk));
427 return Page::FromAddress(NULL);
428 }
429
430 int chunk_id = Pop();
431 chunks_[chunk_id].init(static_cast<Address>(chunk), chunk_size, owner);
432
433 return InitializePagesInChunk(chunk_id, *allocated_pages, owner);
434}
435
436
437Page* MemoryAllocator::CommitPages(Address start, size_t size,
438 PagedSpace* owner, int* num_pages) {
439 ASSERT(start != NULL);
440 *num_pages = PagesInChunk(start, size);
441 ASSERT(*num_pages > 0);
442 ASSERT(initial_chunk_ != NULL);
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000443 ASSERT(InInitialChunk(start));
444 ASSERT(InInitialChunk(start + size - 1));
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000445 if (!initial_chunk_->Commit(start, size, owner->executable() == EXECUTABLE)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000446 return Page::FromAddress(NULL);
447 }
448 Counters::memory_allocated.Increment(size);
449
450 // So long as we correctly overestimated the number of chunks we should not
451 // run out of chunk ids.
452 CHECK(!OutOfChunkIds());
453 int chunk_id = Pop();
454 chunks_[chunk_id].init(start, size, owner);
455 return InitializePagesInChunk(chunk_id, *num_pages, owner);
456}
457
458
kasper.lund7276f142008-07-30 08:49:36 +0000459bool MemoryAllocator::CommitBlock(Address start,
460 size_t size,
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000461 Executability executable) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000462 ASSERT(start != NULL);
463 ASSERT(size > 0);
464 ASSERT(initial_chunk_ != NULL);
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000465 ASSERT(InInitialChunk(start));
466 ASSERT(InInitialChunk(start + size - 1));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000467
kasper.lund7276f142008-07-30 08:49:36 +0000468 if (!initial_chunk_->Commit(start, size, executable)) return false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000469 Counters::memory_allocated.Increment(size);
470 return true;
471}
472
ager@chromium.orgadd848f2009-08-13 12:44:13 +0000473bool MemoryAllocator::UncommitBlock(Address start, size_t size) {
474 ASSERT(start != NULL);
475 ASSERT(size > 0);
476 ASSERT(initial_chunk_ != NULL);
477 ASSERT(InInitialChunk(start));
478 ASSERT(InInitialChunk(start + size - 1));
479
480 if (!initial_chunk_->Uncommit(start, size)) return false;
481 Counters::memory_allocated.Decrement(size);
482 return true;
483}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000484
485Page* MemoryAllocator::InitializePagesInChunk(int chunk_id, int pages_in_chunk,
486 PagedSpace* owner) {
487 ASSERT(IsValidChunk(chunk_id));
488 ASSERT(pages_in_chunk > 0);
489
490 Address chunk_start = chunks_[chunk_id].address();
491
492 Address low = RoundUp(chunk_start, Page::kPageSize);
493
494#ifdef DEBUG
495 size_t chunk_size = chunks_[chunk_id].size();
496 Address high = RoundDown(chunk_start + chunk_size, Page::kPageSize);
497 ASSERT(pages_in_chunk <=
498 ((OffsetFrom(high) - OffsetFrom(low)) / Page::kPageSize));
499#endif
500
501 Address page_addr = low;
502 for (int i = 0; i < pages_in_chunk; i++) {
503 Page* p = Page::FromAddress(page_addr);
504 p->opaque_header = OffsetFrom(page_addr + Page::kPageSize) | chunk_id;
505 p->is_normal_page = 1;
506 page_addr += Page::kPageSize;
507 }
508
509 // Set the next page of the last page to 0.
510 Page* last_page = Page::FromAddress(page_addr - Page::kPageSize);
511 last_page->opaque_header = OffsetFrom(0) | chunk_id;
512
513 return Page::FromAddress(low);
514}
515
516
517Page* MemoryAllocator::FreePages(Page* p) {
518 if (!p->is_valid()) return p;
519
520 // Find the first page in the same chunk as 'p'
521 Page* first_page = FindFirstPageInSameChunk(p);
522 Page* page_to_return = Page::FromAddress(NULL);
523
524 if (p != first_page) {
525 // Find the last page in the same chunk as 'prev'.
526 Page* last_page = FindLastPageInSameChunk(p);
527 first_page = GetNextPage(last_page); // first page in next chunk
528
529 // set the next_page of last_page to NULL
530 SetNextPage(last_page, Page::FromAddress(NULL));
531 page_to_return = p; // return 'p' when exiting
532 }
533
534 while (first_page->is_valid()) {
535 int chunk_id = GetChunkId(first_page);
536 ASSERT(IsValidChunk(chunk_id));
537
538 // Find the first page of the next chunk before deleting this chunk.
539 first_page = GetNextPage(FindLastPageInSameChunk(first_page));
540
541 // Free the current chunk.
542 DeleteChunk(chunk_id);
543 }
544
545 return page_to_return;
546}
547
548
549void MemoryAllocator::DeleteChunk(int chunk_id) {
550 ASSERT(IsValidChunk(chunk_id));
551
552 ChunkInfo& c = chunks_[chunk_id];
553
554 // We cannot free a chunk contained in the initial chunk because it was not
555 // allocated with AllocateRawMemory. Instead we uncommit the virtual
556 // memory.
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000557 if (InInitialChunk(c.address())) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000558 // TODO(1240712): VirtualMemory::Uncommit has a return value which
559 // is ignored here.
560 initial_chunk_->Uncommit(c.address(), c.size());
561 Counters::memory_allocated.Decrement(c.size());
562 } else {
563 LOG(DeleteEvent("PagedChunk", c.address()));
564 FreeRawMemory(c.address(), c.size());
565 }
566 c.init(NULL, 0, NULL);
567 Push(chunk_id);
568}
569
570
571Page* MemoryAllocator::FindFirstPageInSameChunk(Page* p) {
572 int chunk_id = GetChunkId(p);
573 ASSERT(IsValidChunk(chunk_id));
574
575 Address low = RoundUp(chunks_[chunk_id].address(), Page::kPageSize);
576 return Page::FromAddress(low);
577}
578
579
580Page* MemoryAllocator::FindLastPageInSameChunk(Page* p) {
581 int chunk_id = GetChunkId(p);
582 ASSERT(IsValidChunk(chunk_id));
583
584 Address chunk_start = chunks_[chunk_id].address();
585 size_t chunk_size = chunks_[chunk_id].size();
586
587 Address high = RoundDown(chunk_start + chunk_size, Page::kPageSize);
588 ASSERT(chunk_start <= p->address() && p->address() < high);
589
590 return Page::FromAddress(high - Page::kPageSize);
591}
592
593
594#ifdef DEBUG
595void MemoryAllocator::ReportStatistics() {
596 float pct = static_cast<float>(capacity_ - size_) / capacity_;
597 PrintF(" capacity: %d, used: %d, available: %%%d\n\n",
598 capacity_, size_, static_cast<int>(pct*100));
599}
600#endif
601
602
603// -----------------------------------------------------------------------------
604// PagedSpace implementation
605
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000606PagedSpace::PagedSpace(int max_capacity,
607 AllocationSpace id,
608 Executability executable)
kasper.lund7276f142008-07-30 08:49:36 +0000609 : Space(id, executable) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000610 max_capacity_ = (RoundDown(max_capacity, Page::kPageSize) / Page::kPageSize)
611 * Page::kObjectAreaSize;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000612 accounting_stats_.Clear();
613
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000614 allocation_info_.top = NULL;
615 allocation_info_.limit = NULL;
616
617 mc_forwarding_info_.top = NULL;
618 mc_forwarding_info_.limit = NULL;
619}
620
621
622bool PagedSpace::Setup(Address start, size_t size) {
623 if (HasBeenSetup()) return false;
624
625 int num_pages = 0;
626 // Try to use the virtual memory range passed to us. If it is too small to
627 // contain at least one page, ignore it and allocate instead.
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000628 int pages_in_chunk = PagesInChunk(start, size);
629 if (pages_in_chunk > 0) {
630 first_page_ = MemoryAllocator::CommitPages(RoundUp(start, Page::kPageSize),
631 Page::kPageSize * pages_in_chunk,
632 this, &num_pages);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000633 } else {
634 int requested_pages = Min(MemoryAllocator::kPagesPerChunk,
635 max_capacity_ / Page::kObjectAreaSize);
636 first_page_ =
637 MemoryAllocator::AllocatePages(requested_pages, &num_pages, this);
638 if (!first_page_->is_valid()) return false;
639 }
640
641 // We are sure that the first page is valid and that we have at least one
642 // page.
643 ASSERT(first_page_->is_valid());
644 ASSERT(num_pages > 0);
645 accounting_stats_.ExpandSpace(num_pages * Page::kObjectAreaSize);
646 ASSERT(Capacity() <= max_capacity_);
647
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000648 // Sequentially initialize remembered sets in the newly allocated
649 // pages and cache the current last page in the space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000650 for (Page* p = first_page_; p->is_valid(); p = p->next_page()) {
651 p->ClearRSet();
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000652 last_page_ = p;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000653 }
654
655 // Use first_page_ for allocation.
656 SetAllocationInfo(&allocation_info_, first_page_);
657
658 return true;
659}
660
661
662bool PagedSpace::HasBeenSetup() {
663 return (Capacity() > 0);
664}
665
666
667void PagedSpace::TearDown() {
668 first_page_ = MemoryAllocator::FreePages(first_page_);
669 ASSERT(!first_page_->is_valid());
670
671 accounting_stats_.Clear();
672}
673
674
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000675#ifdef ENABLE_HEAP_PROTECTION
676
677void PagedSpace::Protect() {
678 Page* page = first_page_;
679 while (page->is_valid()) {
680 MemoryAllocator::ProtectChunkFromPage(page);
681 page = MemoryAllocator::FindLastPageInSameChunk(page)->next_page();
682 }
683}
684
685
686void PagedSpace::Unprotect() {
687 Page* page = first_page_;
688 while (page->is_valid()) {
689 MemoryAllocator::UnprotectChunkFromPage(page);
690 page = MemoryAllocator::FindLastPageInSameChunk(page)->next_page();
691 }
692}
693
694#endif
695
696
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000697void PagedSpace::ClearRSet() {
698 PageIterator it(this, PageIterator::ALL_PAGES);
699 while (it.has_next()) {
700 it.next()->ClearRSet();
701 }
702}
703
704
705Object* PagedSpace::FindObject(Address addr) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000706 // Note: this function can only be called before or after mark-compact GC
707 // because it accesses map pointers.
708 ASSERT(!MarkCompactCollector::in_use());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000709
710 if (!Contains(addr)) return Failure::Exception();
711
712 Page* p = Page::FromAddress(addr);
kasper.lund7276f142008-07-30 08:49:36 +0000713 ASSERT(IsUsed(p));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000714 Address cur = p->ObjectAreaStart();
715 Address end = p->AllocationTop();
716 while (cur < end) {
717 HeapObject* obj = HeapObject::FromAddress(cur);
718 Address next = cur + obj->Size();
719 if ((cur <= addr) && (addr < next)) return obj;
720 cur = next;
721 }
722
kasper.lund7276f142008-07-30 08:49:36 +0000723 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000724 return Failure::Exception();
725}
726
727
kasper.lund7276f142008-07-30 08:49:36 +0000728bool PagedSpace::IsUsed(Page* page) {
729 PageIterator it(this, PageIterator::PAGES_IN_USE);
730 while (it.has_next()) {
731 if (page == it.next()) return true;
732 }
733 return false;
734}
735
736
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000737void PagedSpace::SetAllocationInfo(AllocationInfo* alloc_info, Page* p) {
738 alloc_info->top = p->ObjectAreaStart();
739 alloc_info->limit = p->ObjectAreaEnd();
kasper.lund7276f142008-07-30 08:49:36 +0000740 ASSERT(alloc_info->VerifyPagedAllocation());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000741}
742
743
744void PagedSpace::MCResetRelocationInfo() {
745 // Set page indexes.
746 int i = 0;
747 PageIterator it(this, PageIterator::ALL_PAGES);
748 while (it.has_next()) {
749 Page* p = it.next();
750 p->mc_page_index = i++;
751 }
752
753 // Set mc_forwarding_info_ to the first page in the space.
754 SetAllocationInfo(&mc_forwarding_info_, first_page_);
755 // All the bytes in the space are 'available'. We will rediscover
756 // allocated and wasted bytes during GC.
757 accounting_stats_.Reset();
758}
759
760
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000761int PagedSpace::MCSpaceOffsetForAddress(Address addr) {
762#ifdef DEBUG
763 // The Contains function considers the address at the beginning of a
764 // page in the page, MCSpaceOffsetForAddress considers it is in the
765 // previous page.
766 if (Page::IsAlignedToPageSize(addr)) {
767 ASSERT(Contains(addr - kPointerSize));
768 } else {
769 ASSERT(Contains(addr));
770 }
771#endif
772
773 // If addr is at the end of a page, it belongs to previous page
774 Page* p = Page::IsAlignedToPageSize(addr)
775 ? Page::FromAllocationTop(addr)
776 : Page::FromAddress(addr);
777 int index = p->mc_page_index;
778 return (index * Page::kPageSize) + p->Offset(addr);
779}
780
781
kasper.lund7276f142008-07-30 08:49:36 +0000782// Slow case for reallocating and promoting objects during a compacting
783// collection. This function is not space-specific.
784HeapObject* PagedSpace::SlowMCAllocateRaw(int size_in_bytes) {
785 Page* current_page = TopPageOf(mc_forwarding_info_);
786 if (!current_page->next_page()->is_valid()) {
787 if (!Expand(current_page)) {
788 return NULL;
789 }
790 }
791
792 // There are surely more pages in the space now.
793 ASSERT(current_page->next_page()->is_valid());
794 // We do not add the top of page block for current page to the space's
795 // free list---the block may contain live objects so we cannot write
796 // bookkeeping information to it. Instead, we will recover top of page
797 // blocks when we move objects to their new locations.
798 //
799 // We do however write the allocation pointer to the page. The encoding
800 // of forwarding addresses is as an offset in terms of live bytes, so we
801 // need quick access to the allocation top of each page to decode
802 // forwarding addresses.
803 current_page->mc_relocation_top = mc_forwarding_info_.top;
804 SetAllocationInfo(&mc_forwarding_info_, current_page->next_page());
805 return AllocateLinearly(&mc_forwarding_info_, size_in_bytes);
806}
807
808
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000809bool PagedSpace::Expand(Page* last_page) {
810 ASSERT(max_capacity_ % Page::kObjectAreaSize == 0);
811 ASSERT(Capacity() % Page::kObjectAreaSize == 0);
812
813 if (Capacity() == max_capacity_) return false;
814
815 ASSERT(Capacity() < max_capacity_);
816 // Last page must be valid and its next page is invalid.
817 ASSERT(last_page->is_valid() && !last_page->next_page()->is_valid());
818
819 int available_pages = (max_capacity_ - Capacity()) / Page::kObjectAreaSize;
820 if (available_pages <= 0) return false;
821
822 int desired_pages = Min(available_pages, MemoryAllocator::kPagesPerChunk);
823 Page* p = MemoryAllocator::AllocatePages(desired_pages, &desired_pages, this);
824 if (!p->is_valid()) return false;
825
826 accounting_stats_.ExpandSpace(desired_pages * Page::kObjectAreaSize);
827 ASSERT(Capacity() <= max_capacity_);
828
829 MemoryAllocator::SetNextPage(last_page, p);
830
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000831 // Sequentially clear remembered set of new pages and and cache the
832 // new last page in the space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000833 while (p->is_valid()) {
834 p->ClearRSet();
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000835 last_page_ = p;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000836 p = p->next_page();
837 }
838
839 return true;
840}
841
842
843#ifdef DEBUG
844int PagedSpace::CountTotalPages() {
845 int count = 0;
846 for (Page* p = first_page_; p->is_valid(); p = p->next_page()) {
847 count++;
848 }
849 return count;
850}
851#endif
852
853
854void PagedSpace::Shrink() {
855 // Release half of free pages.
856 Page* top_page = AllocationTopPage();
857 ASSERT(top_page->is_valid());
858
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000859 // Count the number of pages we would like to free.
860 int pages_to_free = 0;
861 for (Page* p = top_page->next_page(); p->is_valid(); p = p->next_page()) {
862 pages_to_free++;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000863 }
864
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000865 // Free pages after top_page.
866 Page* p = MemoryAllocator::FreePages(top_page->next_page());
867 MemoryAllocator::SetNextPage(top_page, p);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000868
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000869 // Find out how many pages we failed to free and update last_page_.
870 // Please note pages can only be freed in whole chunks.
871 last_page_ = top_page;
872 for (Page* p = top_page->next_page(); p->is_valid(); p = p->next_page()) {
873 pages_to_free--;
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000874 last_page_ = p;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000875 }
876
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000877 accounting_stats_.ShrinkSpace(pages_to_free * Page::kObjectAreaSize);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000878 ASSERT(Capacity() == CountTotalPages() * Page::kObjectAreaSize);
879}
880
881
882bool PagedSpace::EnsureCapacity(int capacity) {
883 if (Capacity() >= capacity) return true;
884
885 // Start from the allocation top and loop to the last page in the space.
886 Page* last_page = AllocationTopPage();
887 Page* next_page = last_page->next_page();
888 while (next_page->is_valid()) {
889 last_page = MemoryAllocator::FindLastPageInSameChunk(next_page);
890 next_page = last_page->next_page();
891 }
892
893 // Expand the space until it has the required capacity or expansion fails.
894 do {
895 if (!Expand(last_page)) return false;
896 ASSERT(last_page->next_page()->is_valid());
897 last_page =
898 MemoryAllocator::FindLastPageInSameChunk(last_page->next_page());
899 } while (Capacity() < capacity);
900
901 return true;
902}
903
904
905#ifdef DEBUG
906void PagedSpace::Print() { }
907#endif
908
909
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +0000910#ifdef DEBUG
911// We do not assume that the PageIterator works, because it depends on the
912// invariants we are checking during verification.
913void PagedSpace::Verify(ObjectVisitor* visitor) {
914 // The allocation pointer should be valid, and it should be in a page in the
915 // space.
916 ASSERT(allocation_info_.VerifyPagedAllocation());
917 Page* top_page = Page::FromAllocationTop(allocation_info_.top);
918 ASSERT(MemoryAllocator::IsPageInSpace(top_page, this));
919
920 // Loop over all the pages.
921 bool above_allocation_top = false;
922 Page* current_page = first_page_;
923 while (current_page->is_valid()) {
924 if (above_allocation_top) {
925 // We don't care what's above the allocation top.
926 } else {
927 // Unless this is the last page in the space containing allocated
928 // objects, the allocation top should be at a constant offset from the
929 // object area end.
930 Address top = current_page->AllocationTop();
931 if (current_page == top_page) {
932 ASSERT(top == allocation_info_.top);
933 // The next page will be above the allocation top.
934 above_allocation_top = true;
935 } else {
936 ASSERT(top == current_page->ObjectAreaEnd() - page_extra_);
937 }
938
939 // It should be packed with objects from the bottom to the top.
940 Address current = current_page->ObjectAreaStart();
941 while (current < top) {
942 HeapObject* object = HeapObject::FromAddress(current);
943
944 // The first word should be a map, and we expect all map pointers to
945 // be in map space.
946 Map* map = object->map();
947 ASSERT(map->IsMap());
948 ASSERT(Heap::map_space()->Contains(map));
949
950 // Perform space-specific object verification.
951 VerifyObject(object);
952
953 // The object itself should look OK.
954 object->Verify();
955
956 // All the interior pointers should be contained in the heap and
957 // have their remembered set bits set if required as determined
958 // by the visitor.
959 int size = object->Size();
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000960 object->IterateBody(map->instance_type(), size, visitor);
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +0000961
962 current += size;
963 }
964
965 // The allocation pointer should not be in the middle of an object.
966 ASSERT(current == top);
967 }
968
969 current_page = current_page->next_page();
970 }
971}
972#endif
973
974
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000975// -----------------------------------------------------------------------------
976// NewSpace implementation
977
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000978
979bool NewSpace::Setup(Address start, int size) {
980 // Setup new space based on the preallocated memory block defined by
981 // start and size. The provided space is divided into two semi-spaces.
982 // To support fast containment testing in the new space, the size of
983 // this chunk must be a power of two and it must be aligned to its size.
984 int initial_semispace_capacity = Heap::InitialSemiSpaceSize();
ager@chromium.org3811b432009-10-28 14:53:37 +0000985 int maximum_semispace_capacity = Heap::MaxSemiSpaceSize();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000986
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000987 ASSERT(initial_semispace_capacity <= maximum_semispace_capacity);
988 ASSERT(IsPowerOf2(maximum_semispace_capacity));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000989
990 // Allocate and setup the histogram arrays if necessary.
991#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
992 allocated_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1);
993 promoted_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1);
994
995#define SET_NAME(name) allocated_histogram_[name].set_name(#name); \
996 promoted_histogram_[name].set_name(#name);
997 INSTANCE_TYPE_LIST(SET_NAME)
998#undef SET_NAME
999#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001000
ager@chromium.org3811b432009-10-28 14:53:37 +00001001 ASSERT(size == 2 * Heap::ReservedSemiSpaceSize());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001002 ASSERT(IsAddressAligned(start, size, 0));
1003
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001004 if (!to_space_.Setup(start,
1005 initial_semispace_capacity,
1006 maximum_semispace_capacity)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001007 return false;
1008 }
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001009 if (!from_space_.Setup(start + maximum_semispace_capacity,
1010 initial_semispace_capacity,
1011 maximum_semispace_capacity)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001012 return false;
1013 }
1014
1015 start_ = start;
1016 address_mask_ = ~(size - 1);
1017 object_mask_ = address_mask_ | kHeapObjectTag;
ager@chromium.org9085a012009-05-11 19:22:57 +00001018 object_expected_ = reinterpret_cast<uintptr_t>(start) | kHeapObjectTag;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001019
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001020 allocation_info_.top = to_space_.low();
1021 allocation_info_.limit = to_space_.high();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001022 mc_forwarding_info_.top = NULL;
1023 mc_forwarding_info_.limit = NULL;
1024
1025 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1026 return true;
1027}
1028
1029
1030void NewSpace::TearDown() {
1031#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
1032 if (allocated_histogram_) {
1033 DeleteArray(allocated_histogram_);
1034 allocated_histogram_ = NULL;
1035 }
1036 if (promoted_histogram_) {
1037 DeleteArray(promoted_histogram_);
1038 promoted_histogram_ = NULL;
1039 }
1040#endif
1041
1042 start_ = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001043 allocation_info_.top = NULL;
1044 allocation_info_.limit = NULL;
1045 mc_forwarding_info_.top = NULL;
1046 mc_forwarding_info_.limit = NULL;
1047
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001048 to_space_.TearDown();
1049 from_space_.TearDown();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001050}
1051
1052
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +00001053#ifdef ENABLE_HEAP_PROTECTION
1054
1055void NewSpace::Protect() {
1056 MemoryAllocator::Protect(ToSpaceLow(), Capacity());
1057 MemoryAllocator::Protect(FromSpaceLow(), Capacity());
1058}
1059
1060
1061void NewSpace::Unprotect() {
1062 MemoryAllocator::Unprotect(ToSpaceLow(), Capacity(),
1063 to_space_.executable());
1064 MemoryAllocator::Unprotect(FromSpaceLow(), Capacity(),
1065 from_space_.executable());
1066}
1067
1068#endif
1069
1070
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001071void NewSpace::Flip() {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001072 SemiSpace tmp = from_space_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001073 from_space_ = to_space_;
1074 to_space_ = tmp;
1075}
1076
1077
ager@chromium.orgab99eea2009-08-25 07:05:41 +00001078void NewSpace::Grow() {
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001079 ASSERT(Capacity() < MaximumCapacity());
ager@chromium.orgab99eea2009-08-25 07:05:41 +00001080 if (to_space_.Grow()) {
1081 // Only grow from space if we managed to grow to space.
1082 if (!from_space_.Grow()) {
1083 // If we managed to grow to space but couldn't grow from space,
1084 // attempt to shrink to space.
1085 if (!to_space_.ShrinkTo(from_space_.Capacity())) {
1086 // We are in an inconsistent state because we could not
1087 // commit/uncommit memory from new space.
1088 V8::FatalProcessOutOfMemory("Failed to grow new space.");
1089 }
1090 }
1091 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001092 allocation_info_.limit = to_space_.high();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001093 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00001094}
1095
1096
1097void NewSpace::Shrink() {
1098 int new_capacity = Max(InitialCapacity(), 2 * Size());
1099 int rounded_new_capacity = RoundUp(new_capacity, OS::AllocateAlignment());
1100 if (rounded_new_capacity < Capacity() &&
1101 to_space_.ShrinkTo(rounded_new_capacity)) {
1102 // Only shrink from space if we managed to shrink to space.
1103 if (!from_space_.ShrinkTo(rounded_new_capacity)) {
1104 // If we managed to shrink to space but couldn't shrink from
1105 // space, attempt to grow to space again.
1106 if (!to_space_.GrowTo(from_space_.Capacity())) {
1107 // We are in an inconsistent state because we could not
1108 // commit/uncommit memory from new space.
1109 V8::FatalProcessOutOfMemory("Failed to shrink new space.");
1110 }
1111 }
1112 }
1113 allocation_info_.limit = to_space_.high();
1114 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001115}
1116
1117
1118void NewSpace::ResetAllocationInfo() {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001119 allocation_info_.top = to_space_.low();
1120 allocation_info_.limit = to_space_.high();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001121 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1122}
1123
1124
1125void NewSpace::MCResetRelocationInfo() {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001126 mc_forwarding_info_.top = from_space_.low();
1127 mc_forwarding_info_.limit = from_space_.high();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001128 ASSERT_SEMISPACE_ALLOCATION_INFO(mc_forwarding_info_, from_space_);
1129}
1130
1131
1132void NewSpace::MCCommitRelocationInfo() {
1133 // Assumes that the spaces have been flipped so that mc_forwarding_info_ is
1134 // valid allocation info for the to space.
1135 allocation_info_.top = mc_forwarding_info_.top;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001136 allocation_info_.limit = to_space_.high();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001137 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1138}
1139
1140
1141#ifdef DEBUG
1142// We do not use the SemispaceIterator because verification doesn't assume
1143// that it works (it depends on the invariants we are checking).
1144void NewSpace::Verify() {
1145 // The allocation pointer should be in the space or at the very end.
1146 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1147
1148 // There should be objects packed in from the low address up to the
1149 // allocation pointer.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001150 Address current = to_space_.low();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001151 while (current < top()) {
1152 HeapObject* object = HeapObject::FromAddress(current);
1153
1154 // The first word should be a map, and we expect all map pointers to
1155 // be in map space.
1156 Map* map = object->map();
1157 ASSERT(map->IsMap());
1158 ASSERT(Heap::map_space()->Contains(map));
1159
1160 // The object should not be code or a map.
1161 ASSERT(!object->IsMap());
1162 ASSERT(!object->IsCode());
1163
1164 // The object itself should look OK.
1165 object->Verify();
1166
1167 // All the interior pointers should be contained in the heap.
1168 VerifyPointersVisitor visitor;
1169 int size = object->Size();
1170 object->IterateBody(map->instance_type(), size, &visitor);
1171
1172 current += size;
1173 }
1174
1175 // The allocation pointer should not be in the middle of an object.
1176 ASSERT(current == top());
1177}
1178#endif
1179
1180
ager@chromium.orgadd848f2009-08-13 12:44:13 +00001181bool SemiSpace::Commit() {
1182 ASSERT(!is_committed());
1183 if (!MemoryAllocator::CommitBlock(start_, capacity_, executable())) {
1184 return false;
1185 }
1186 committed_ = true;
1187 return true;
1188}
1189
1190
1191bool SemiSpace::Uncommit() {
1192 ASSERT(is_committed());
1193 if (!MemoryAllocator::UncommitBlock(start_, capacity_)) {
1194 return false;
1195 }
1196 committed_ = false;
1197 return true;
1198}
1199
1200
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001201// -----------------------------------------------------------------------------
1202// SemiSpace implementation
1203
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001204bool SemiSpace::Setup(Address start,
1205 int initial_capacity,
1206 int maximum_capacity) {
1207 // Creates a space in the young generation. The constructor does not
1208 // allocate memory from the OS. A SemiSpace is given a contiguous chunk of
1209 // memory of size 'capacity' when set up, and does not grow or shrink
1210 // otherwise. In the mark-compact collector, the memory region of the from
1211 // space is used as the marking stack. It requires contiguous memory
1212 // addresses.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00001213 initial_capacity_ = initial_capacity;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001214 capacity_ = initial_capacity;
1215 maximum_capacity_ = maximum_capacity;
ager@chromium.orgadd848f2009-08-13 12:44:13 +00001216 committed_ = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001217
1218 start_ = start;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001219 address_mask_ = ~(maximum_capacity - 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001220 object_mask_ = address_mask_ | kHeapObjectTag;
ager@chromium.org9085a012009-05-11 19:22:57 +00001221 object_expected_ = reinterpret_cast<uintptr_t>(start) | kHeapObjectTag;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001222 age_mark_ = start_;
ager@chromium.orgadd848f2009-08-13 12:44:13 +00001223
1224 return Commit();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001225}
1226
1227
1228void SemiSpace::TearDown() {
1229 start_ = NULL;
1230 capacity_ = 0;
1231}
1232
1233
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00001234bool SemiSpace::Grow() {
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00001235 // Double the semispace size but only up to maximum capacity.
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001236 int maximum_extra = maximum_capacity_ - capacity_;
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00001237 int extra = Min(RoundUp(capacity_, OS::AllocateAlignment()),
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001238 maximum_extra);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00001239 if (!MemoryAllocator::CommitBlock(high(), extra, executable())) {
kasper.lund7276f142008-07-30 08:49:36 +00001240 return false;
1241 }
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00001242 capacity_ += extra;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001243 return true;
1244}
1245
1246
ager@chromium.orgab99eea2009-08-25 07:05:41 +00001247bool SemiSpace::GrowTo(int new_capacity) {
1248 ASSERT(new_capacity <= maximum_capacity_);
1249 ASSERT(new_capacity > capacity_);
1250 size_t delta = new_capacity - capacity_;
1251 ASSERT(IsAligned(delta, OS::AllocateAlignment()));
1252 if (!MemoryAllocator::CommitBlock(high(), delta, executable())) {
1253 return false;
1254 }
1255 capacity_ = new_capacity;
1256 return true;
1257}
1258
1259
1260bool SemiSpace::ShrinkTo(int new_capacity) {
1261 ASSERT(new_capacity >= initial_capacity_);
1262 ASSERT(new_capacity < capacity_);
1263 size_t delta = capacity_ - new_capacity;
1264 ASSERT(IsAligned(delta, OS::AllocateAlignment()));
1265 if (!MemoryAllocator::UncommitBlock(high() - delta, delta)) {
1266 return false;
1267 }
1268 capacity_ = new_capacity;
1269 return true;
1270}
1271
1272
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001273#ifdef DEBUG
1274void SemiSpace::Print() { }
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001275
1276
1277void SemiSpace::Verify() { }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001278#endif
1279
1280
1281// -----------------------------------------------------------------------------
1282// SemiSpaceIterator implementation.
1283SemiSpaceIterator::SemiSpaceIterator(NewSpace* space) {
1284 Initialize(space, space->bottom(), space->top(), NULL);
1285}
1286
1287
1288SemiSpaceIterator::SemiSpaceIterator(NewSpace* space,
1289 HeapObjectCallback size_func) {
1290 Initialize(space, space->bottom(), space->top(), size_func);
1291}
1292
1293
1294SemiSpaceIterator::SemiSpaceIterator(NewSpace* space, Address start) {
1295 Initialize(space, start, space->top(), NULL);
1296}
1297
1298
1299void SemiSpaceIterator::Initialize(NewSpace* space, Address start,
1300 Address end,
1301 HeapObjectCallback size_func) {
1302 ASSERT(space->ToSpaceContains(start));
1303 ASSERT(space->ToSpaceLow() <= end
1304 && end <= space->ToSpaceHigh());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001305 space_ = &space->to_space_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001306 current_ = start;
1307 limit_ = end;
1308 size_func_ = size_func;
1309}
1310
1311
1312#ifdef DEBUG
1313// A static array of histogram info for each type.
1314static HistogramInfo heap_histograms[LAST_TYPE+1];
1315static JSObject::SpillInformation js_spill_information;
1316
1317// heap_histograms is shared, always clear it before using it.
1318static void ClearHistograms() {
1319 // We reset the name each time, though it hasn't changed.
1320#define DEF_TYPE_NAME(name) heap_histograms[name].set_name(#name);
1321 INSTANCE_TYPE_LIST(DEF_TYPE_NAME)
1322#undef DEF_TYPE_NAME
1323
1324#define CLEAR_HISTOGRAM(name) heap_histograms[name].clear();
1325 INSTANCE_TYPE_LIST(CLEAR_HISTOGRAM)
1326#undef CLEAR_HISTOGRAM
1327
1328 js_spill_information.Clear();
1329}
1330
1331
1332static int code_kind_statistics[Code::NUMBER_OF_KINDS];
1333
1334
1335static void ClearCodeKindStatistics() {
1336 for (int i = 0; i < Code::NUMBER_OF_KINDS; i++) {
1337 code_kind_statistics[i] = 0;
1338 }
1339}
1340
1341
1342static void ReportCodeKindStatistics() {
1343 const char* table[Code::NUMBER_OF_KINDS];
1344
1345#define CASE(name) \
1346 case Code::name: table[Code::name] = #name; \
1347 break
1348
1349 for (int i = 0; i < Code::NUMBER_OF_KINDS; i++) {
1350 switch (static_cast<Code::Kind>(i)) {
1351 CASE(FUNCTION);
1352 CASE(STUB);
1353 CASE(BUILTIN);
1354 CASE(LOAD_IC);
1355 CASE(KEYED_LOAD_IC);
1356 CASE(STORE_IC);
1357 CASE(KEYED_STORE_IC);
1358 CASE(CALL_IC);
1359 }
1360 }
1361
1362#undef CASE
1363
1364 PrintF("\n Code kind histograms: \n");
1365 for (int i = 0; i < Code::NUMBER_OF_KINDS; i++) {
1366 if (code_kind_statistics[i] > 0) {
1367 PrintF(" %-20s: %10d bytes\n", table[i], code_kind_statistics[i]);
1368 }
1369 }
1370 PrintF("\n");
1371}
1372
1373
1374static int CollectHistogramInfo(HeapObject* obj) {
1375 InstanceType type = obj->map()->instance_type();
1376 ASSERT(0 <= type && type <= LAST_TYPE);
1377 ASSERT(heap_histograms[type].name() != NULL);
1378 heap_histograms[type].increment_number(1);
1379 heap_histograms[type].increment_bytes(obj->Size());
1380
1381 if (FLAG_collect_heap_spill_statistics && obj->IsJSObject()) {
1382 JSObject::cast(obj)->IncrementSpillStatistics(&js_spill_information);
1383 }
1384
1385 return obj->Size();
1386}
1387
1388
1389static void ReportHistogram(bool print_spill) {
1390 PrintF("\n Object Histogram:\n");
1391 for (int i = 0; i <= LAST_TYPE; i++) {
1392 if (heap_histograms[i].number() > 0) {
1393 PrintF(" %-33s%10d (%10d bytes)\n",
1394 heap_histograms[i].name(),
1395 heap_histograms[i].number(),
1396 heap_histograms[i].bytes());
1397 }
1398 }
1399 PrintF("\n");
1400
1401 // Summarize string types.
1402 int string_number = 0;
1403 int string_bytes = 0;
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001404#define INCREMENT(type, size, name, camel_name) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001405 string_number += heap_histograms[type].number(); \
1406 string_bytes += heap_histograms[type].bytes();
1407 STRING_TYPE_LIST(INCREMENT)
1408#undef INCREMENT
1409 if (string_number > 0) {
1410 PrintF(" %-33s%10d (%10d bytes)\n\n", "STRING_TYPE", string_number,
1411 string_bytes);
1412 }
1413
1414 if (FLAG_collect_heap_spill_statistics && print_spill) {
1415 js_spill_information.Print();
1416 }
1417}
1418#endif // DEBUG
1419
1420
1421// Support for statistics gathering for --heap-stats and --log-gc.
1422#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
1423void NewSpace::ClearHistograms() {
1424 for (int i = 0; i <= LAST_TYPE; i++) {
1425 allocated_histogram_[i].clear();
1426 promoted_histogram_[i].clear();
1427 }
1428}
1429
1430// Because the copying collector does not touch garbage objects, we iterate
1431// the new space before a collection to get a histogram of allocated objects.
1432// This only happens (1) when compiled with DEBUG and the --heap-stats flag is
1433// set, or when compiled with ENABLE_LOGGING_AND_PROFILING and the --log-gc
1434// flag is set.
1435void NewSpace::CollectStatistics() {
1436 ClearHistograms();
1437 SemiSpaceIterator it(this);
1438 while (it.has_next()) RecordAllocation(it.next());
1439}
1440
1441
1442#ifdef ENABLE_LOGGING_AND_PROFILING
1443static void DoReportStatistics(HistogramInfo* info, const char* description) {
1444 LOG(HeapSampleBeginEvent("NewSpace", description));
1445 // Lump all the string types together.
1446 int string_number = 0;
1447 int string_bytes = 0;
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001448#define INCREMENT(type, size, name, camel_name) \
1449 string_number += info[type].number(); \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001450 string_bytes += info[type].bytes();
1451 STRING_TYPE_LIST(INCREMENT)
1452#undef INCREMENT
1453 if (string_number > 0) {
1454 LOG(HeapSampleItemEvent("STRING_TYPE", string_number, string_bytes));
1455 }
1456
1457 // Then do the other types.
1458 for (int i = FIRST_NONSTRING_TYPE; i <= LAST_TYPE; ++i) {
1459 if (info[i].number() > 0) {
1460 LOG(HeapSampleItemEvent(info[i].name(), info[i].number(),
1461 info[i].bytes()));
1462 }
1463 }
1464 LOG(HeapSampleEndEvent("NewSpace", description));
1465}
1466#endif // ENABLE_LOGGING_AND_PROFILING
1467
1468
1469void NewSpace::ReportStatistics() {
1470#ifdef DEBUG
1471 if (FLAG_heap_stats) {
1472 float pct = static_cast<float>(Available()) / Capacity();
1473 PrintF(" capacity: %d, available: %d, %%%d\n",
1474 Capacity(), Available(), static_cast<int>(pct*100));
1475 PrintF("\n Object Histogram:\n");
1476 for (int i = 0; i <= LAST_TYPE; i++) {
1477 if (allocated_histogram_[i].number() > 0) {
1478 PrintF(" %-33s%10d (%10d bytes)\n",
1479 allocated_histogram_[i].name(),
1480 allocated_histogram_[i].number(),
1481 allocated_histogram_[i].bytes());
1482 }
1483 }
1484 PrintF("\n");
1485 }
1486#endif // DEBUG
1487
1488#ifdef ENABLE_LOGGING_AND_PROFILING
1489 if (FLAG_log_gc) {
1490 DoReportStatistics(allocated_histogram_, "allocated");
1491 DoReportStatistics(promoted_histogram_, "promoted");
1492 }
1493#endif // ENABLE_LOGGING_AND_PROFILING
1494}
1495
1496
1497void NewSpace::RecordAllocation(HeapObject* obj) {
1498 InstanceType type = obj->map()->instance_type();
1499 ASSERT(0 <= type && type <= LAST_TYPE);
1500 allocated_histogram_[type].increment_number(1);
1501 allocated_histogram_[type].increment_bytes(obj->Size());
1502}
1503
1504
1505void NewSpace::RecordPromotion(HeapObject* obj) {
1506 InstanceType type = obj->map()->instance_type();
1507 ASSERT(0 <= type && type <= LAST_TYPE);
1508 promoted_histogram_[type].increment_number(1);
1509 promoted_histogram_[type].increment_bytes(obj->Size());
1510}
1511#endif // defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
1512
1513
1514// -----------------------------------------------------------------------------
1515// Free lists for old object spaces implementation
1516
1517void FreeListNode::set_size(int size_in_bytes) {
1518 ASSERT(size_in_bytes > 0);
1519 ASSERT(IsAligned(size_in_bytes, kPointerSize));
1520
1521 // We write a map and possibly size information to the block. If the block
1522 // is big enough to be a ByteArray with at least one extra word (the next
1523 // pointer), we set its map to be the byte array map and its size to an
1524 // appropriate array length for the desired size from HeapObject::Size().
1525 // If the block is too small (eg, one or two words), to hold both a size
1526 // field and a next pointer, we give it a filler map that gives it the
1527 // correct size.
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001528 if (size_in_bytes > ByteArray::kAlignedSize) {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001529 set_map(Heap::raw_unchecked_byte_array_map());
ager@chromium.org3811b432009-10-28 14:53:37 +00001530 // Can't use ByteArray::cast because it fails during deserialization.
1531 ByteArray* this_as_byte_array = reinterpret_cast<ByteArray*>(this);
1532 this_as_byte_array->set_length(ByteArray::LengthFor(size_in_bytes));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001533 } else if (size_in_bytes == kPointerSize) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001534 set_map(Heap::raw_unchecked_one_pointer_filler_map());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001535 } else if (size_in_bytes == 2 * kPointerSize) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001536 set_map(Heap::raw_unchecked_two_pointer_filler_map());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001537 } else {
1538 UNREACHABLE();
1539 }
ager@chromium.org3811b432009-10-28 14:53:37 +00001540 // We would like to ASSERT(Size() == size_in_bytes) but this would fail during
1541 // deserialization because the byte array map is not done yet.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001542}
1543
1544
1545Address FreeListNode::next() {
ager@chromium.org3811b432009-10-28 14:53:37 +00001546 ASSERT(IsFreeListNode(this));
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001547 if (map() == Heap::raw_unchecked_byte_array_map()) {
1548 ASSERT(Size() >= kNextOffset + kPointerSize);
1549 return Memory::Address_at(address() + kNextOffset);
1550 } else {
1551 return Memory::Address_at(address() + kPointerSize);
1552 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001553}
1554
1555
1556void FreeListNode::set_next(Address next) {
ager@chromium.org3811b432009-10-28 14:53:37 +00001557 ASSERT(IsFreeListNode(this));
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001558 if (map() == Heap::raw_unchecked_byte_array_map()) {
1559 ASSERT(Size() >= kNextOffset + kPointerSize);
1560 Memory::Address_at(address() + kNextOffset) = next;
1561 } else {
1562 Memory::Address_at(address() + kPointerSize) = next;
1563 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001564}
1565
1566
1567OldSpaceFreeList::OldSpaceFreeList(AllocationSpace owner) : owner_(owner) {
1568 Reset();
1569}
1570
1571
1572void OldSpaceFreeList::Reset() {
1573 available_ = 0;
1574 for (int i = 0; i < kFreeListsLength; i++) {
1575 free_[i].head_node_ = NULL;
1576 }
1577 needs_rebuild_ = false;
1578 finger_ = kHead;
1579 free_[kHead].next_size_ = kEnd;
1580}
1581
1582
1583void OldSpaceFreeList::RebuildSizeList() {
1584 ASSERT(needs_rebuild_);
1585 int cur = kHead;
1586 for (int i = cur + 1; i < kFreeListsLength; i++) {
1587 if (free_[i].head_node_ != NULL) {
1588 free_[cur].next_size_ = i;
1589 cur = i;
1590 }
1591 }
1592 free_[cur].next_size_ = kEnd;
1593 needs_rebuild_ = false;
1594}
1595
1596
1597int OldSpaceFreeList::Free(Address start, int size_in_bytes) {
1598#ifdef DEBUG
1599 for (int i = 0; i < size_in_bytes; i += kPointerSize) {
1600 Memory::Address_at(start + i) = kZapValue;
1601 }
1602#endif
1603 FreeListNode* node = FreeListNode::FromAddress(start);
1604 node->set_size(size_in_bytes);
1605
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001606 // We don't use the freelists in compacting mode. This makes it more like a
1607 // GC that only has mark-sweep-compact and doesn't have a mark-sweep
1608 // collector.
1609 if (FLAG_always_compact) {
1610 return size_in_bytes;
1611 }
1612
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001613 // Early return to drop too-small blocks on the floor (one or two word
1614 // blocks cannot hold a map pointer, a size field, and a pointer to the
1615 // next block in the free list).
1616 if (size_in_bytes < kMinBlockSize) {
1617 return size_in_bytes;
1618 }
1619
1620 // Insert other blocks at the head of an exact free list.
1621 int index = size_in_bytes >> kPointerSizeLog2;
1622 node->set_next(free_[index].head_node_);
1623 free_[index].head_node_ = node->address();
1624 available_ += size_in_bytes;
1625 needs_rebuild_ = true;
1626 return 0;
1627}
1628
1629
1630Object* OldSpaceFreeList::Allocate(int size_in_bytes, int* wasted_bytes) {
1631 ASSERT(0 < size_in_bytes);
1632 ASSERT(size_in_bytes <= kMaxBlockSize);
1633 ASSERT(IsAligned(size_in_bytes, kPointerSize));
1634
1635 if (needs_rebuild_) RebuildSizeList();
1636 int index = size_in_bytes >> kPointerSizeLog2;
1637 // Check for a perfect fit.
1638 if (free_[index].head_node_ != NULL) {
1639 FreeListNode* node = FreeListNode::FromAddress(free_[index].head_node_);
1640 // If this was the last block of its size, remove the size.
1641 if ((free_[index].head_node_ = node->next()) == NULL) RemoveSize(index);
1642 available_ -= size_in_bytes;
1643 *wasted_bytes = 0;
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001644 ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001645 return node;
1646 }
1647 // Search the size list for the best fit.
1648 int prev = finger_ < index ? finger_ : kHead;
1649 int cur = FindSize(index, &prev);
1650 ASSERT(index < cur);
1651 if (cur == kEnd) {
1652 // No large enough size in list.
1653 *wasted_bytes = 0;
1654 return Failure::RetryAfterGC(size_in_bytes, owner_);
1655 }
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001656 ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001657 int rem = cur - index;
1658 int rem_bytes = rem << kPointerSizeLog2;
1659 FreeListNode* cur_node = FreeListNode::FromAddress(free_[cur].head_node_);
kasper.lund7276f142008-07-30 08:49:36 +00001660 ASSERT(cur_node->Size() == (cur << kPointerSizeLog2));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001661 FreeListNode* rem_node = FreeListNode::FromAddress(free_[cur].head_node_ +
1662 size_in_bytes);
1663 // Distinguish the cases prev < rem < cur and rem <= prev < cur
1664 // to avoid many redundant tests and calls to Insert/RemoveSize.
1665 if (prev < rem) {
1666 // Simple case: insert rem between prev and cur.
1667 finger_ = prev;
1668 free_[prev].next_size_ = rem;
1669 // If this was the last block of size cur, remove the size.
1670 if ((free_[cur].head_node_ = cur_node->next()) == NULL) {
1671 free_[rem].next_size_ = free_[cur].next_size_;
1672 } else {
1673 free_[rem].next_size_ = cur;
1674 }
1675 // Add the remainder block.
1676 rem_node->set_size(rem_bytes);
1677 rem_node->set_next(free_[rem].head_node_);
1678 free_[rem].head_node_ = rem_node->address();
1679 } else {
1680 // If this was the last block of size cur, remove the size.
1681 if ((free_[cur].head_node_ = cur_node->next()) == NULL) {
1682 finger_ = prev;
1683 free_[prev].next_size_ = free_[cur].next_size_;
1684 }
1685 if (rem_bytes < kMinBlockSize) {
1686 // Too-small remainder is wasted.
1687 rem_node->set_size(rem_bytes);
1688 available_ -= size_in_bytes + rem_bytes;
1689 *wasted_bytes = rem_bytes;
1690 return cur_node;
1691 }
1692 // Add the remainder block and, if needed, insert its size.
1693 rem_node->set_size(rem_bytes);
1694 rem_node->set_next(free_[rem].head_node_);
1695 free_[rem].head_node_ = rem_node->address();
1696 if (rem_node->next() == NULL) InsertSize(rem);
1697 }
1698 available_ -= size_in_bytes;
1699 *wasted_bytes = 0;
1700 return cur_node;
1701}
1702
1703
kasper.lund7276f142008-07-30 08:49:36 +00001704#ifdef DEBUG
1705bool OldSpaceFreeList::Contains(FreeListNode* node) {
1706 for (int i = 0; i < kFreeListsLength; i++) {
1707 Address cur_addr = free_[i].head_node_;
1708 while (cur_addr != NULL) {
1709 FreeListNode* cur_node = FreeListNode::FromAddress(cur_addr);
1710 if (cur_node == node) return true;
1711 cur_addr = cur_node->next();
1712 }
1713 }
1714 return false;
1715}
1716#endif
1717
1718
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001719FixedSizeFreeList::FixedSizeFreeList(AllocationSpace owner, int object_size)
1720 : owner_(owner), object_size_(object_size) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001721 Reset();
1722}
1723
1724
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001725void FixedSizeFreeList::Reset() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001726 available_ = 0;
1727 head_ = NULL;
1728}
1729
1730
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001731void FixedSizeFreeList::Free(Address start) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001732#ifdef DEBUG
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001733 for (int i = 0; i < object_size_; i += kPointerSize) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001734 Memory::Address_at(start + i) = kZapValue;
1735 }
1736#endif
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001737 ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001738 FreeListNode* node = FreeListNode::FromAddress(start);
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001739 node->set_size(object_size_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001740 node->set_next(head_);
1741 head_ = node->address();
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001742 available_ += object_size_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001743}
1744
1745
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001746Object* FixedSizeFreeList::Allocate() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001747 if (head_ == NULL) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001748 return Failure::RetryAfterGC(object_size_, owner_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001749 }
1750
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001751 ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001752 FreeListNode* node = FreeListNode::FromAddress(head_);
1753 head_ = node->next();
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001754 available_ -= object_size_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001755 return node;
1756}
1757
1758
1759// -----------------------------------------------------------------------------
1760// OldSpace implementation
1761
1762void OldSpace::PrepareForMarkCompact(bool will_compact) {
1763 if (will_compact) {
1764 // Reset relocation info. During a compacting collection, everything in
1765 // the space is considered 'available' and we will rediscover live data
1766 // and waste during the collection.
1767 MCResetRelocationInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001768 ASSERT(Available() == Capacity());
1769 } else {
1770 // During a non-compacting collection, everything below the linear
1771 // allocation pointer is considered allocated (everything above is
1772 // available) and we will rediscover available and wasted bytes during
1773 // the collection.
1774 accounting_stats_.AllocateBytes(free_list_.available());
1775 accounting_stats_.FillWastedBytes(Waste());
1776 }
1777
kasper.lund7276f142008-07-30 08:49:36 +00001778 // Clear the free list before a full GC---it will be rebuilt afterward.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001779 free_list_.Reset();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001780}
1781
1782
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001783void OldSpace::MCCommitRelocationInfo() {
1784 // Update fast allocation info.
1785 allocation_info_.top = mc_forwarding_info_.top;
1786 allocation_info_.limit = mc_forwarding_info_.limit;
kasper.lund7276f142008-07-30 08:49:36 +00001787 ASSERT(allocation_info_.VerifyPagedAllocation());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001788
1789 // The space is compacted and we haven't yet built free lists or
1790 // wasted any space.
1791 ASSERT(Waste() == 0);
1792 ASSERT(AvailableFree() == 0);
1793
1794 // Build the free list for the space.
1795 int computed_size = 0;
1796 PageIterator it(this, PageIterator::PAGES_USED_BY_MC);
1797 while (it.has_next()) {
1798 Page* p = it.next();
1799 // Space below the relocation pointer is allocated.
1800 computed_size += p->mc_relocation_top - p->ObjectAreaStart();
1801 if (it.has_next()) {
1802 // Free the space at the top of the page. We cannot use
1803 // p->mc_relocation_top after the call to Free (because Free will clear
1804 // remembered set bits).
1805 int extra_size = p->ObjectAreaEnd() - p->mc_relocation_top;
1806 if (extra_size > 0) {
1807 int wasted_bytes = free_list_.Free(p->mc_relocation_top, extra_size);
1808 // The bytes we have just "freed" to add to the free list were
1809 // already accounted as available.
1810 accounting_stats_.WasteBytes(wasted_bytes);
1811 }
1812 }
1813 }
1814
1815 // Make sure the computed size - based on the used portion of the pages in
1816 // use - matches the size obtained while computing forwarding addresses.
1817 ASSERT(computed_size == Size());
1818}
1819
1820
kasper.lund7276f142008-07-30 08:49:36 +00001821// Slow case for normal allocation. Try in order: (1) allocate in the next
1822// page in the space, (2) allocate off the space's free list, (3) expand the
1823// space, (4) fail.
1824HeapObject* OldSpace::SlowAllocateRaw(int size_in_bytes) {
1825 // Linear allocation in this space has failed. If there is another page
1826 // in the space, move to that page and allocate there. This allocation
1827 // should succeed (size_in_bytes should not be greater than a page's
1828 // object area size).
1829 Page* current_page = TopPageOf(allocation_info_);
1830 if (current_page->next_page()->is_valid()) {
1831 return AllocateInNextPage(current_page, size_in_bytes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001832 }
kasper.lund7276f142008-07-30 08:49:36 +00001833
ager@chromium.org3811b432009-10-28 14:53:37 +00001834 // There is no next page in this space. Try free list allocation unless that
1835 // is currently forbidden.
1836 if (!Heap::linear_allocation()) {
1837 int wasted_bytes;
1838 Object* result = free_list_.Allocate(size_in_bytes, &wasted_bytes);
1839 accounting_stats_.WasteBytes(wasted_bytes);
1840 if (!result->IsFailure()) {
1841 accounting_stats_.AllocateBytes(size_in_bytes);
1842 return HeapObject::cast(result);
1843 }
kasper.lund7276f142008-07-30 08:49:36 +00001844 }
1845
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00001846 // Free list allocation failed and there is no next page. Fail if we have
1847 // hit the old generation size limit that should cause a garbage
1848 // collection.
1849 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) {
1850 return NULL;
1851 }
1852
1853 // Try to expand the space and allocate in the new next page.
kasper.lund7276f142008-07-30 08:49:36 +00001854 ASSERT(!current_page->next_page()->is_valid());
1855 if (Expand(current_page)) {
1856 return AllocateInNextPage(current_page, size_in_bytes);
1857 }
1858
1859 // Finally, fail.
1860 return NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001861}
1862
1863
kasper.lund7276f142008-07-30 08:49:36 +00001864// Add the block at the top of the page to the space's free list, set the
1865// allocation info to the next page (assumed to be one), and allocate
1866// linearly there.
1867HeapObject* OldSpace::AllocateInNextPage(Page* current_page,
1868 int size_in_bytes) {
1869 ASSERT(current_page->next_page()->is_valid());
1870 // Add the block at the top of this page to the free list.
1871 int free_size = current_page->ObjectAreaEnd() - allocation_info_.top;
1872 if (free_size > 0) {
1873 int wasted_bytes = free_list_.Free(allocation_info_.top, free_size);
1874 accounting_stats_.WasteBytes(wasted_bytes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001875 }
kasper.lund7276f142008-07-30 08:49:36 +00001876 SetAllocationInfo(&allocation_info_, current_page->next_page());
1877 return AllocateLinearly(&allocation_info_, size_in_bytes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001878}
1879
1880
1881#ifdef DEBUG
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001882struct CommentStatistic {
1883 const char* comment;
1884 int size;
1885 int count;
1886 void Clear() {
1887 comment = NULL;
1888 size = 0;
1889 count = 0;
1890 }
1891};
1892
1893
1894// must be small, since an iteration is used for lookup
1895const int kMaxComments = 64;
1896static CommentStatistic comments_statistics[kMaxComments+1];
1897
1898
1899void PagedSpace::ReportCodeStatistics() {
1900 ReportCodeKindStatistics();
1901 PrintF("Code comment statistics (\" [ comment-txt : size/ "
1902 "count (average)\"):\n");
1903 for (int i = 0; i <= kMaxComments; i++) {
1904 const CommentStatistic& cs = comments_statistics[i];
1905 if (cs.size > 0) {
1906 PrintF(" %-30s: %10d/%6d (%d)\n", cs.comment, cs.size, cs.count,
1907 cs.size/cs.count);
1908 }
1909 }
1910 PrintF("\n");
1911}
1912
1913
1914void PagedSpace::ResetCodeStatistics() {
1915 ClearCodeKindStatistics();
1916 for (int i = 0; i < kMaxComments; i++) comments_statistics[i].Clear();
1917 comments_statistics[kMaxComments].comment = "Unknown";
1918 comments_statistics[kMaxComments].size = 0;
1919 comments_statistics[kMaxComments].count = 0;
1920}
1921
1922
1923// Adds comment to 'comment_statistics' table. Performance OK sa long as
1924// 'kMaxComments' is small
1925static void EnterComment(const char* comment, int delta) {
1926 // Do not count empty comments
1927 if (delta <= 0) return;
1928 CommentStatistic* cs = &comments_statistics[kMaxComments];
1929 // Search for a free or matching entry in 'comments_statistics': 'cs'
1930 // points to result.
1931 for (int i = 0; i < kMaxComments; i++) {
1932 if (comments_statistics[i].comment == NULL) {
1933 cs = &comments_statistics[i];
1934 cs->comment = comment;
1935 break;
1936 } else if (strcmp(comments_statistics[i].comment, comment) == 0) {
1937 cs = &comments_statistics[i];
1938 break;
1939 }
1940 }
1941 // Update entry for 'comment'
1942 cs->size += delta;
1943 cs->count += 1;
1944}
1945
1946
1947// Call for each nested comment start (start marked with '[ xxx', end marked
1948// with ']'. RelocIterator 'it' must point to a comment reloc info.
1949static void CollectCommentStatistics(RelocIterator* it) {
1950 ASSERT(!it->done());
ager@chromium.org236ad962008-09-25 09:45:57 +00001951 ASSERT(it->rinfo()->rmode() == RelocInfo::COMMENT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001952 const char* tmp = reinterpret_cast<const char*>(it->rinfo()->data());
1953 if (tmp[0] != '[') {
1954 // Not a nested comment; skip
1955 return;
1956 }
1957
1958 // Search for end of nested comment or a new nested comment
1959 const char* const comment_txt =
1960 reinterpret_cast<const char*>(it->rinfo()->data());
1961 const byte* prev_pc = it->rinfo()->pc();
1962 int flat_delta = 0;
1963 it->next();
1964 while (true) {
1965 // All nested comments must be terminated properly, and therefore exit
1966 // from loop.
1967 ASSERT(!it->done());
ager@chromium.org236ad962008-09-25 09:45:57 +00001968 if (it->rinfo()->rmode() == RelocInfo::COMMENT) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001969 const char* const txt =
1970 reinterpret_cast<const char*>(it->rinfo()->data());
1971 flat_delta += it->rinfo()->pc() - prev_pc;
1972 if (txt[0] == ']') break; // End of nested comment
1973 // A new comment
1974 CollectCommentStatistics(it);
1975 // Skip code that was covered with previous comment
1976 prev_pc = it->rinfo()->pc();
1977 }
1978 it->next();
1979 }
1980 EnterComment(comment_txt, flat_delta);
1981}
1982
1983
1984// Collects code size statistics:
1985// - by code kind
1986// - by code comment
1987void PagedSpace::CollectCodeStatistics() {
1988 HeapObjectIterator obj_it(this);
1989 while (obj_it.has_next()) {
1990 HeapObject* obj = obj_it.next();
1991 if (obj->IsCode()) {
1992 Code* code = Code::cast(obj);
1993 code_kind_statistics[code->kind()] += code->Size();
1994 RelocIterator it(code);
1995 int delta = 0;
1996 const byte* prev_pc = code->instruction_start();
1997 while (!it.done()) {
ager@chromium.org236ad962008-09-25 09:45:57 +00001998 if (it.rinfo()->rmode() == RelocInfo::COMMENT) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001999 delta += it.rinfo()->pc() - prev_pc;
2000 CollectCommentStatistics(&it);
2001 prev_pc = it.rinfo()->pc();
2002 }
2003 it.next();
2004 }
2005
2006 ASSERT(code->instruction_start() <= prev_pc &&
2007 prev_pc <= code->relocation_start());
2008 delta += code->relocation_start() - prev_pc;
2009 EnterComment("NoComment", delta);
2010 }
2011 }
2012}
2013
2014
2015void OldSpace::ReportStatistics() {
2016 int pct = Available() * 100 / Capacity();
2017 PrintF(" capacity: %d, waste: %d, available: %d, %%%d\n",
2018 Capacity(), Waste(), Available(), pct);
2019
2020 // Report remembered set statistics.
2021 int rset_marked_pointers = 0;
2022 int rset_marked_arrays = 0;
2023 int rset_marked_array_elements = 0;
2024 int cross_gen_pointers = 0;
2025 int cross_gen_array_elements = 0;
2026
2027 PageIterator page_it(this, PageIterator::PAGES_IN_USE);
2028 while (page_it.has_next()) {
2029 Page* p = page_it.next();
2030
2031 for (Address rset_addr = p->RSetStart();
2032 rset_addr < p->RSetEnd();
2033 rset_addr += kIntSize) {
2034 int rset = Memory::int_at(rset_addr);
2035 if (rset != 0) {
2036 // Bits were set
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00002037 int intoff = rset_addr - p->address() - Page::kRSetOffset;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002038 int bitoff = 0;
2039 for (; bitoff < kBitsPerInt; ++bitoff) {
2040 if ((rset & (1 << bitoff)) != 0) {
2041 int bitpos = intoff*kBitsPerByte + bitoff;
2042 Address slot = p->OffsetToAddress(bitpos << kObjectAlignmentBits);
2043 Object** obj = reinterpret_cast<Object**>(slot);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002044 if (*obj == Heap::raw_unchecked_fixed_array_map()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002045 rset_marked_arrays++;
2046 FixedArray* fa = FixedArray::cast(HeapObject::FromAddress(slot));
2047
2048 rset_marked_array_elements += fa->length();
2049 // Manually inline FixedArray::IterateBody
2050 Address elm_start = slot + FixedArray::kHeaderSize;
2051 Address elm_stop = elm_start + fa->length() * kPointerSize;
2052 for (Address elm_addr = elm_start;
2053 elm_addr < elm_stop; elm_addr += kPointerSize) {
2054 // Filter non-heap-object pointers
2055 Object** elm_p = reinterpret_cast<Object**>(elm_addr);
2056 if (Heap::InNewSpace(*elm_p))
2057 cross_gen_array_elements++;
2058 }
2059 } else {
2060 rset_marked_pointers++;
2061 if (Heap::InNewSpace(*obj))
2062 cross_gen_pointers++;
2063 }
2064 }
2065 }
2066 }
2067 }
2068 }
2069
2070 pct = rset_marked_pointers == 0 ?
2071 0 : cross_gen_pointers * 100 / rset_marked_pointers;
2072 PrintF(" rset-marked pointers %d, to-new-space %d (%%%d)\n",
2073 rset_marked_pointers, cross_gen_pointers, pct);
2074 PrintF(" rset_marked arrays %d, ", rset_marked_arrays);
2075 PrintF(" elements %d, ", rset_marked_array_elements);
2076 pct = rset_marked_array_elements == 0 ? 0
2077 : cross_gen_array_elements * 100 / rset_marked_array_elements;
2078 PrintF(" pointers to new space %d (%%%d)\n", cross_gen_array_elements, pct);
2079 PrintF(" total rset-marked bits %d\n",
2080 (rset_marked_pointers + rset_marked_arrays));
2081 pct = (rset_marked_pointers + rset_marked_array_elements) == 0 ? 0
2082 : (cross_gen_pointers + cross_gen_array_elements) * 100 /
2083 (rset_marked_pointers + rset_marked_array_elements);
2084 PrintF(" total rset pointers %d, true cross generation ones %d (%%%d)\n",
2085 (rset_marked_pointers + rset_marked_array_elements),
2086 (cross_gen_pointers + cross_gen_array_elements),
2087 pct);
2088
2089 ClearHistograms();
2090 HeapObjectIterator obj_it(this);
2091 while (obj_it.has_next()) { CollectHistogramInfo(obj_it.next()); }
2092 ReportHistogram(true);
2093}
2094
2095
2096// Dump the range of remembered set words between [start, end) corresponding
2097// to the pointers starting at object_p. The allocation_top is an object
2098// pointer which should not be read past. This is important for large object
2099// pages, where some bits in the remembered set range do not correspond to
2100// allocated addresses.
2101static void PrintRSetRange(Address start, Address end, Object** object_p,
2102 Address allocation_top) {
2103 Address rset_address = start;
2104
2105 // If the range starts on on odd numbered word (eg, for large object extra
2106 // remembered set ranges), print some spaces.
ager@chromium.org9085a012009-05-11 19:22:57 +00002107 if ((reinterpret_cast<uintptr_t>(start) / kIntSize) % 2 == 1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002108 PrintF(" ");
2109 }
2110
2111 // Loop over all the words in the range.
2112 while (rset_address < end) {
2113 uint32_t rset_word = Memory::uint32_at(rset_address);
2114 int bit_position = 0;
2115
2116 // Loop over all the bits in the word.
2117 while (bit_position < kBitsPerInt) {
2118 if (object_p == reinterpret_cast<Object**>(allocation_top)) {
2119 // Print a bar at the allocation pointer.
2120 PrintF("|");
2121 } else if (object_p > reinterpret_cast<Object**>(allocation_top)) {
2122 // Do not dereference object_p past the allocation pointer.
2123 PrintF("#");
2124 } else if ((rset_word & (1 << bit_position)) == 0) {
2125 // Print a dot for zero bits.
2126 PrintF(".");
2127 } else if (Heap::InNewSpace(*object_p)) {
2128 // Print an X for one bits for pointers to new space.
2129 PrintF("X");
2130 } else {
2131 // Print a circle for one bits for pointers to old space.
2132 PrintF("o");
2133 }
2134
2135 // Print a space after every 8th bit except the last.
2136 if (bit_position % 8 == 7 && bit_position != (kBitsPerInt - 1)) {
2137 PrintF(" ");
2138 }
2139
2140 // Advance to next bit.
2141 bit_position++;
2142 object_p++;
2143 }
2144
2145 // Print a newline after every odd numbered word, otherwise a space.
ager@chromium.org9085a012009-05-11 19:22:57 +00002146 if ((reinterpret_cast<uintptr_t>(rset_address) / kIntSize) % 2 == 1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002147 PrintF("\n");
2148 } else {
2149 PrintF(" ");
2150 }
2151
2152 // Advance to next remembered set word.
2153 rset_address += kIntSize;
2154 }
2155}
2156
2157
2158void PagedSpace::DoPrintRSet(const char* space_name) {
2159 PageIterator it(this, PageIterator::PAGES_IN_USE);
2160 while (it.has_next()) {
2161 Page* p = it.next();
2162 PrintF("%s page 0x%x:\n", space_name, p);
2163 PrintRSetRange(p->RSetStart(), p->RSetEnd(),
2164 reinterpret_cast<Object**>(p->ObjectAreaStart()),
2165 p->AllocationTop());
2166 PrintF("\n");
2167 }
2168}
2169
2170
2171void OldSpace::PrintRSet() { DoPrintRSet("old"); }
2172#endif
2173
2174// -----------------------------------------------------------------------------
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002175// FixedSpace implementation
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002176
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002177void FixedSpace::PrepareForMarkCompact(bool will_compact) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002178 if (will_compact) {
2179 // Reset relocation info.
2180 MCResetRelocationInfo();
2181
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002182 // During a compacting collection, everything in the space is considered
2183 // 'available' (set by the call to MCResetRelocationInfo) and we will
2184 // rediscover live and wasted bytes during the collection.
2185 ASSERT(Available() == Capacity());
2186 } else {
2187 // During a non-compacting collection, everything below the linear
2188 // allocation pointer except wasted top-of-page blocks is considered
2189 // allocated and we will rediscover available bytes during the
2190 // collection.
2191 accounting_stats_.AllocateBytes(free_list_.available());
2192 }
2193
kasper.lund7276f142008-07-30 08:49:36 +00002194 // Clear the free list before a full GC---it will be rebuilt afterward.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002195 free_list_.Reset();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002196}
2197
2198
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002199void FixedSpace::MCCommitRelocationInfo() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002200 // Update fast allocation info.
2201 allocation_info_.top = mc_forwarding_info_.top;
2202 allocation_info_.limit = mc_forwarding_info_.limit;
kasper.lund7276f142008-07-30 08:49:36 +00002203 ASSERT(allocation_info_.VerifyPagedAllocation());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002204
2205 // The space is compacted and we haven't yet wasted any space.
2206 ASSERT(Waste() == 0);
2207
2208 // Update allocation_top of each page in use and compute waste.
2209 int computed_size = 0;
2210 PageIterator it(this, PageIterator::PAGES_USED_BY_MC);
2211 while (it.has_next()) {
2212 Page* page = it.next();
2213 Address page_top = page->AllocationTop();
2214 computed_size += page_top - page->ObjectAreaStart();
2215 if (it.has_next()) {
2216 accounting_stats_.WasteBytes(page->ObjectAreaEnd() - page_top);
2217 }
2218 }
2219
2220 // Make sure the computed size - based on the used portion of the
2221 // pages in use - matches the size we adjust during allocation.
2222 ASSERT(computed_size == Size());
2223}
2224
2225
kasper.lund7276f142008-07-30 08:49:36 +00002226// Slow case for normal allocation. Try in order: (1) allocate in the next
2227// page in the space, (2) allocate off the space's free list, (3) expand the
2228// space, (4) fail.
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002229HeapObject* FixedSpace::SlowAllocateRaw(int size_in_bytes) {
2230 ASSERT_EQ(object_size_in_bytes_, size_in_bytes);
kasper.lund7276f142008-07-30 08:49:36 +00002231 // Linear allocation in this space has failed. If there is another page
2232 // in the space, move to that page and allocate there. This allocation
2233 // should succeed.
2234 Page* current_page = TopPageOf(allocation_info_);
2235 if (current_page->next_page()->is_valid()) {
2236 return AllocateInNextPage(current_page, size_in_bytes);
2237 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002238
ager@chromium.org3811b432009-10-28 14:53:37 +00002239 // There is no next page in this space. Try free list allocation unless
2240 // that is currently forbidden. The fixed space free list implicitly assumes
2241 // that all free blocks are of the fixed size.
2242 if (!Heap::linear_allocation()) {
kasper.lund7276f142008-07-30 08:49:36 +00002243 Object* result = free_list_.Allocate();
2244 if (!result->IsFailure()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002245 accounting_stats_.AllocateBytes(size_in_bytes);
kasper.lund7276f142008-07-30 08:49:36 +00002246 return HeapObject::cast(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002247 }
2248 }
kasper.lund7276f142008-07-30 08:49:36 +00002249
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00002250 // Free list allocation failed and there is no next page. Fail if we have
2251 // hit the old generation size limit that should cause a garbage
2252 // collection.
2253 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) {
2254 return NULL;
2255 }
2256
2257 // Try to expand the space and allocate in the new next page.
kasper.lund7276f142008-07-30 08:49:36 +00002258 ASSERT(!current_page->next_page()->is_valid());
2259 if (Expand(current_page)) {
2260 return AllocateInNextPage(current_page, size_in_bytes);
2261 }
2262
2263 // Finally, fail.
2264 return NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002265}
2266
2267
kasper.lund7276f142008-07-30 08:49:36 +00002268// Move to the next page (there is assumed to be one) and allocate there.
2269// The top of page block is always wasted, because it is too small to hold a
2270// map.
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002271HeapObject* FixedSpace::AllocateInNextPage(Page* current_page,
2272 int size_in_bytes) {
kasper.lund7276f142008-07-30 08:49:36 +00002273 ASSERT(current_page->next_page()->is_valid());
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002274 ASSERT(current_page->ObjectAreaEnd() - allocation_info_.top == page_extra_);
2275 ASSERT_EQ(object_size_in_bytes_, size_in_bytes);
2276 accounting_stats_.WasteBytes(page_extra_);
kasper.lund7276f142008-07-30 08:49:36 +00002277 SetAllocationInfo(&allocation_info_, current_page->next_page());
2278 return AllocateLinearly(&allocation_info_, size_in_bytes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002279}
2280
2281
2282#ifdef DEBUG
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002283void FixedSpace::ReportStatistics() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002284 int pct = Available() * 100 / Capacity();
2285 PrintF(" capacity: %d, waste: %d, available: %d, %%%d\n",
2286 Capacity(), Waste(), Available(), pct);
2287
2288 // Report remembered set statistics.
2289 int rset_marked_pointers = 0;
2290 int cross_gen_pointers = 0;
2291
2292 PageIterator page_it(this, PageIterator::PAGES_IN_USE);
2293 while (page_it.has_next()) {
2294 Page* p = page_it.next();
2295
2296 for (Address rset_addr = p->RSetStart();
2297 rset_addr < p->RSetEnd();
2298 rset_addr += kIntSize) {
2299 int rset = Memory::int_at(rset_addr);
2300 if (rset != 0) {
2301 // Bits were set
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00002302 int intoff = rset_addr - p->address() - Page::kRSetOffset;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002303 int bitoff = 0;
2304 for (; bitoff < kBitsPerInt; ++bitoff) {
2305 if ((rset & (1 << bitoff)) != 0) {
2306 int bitpos = intoff*kBitsPerByte + bitoff;
2307 Address slot = p->OffsetToAddress(bitpos << kObjectAlignmentBits);
2308 Object** obj = reinterpret_cast<Object**>(slot);
2309 rset_marked_pointers++;
2310 if (Heap::InNewSpace(*obj))
2311 cross_gen_pointers++;
2312 }
2313 }
2314 }
2315 }
2316 }
2317
2318 pct = rset_marked_pointers == 0 ?
2319 0 : cross_gen_pointers * 100 / rset_marked_pointers;
2320 PrintF(" rset-marked pointers %d, to-new-space %d (%%%d)\n",
2321 rset_marked_pointers, cross_gen_pointers, pct);
2322
2323 ClearHistograms();
2324 HeapObjectIterator obj_it(this);
2325 while (obj_it.has_next()) { CollectHistogramInfo(obj_it.next()); }
2326 ReportHistogram(false);
2327}
2328
2329
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002330void FixedSpace::PrintRSet() { DoPrintRSet(name_); }
2331#endif
2332
2333
2334// -----------------------------------------------------------------------------
2335// MapSpace implementation
2336
2337void MapSpace::PrepareForMarkCompact(bool will_compact) {
2338 // Call prepare of the super class.
2339 FixedSpace::PrepareForMarkCompact(will_compact);
2340
2341 if (will_compact) {
2342 // Initialize map index entry.
2343 int page_count = 0;
2344 PageIterator it(this, PageIterator::ALL_PAGES);
2345 while (it.has_next()) {
2346 ASSERT_MAP_PAGE_INDEX(page_count);
2347
2348 Page* p = it.next();
2349 ASSERT(p->mc_page_index == page_count);
2350
2351 page_addresses_[page_count++] = p->address();
2352 }
2353 }
2354}
2355
2356
2357#ifdef DEBUG
2358void MapSpace::VerifyObject(HeapObject* object) {
2359 // The object should be a map or a free-list node.
2360 ASSERT(object->IsMap() || object->IsByteArray());
2361}
2362#endif
2363
2364
2365// -----------------------------------------------------------------------------
2366// GlobalPropertyCellSpace implementation
2367
2368#ifdef DEBUG
2369void CellSpace::VerifyObject(HeapObject* object) {
2370 // The object should be a global object property cell or a free-list node.
2371 ASSERT(object->IsJSGlobalPropertyCell() ||
2372 object->map() == Heap::two_pointer_filler_map());
2373}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002374#endif
2375
2376
2377// -----------------------------------------------------------------------------
2378// LargeObjectIterator
2379
2380LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space) {
2381 current_ = space->first_chunk_;
2382 size_func_ = NULL;
2383}
2384
2385
2386LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space,
2387 HeapObjectCallback size_func) {
2388 current_ = space->first_chunk_;
2389 size_func_ = size_func;
2390}
2391
2392
2393HeapObject* LargeObjectIterator::next() {
2394 ASSERT(has_next());
2395 HeapObject* object = current_->GetObject();
2396 current_ = current_->next();
2397 return object;
2398}
2399
2400
2401// -----------------------------------------------------------------------------
2402// LargeObjectChunk
2403
2404LargeObjectChunk* LargeObjectChunk::New(int size_in_bytes,
kasper.lund7276f142008-07-30 08:49:36 +00002405 size_t* chunk_size,
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002406 Executability executable) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002407 size_t requested = ChunkSizeFor(size_in_bytes);
kasper.lund7276f142008-07-30 08:49:36 +00002408 void* mem = MemoryAllocator::AllocateRawMemory(requested,
2409 chunk_size,
2410 executable);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002411 if (mem == NULL) return NULL;
2412 LOG(NewEvent("LargeObjectChunk", mem, *chunk_size));
2413 if (*chunk_size < requested) {
2414 MemoryAllocator::FreeRawMemory(mem, *chunk_size);
2415 LOG(DeleteEvent("LargeObjectChunk", mem));
2416 return NULL;
2417 }
2418 return reinterpret_cast<LargeObjectChunk*>(mem);
2419}
2420
2421
2422int LargeObjectChunk::ChunkSizeFor(int size_in_bytes) {
2423 int os_alignment = OS::AllocateAlignment();
2424 if (os_alignment < Page::kPageSize)
2425 size_in_bytes += (Page::kPageSize - os_alignment);
2426 return size_in_bytes + Page::kObjectStartOffset;
2427}
2428
2429// -----------------------------------------------------------------------------
2430// LargeObjectSpace
2431
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002432LargeObjectSpace::LargeObjectSpace(AllocationSpace id)
2433 : Space(id, NOT_EXECUTABLE), // Managed on a per-allocation basis
kasper.lund7276f142008-07-30 08:49:36 +00002434 first_chunk_(NULL),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002435 size_(0),
2436 page_count_(0) {}
2437
2438
2439bool LargeObjectSpace::Setup() {
2440 first_chunk_ = NULL;
2441 size_ = 0;
2442 page_count_ = 0;
2443 return true;
2444}
2445
2446
2447void LargeObjectSpace::TearDown() {
2448 while (first_chunk_ != NULL) {
2449 LargeObjectChunk* chunk = first_chunk_;
2450 first_chunk_ = first_chunk_->next();
2451 LOG(DeleteEvent("LargeObjectChunk", chunk->address()));
2452 MemoryAllocator::FreeRawMemory(chunk->address(), chunk->size());
2453 }
2454
2455 size_ = 0;
2456 page_count_ = 0;
2457}
2458
2459
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +00002460#ifdef ENABLE_HEAP_PROTECTION
2461
2462void LargeObjectSpace::Protect() {
2463 LargeObjectChunk* chunk = first_chunk_;
2464 while (chunk != NULL) {
2465 MemoryAllocator::Protect(chunk->address(), chunk->size());
2466 chunk = chunk->next();
2467 }
2468}
2469
2470
2471void LargeObjectSpace::Unprotect() {
2472 LargeObjectChunk* chunk = first_chunk_;
2473 while (chunk != NULL) {
2474 bool is_code = chunk->GetObject()->IsCode();
2475 MemoryAllocator::Unprotect(chunk->address(), chunk->size(),
2476 is_code ? EXECUTABLE : NOT_EXECUTABLE);
2477 chunk = chunk->next();
2478 }
2479}
2480
2481#endif
2482
2483
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002484Object* LargeObjectSpace::AllocateRawInternal(int requested_size,
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002485 int object_size,
2486 Executability executable) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002487 ASSERT(0 < object_size && object_size <= requested_size);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00002488
2489 // Check if we want to force a GC before growing the old space further.
2490 // If so, fail the allocation.
2491 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) {
2492 return Failure::RetryAfterGC(requested_size, identity());
2493 }
2494
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002495 size_t chunk_size;
2496 LargeObjectChunk* chunk =
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002497 LargeObjectChunk::New(requested_size, &chunk_size, executable);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002498 if (chunk == NULL) {
kasper.lund7276f142008-07-30 08:49:36 +00002499 return Failure::RetryAfterGC(requested_size, identity());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002500 }
2501
2502 size_ += chunk_size;
2503 page_count_++;
2504 chunk->set_next(first_chunk_);
2505 chunk->set_size(chunk_size);
2506 first_chunk_ = chunk;
2507
2508 // Set the object address and size in the page header and clear its
2509 // remembered set.
2510 Page* page = Page::FromAddress(RoundUp(chunk->address(), Page::kPageSize));
2511 Address object_address = page->ObjectAreaStart();
2512 // Clear the low order bit of the second word in the page to flag it as a
2513 // large object page. If the chunk_size happened to be written there, its
2514 // low order bit should already be clear.
2515 ASSERT((chunk_size & 0x1) == 0);
2516 page->is_normal_page &= ~0x1;
2517 page->ClearRSet();
2518 int extra_bytes = requested_size - object_size;
2519 if (extra_bytes > 0) {
2520 // The extra memory for the remembered set should be cleared.
2521 memset(object_address + object_size, 0, extra_bytes);
2522 }
2523
2524 return HeapObject::FromAddress(object_address);
2525}
2526
2527
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002528Object* LargeObjectSpace::AllocateRawCode(int size_in_bytes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002529 ASSERT(0 < size_in_bytes);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002530 return AllocateRawInternal(size_in_bytes,
2531 size_in_bytes,
2532 EXECUTABLE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002533}
2534
2535
2536Object* LargeObjectSpace::AllocateRawFixedArray(int size_in_bytes) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002537 ASSERT(0 < size_in_bytes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002538 int extra_rset_bytes = ExtraRSetBytesFor(size_in_bytes);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002539 return AllocateRawInternal(size_in_bytes + extra_rset_bytes,
2540 size_in_bytes,
2541 NOT_EXECUTABLE);
2542}
2543
2544
2545Object* LargeObjectSpace::AllocateRaw(int size_in_bytes) {
2546 ASSERT(0 < size_in_bytes);
2547 return AllocateRawInternal(size_in_bytes,
2548 size_in_bytes,
2549 NOT_EXECUTABLE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002550}
2551
2552
2553// GC support
2554Object* LargeObjectSpace::FindObject(Address a) {
2555 for (LargeObjectChunk* chunk = first_chunk_;
2556 chunk != NULL;
2557 chunk = chunk->next()) {
2558 Address chunk_address = chunk->address();
2559 if (chunk_address <= a && a < chunk_address + chunk->size()) {
2560 return chunk->GetObject();
2561 }
2562 }
2563 return Failure::Exception();
2564}
2565
2566
2567void LargeObjectSpace::ClearRSet() {
2568 ASSERT(Page::is_rset_in_use());
2569
2570 LargeObjectIterator it(this);
2571 while (it.has_next()) {
2572 HeapObject* object = it.next();
2573 // We only have code, sequential strings, or fixed arrays in large
2574 // object space, and only fixed arrays need remembered set support.
2575 if (object->IsFixedArray()) {
2576 // Clear the normal remembered set region of the page;
2577 Page* page = Page::FromAddress(object->address());
2578 page->ClearRSet();
2579
2580 // Clear the extra remembered set.
2581 int size = object->Size();
2582 int extra_rset_bytes = ExtraRSetBytesFor(size);
2583 memset(object->address() + size, 0, extra_rset_bytes);
2584 }
2585 }
2586}
2587
2588
2589void LargeObjectSpace::IterateRSet(ObjectSlotCallback copy_object_func) {
2590 ASSERT(Page::is_rset_in_use());
2591
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002592 static void* lo_rset_histogram = StatsTable::CreateHistogram(
2593 "V8.RSetLO",
2594 0,
2595 // Keeping this histogram's buckets the same as the paged space histogram.
2596 Page::kObjectAreaSize / kPointerSize,
2597 30);
2598
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002599 LargeObjectIterator it(this);
2600 while (it.has_next()) {
2601 // We only have code, sequential strings, or fixed arrays in large
2602 // object space, and only fixed arrays can possibly contain pointers to
2603 // the young generation.
2604 HeapObject* object = it.next();
2605 if (object->IsFixedArray()) {
2606 // Iterate the normal page remembered set range.
2607 Page* page = Page::FromAddress(object->address());
2608 Address object_end = object->address() + object->Size();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002609 int count = Heap::IterateRSetRange(page->ObjectAreaStart(),
2610 Min(page->ObjectAreaEnd(), object_end),
2611 page->RSetStart(),
2612 copy_object_func);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002613
2614 // Iterate the extra array elements.
2615 if (object_end > page->ObjectAreaEnd()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002616 count += Heap::IterateRSetRange(page->ObjectAreaEnd(), object_end,
2617 object_end, copy_object_func);
2618 }
2619 if (lo_rset_histogram != NULL) {
2620 StatsTable::AddHistogramSample(lo_rset_histogram, count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002621 }
2622 }
2623 }
2624}
2625
2626
2627void LargeObjectSpace::FreeUnmarkedObjects() {
2628 LargeObjectChunk* previous = NULL;
2629 LargeObjectChunk* current = first_chunk_;
2630 while (current != NULL) {
2631 HeapObject* object = current->GetObject();
kasper.lund7276f142008-07-30 08:49:36 +00002632 if (object->IsMarked()) {
2633 object->ClearMark();
2634 MarkCompactCollector::tracer()->decrement_marked_count();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002635 previous = current;
2636 current = current->next();
2637 } else {
2638 Address chunk_address = current->address();
2639 size_t chunk_size = current->size();
2640
2641 // Cut the chunk out from the chunk list.
2642 current = current->next();
2643 if (previous == NULL) {
2644 first_chunk_ = current;
2645 } else {
2646 previous->set_next(current);
2647 }
2648
2649 // Free the chunk.
2650 if (object->IsCode()) {
2651 LOG(CodeDeleteEvent(object->address()));
2652 }
2653 size_ -= chunk_size;
2654 page_count_--;
2655 MemoryAllocator::FreeRawMemory(chunk_address, chunk_size);
2656 LOG(DeleteEvent("LargeObjectChunk", chunk_address));
2657 }
2658 }
2659}
2660
2661
2662bool LargeObjectSpace::Contains(HeapObject* object) {
2663 Address address = object->address();
2664 Page* page = Page::FromAddress(address);
2665
2666 SLOW_ASSERT(!page->IsLargeObjectPage()
2667 || !FindObject(address)->IsFailure());
2668
2669 return page->IsLargeObjectPage();
2670}
2671
2672
2673#ifdef DEBUG
2674// We do not assume that the large object iterator works, because it depends
2675// on the invariants we are checking during verification.
2676void LargeObjectSpace::Verify() {
2677 for (LargeObjectChunk* chunk = first_chunk_;
2678 chunk != NULL;
2679 chunk = chunk->next()) {
2680 // Each chunk contains an object that starts at the large object page's
2681 // object area start.
2682 HeapObject* object = chunk->GetObject();
2683 Page* page = Page::FromAddress(object->address());
2684 ASSERT(object->address() == page->ObjectAreaStart());
2685
2686 // The first word should be a map, and we expect all map pointers to be
2687 // in map space.
2688 Map* map = object->map();
2689 ASSERT(map->IsMap());
2690 ASSERT(Heap::map_space()->Contains(map));
2691
ager@chromium.orga1645e22009-09-09 19:27:10 +00002692 // We have only code, sequential strings, external strings
2693 // (sequential strings that have been morphed into external
2694 // strings), fixed arrays, and byte arrays in large object space.
2695 ASSERT(object->IsCode() || object->IsSeqString() ||
2696 object->IsExternalString() || object->IsFixedArray() ||
2697 object->IsByteArray());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002698
2699 // The object itself should look OK.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002700 object->Verify();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002701
2702 // Byte arrays and strings don't have interior pointers.
2703 if (object->IsCode()) {
2704 VerifyPointersVisitor code_visitor;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002705 object->IterateBody(map->instance_type(),
2706 object->Size(),
2707 &code_visitor);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002708 } else if (object->IsFixedArray()) {
2709 // We loop over fixed arrays ourselves, rather then using the visitor,
2710 // because the visitor doesn't support the start/offset iteration
2711 // needed for IsRSetSet.
2712 FixedArray* array = FixedArray::cast(object);
2713 for (int j = 0; j < array->length(); j++) {
2714 Object* element = array->get(j);
2715 if (element->IsHeapObject()) {
2716 HeapObject* element_object = HeapObject::cast(element);
2717 ASSERT(Heap::Contains(element_object));
2718 ASSERT(element_object->map()->IsMap());
2719 if (Heap::InNewSpace(element_object)) {
2720 ASSERT(Page::IsRSetSet(object->address(),
2721 FixedArray::kHeaderSize + j * kPointerSize));
2722 }
2723 }
2724 }
2725 }
2726 }
2727}
2728
2729
2730void LargeObjectSpace::Print() {
2731 LargeObjectIterator it(this);
2732 while (it.has_next()) {
2733 it.next()->Print();
2734 }
2735}
2736
2737
2738void LargeObjectSpace::ReportStatistics() {
2739 PrintF(" size: %d\n", size_);
2740 int num_objects = 0;
2741 ClearHistograms();
2742 LargeObjectIterator it(this);
2743 while (it.has_next()) {
2744 num_objects++;
2745 CollectHistogramInfo(it.next());
2746 }
2747
2748 PrintF(" number of objects %d\n", num_objects);
2749 if (num_objects > 0) ReportHistogram(false);
2750}
2751
2752
2753void LargeObjectSpace::CollectCodeStatistics() {
2754 LargeObjectIterator obj_it(this);
2755 while (obj_it.has_next()) {
2756 HeapObject* obj = obj_it.next();
2757 if (obj->IsCode()) {
2758 Code* code = Code::cast(obj);
2759 code_kind_statistics[code->kind()] += code->Size();
2760 }
2761 }
2762}
2763
2764
2765void LargeObjectSpace::PrintRSet() {
2766 LargeObjectIterator it(this);
2767 while (it.has_next()) {
2768 HeapObject* object = it.next();
2769 if (object->IsFixedArray()) {
2770 Page* page = Page::FromAddress(object->address());
2771
2772 Address allocation_top = object->address() + object->Size();
2773 PrintF("large page 0x%x:\n", page);
2774 PrintRSetRange(page->RSetStart(), page->RSetEnd(),
2775 reinterpret_cast<Object**>(object->address()),
2776 allocation_top);
2777 int extra_array_bytes = object->Size() - Page::kObjectAreaSize;
2778 int extra_rset_bits = RoundUp(extra_array_bytes / kPointerSize,
2779 kBitsPerInt);
2780 PrintF("------------------------------------------------------------"
2781 "-----------\n");
2782 PrintRSetRange(allocation_top,
2783 allocation_top + extra_rset_bits / kBitsPerByte,
2784 reinterpret_cast<Object**>(object->address()
2785 + Page::kObjectAreaSize),
2786 allocation_top);
2787 PrintF("\n");
2788 }
2789 }
2790}
2791#endif // DEBUG
2792
2793} } // namespace v8::internal