blob: 43abaa499931de9dff2a948a1caf901663620779 [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();
985 int maximum_semispace_capacity = Heap::SemiSpaceSize();
986
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
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001001 ASSERT(size == 2 * maximum_semispace_capacity);
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());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001530 ByteArray::cast(this)->set_length(ByteArray::LengthFor(size_in_bytes));
1531 } else if (size_in_bytes == kPointerSize) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001532 set_map(Heap::raw_unchecked_one_pointer_filler_map());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001533 } else if (size_in_bytes == 2 * kPointerSize) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001534 set_map(Heap::raw_unchecked_two_pointer_filler_map());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001535 } else {
1536 UNREACHABLE();
1537 }
kasper.lund7276f142008-07-30 08:49:36 +00001538 ASSERT(Size() == size_in_bytes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001539}
1540
1541
1542Address FreeListNode::next() {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001543 ASSERT(map() == Heap::raw_unchecked_byte_array_map() ||
1544 map() == Heap::raw_unchecked_two_pointer_filler_map());
1545 if (map() == Heap::raw_unchecked_byte_array_map()) {
1546 ASSERT(Size() >= kNextOffset + kPointerSize);
1547 return Memory::Address_at(address() + kNextOffset);
1548 } else {
1549 return Memory::Address_at(address() + kPointerSize);
1550 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001551}
1552
1553
1554void FreeListNode::set_next(Address next) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001555 ASSERT(map() == Heap::raw_unchecked_byte_array_map() ||
1556 map() == Heap::raw_unchecked_two_pointer_filler_map());
1557 if (map() == Heap::raw_unchecked_byte_array_map()) {
1558 ASSERT(Size() >= kNextOffset + kPointerSize);
1559 Memory::Address_at(address() + kNextOffset) = next;
1560 } else {
1561 Memory::Address_at(address() + kPointerSize) = next;
1562 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001563}
1564
1565
1566OldSpaceFreeList::OldSpaceFreeList(AllocationSpace owner) : owner_(owner) {
1567 Reset();
1568}
1569
1570
1571void OldSpaceFreeList::Reset() {
1572 available_ = 0;
1573 for (int i = 0; i < kFreeListsLength; i++) {
1574 free_[i].head_node_ = NULL;
1575 }
1576 needs_rebuild_ = false;
1577 finger_ = kHead;
1578 free_[kHead].next_size_ = kEnd;
1579}
1580
1581
1582void OldSpaceFreeList::RebuildSizeList() {
1583 ASSERT(needs_rebuild_);
1584 int cur = kHead;
1585 for (int i = cur + 1; i < kFreeListsLength; i++) {
1586 if (free_[i].head_node_ != NULL) {
1587 free_[cur].next_size_ = i;
1588 cur = i;
1589 }
1590 }
1591 free_[cur].next_size_ = kEnd;
1592 needs_rebuild_ = false;
1593}
1594
1595
1596int OldSpaceFreeList::Free(Address start, int size_in_bytes) {
1597#ifdef DEBUG
1598 for (int i = 0; i < size_in_bytes; i += kPointerSize) {
1599 Memory::Address_at(start + i) = kZapValue;
1600 }
1601#endif
1602 FreeListNode* node = FreeListNode::FromAddress(start);
1603 node->set_size(size_in_bytes);
1604
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001605 // We don't use the freelists in compacting mode. This makes it more like a
1606 // GC that only has mark-sweep-compact and doesn't have a mark-sweep
1607 // collector.
1608 if (FLAG_always_compact) {
1609 return size_in_bytes;
1610 }
1611
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001612 // Early return to drop too-small blocks on the floor (one or two word
1613 // blocks cannot hold a map pointer, a size field, and a pointer to the
1614 // next block in the free list).
1615 if (size_in_bytes < kMinBlockSize) {
1616 return size_in_bytes;
1617 }
1618
1619 // Insert other blocks at the head of an exact free list.
1620 int index = size_in_bytes >> kPointerSizeLog2;
1621 node->set_next(free_[index].head_node_);
1622 free_[index].head_node_ = node->address();
1623 available_ += size_in_bytes;
1624 needs_rebuild_ = true;
1625 return 0;
1626}
1627
1628
1629Object* OldSpaceFreeList::Allocate(int size_in_bytes, int* wasted_bytes) {
1630 ASSERT(0 < size_in_bytes);
1631 ASSERT(size_in_bytes <= kMaxBlockSize);
1632 ASSERT(IsAligned(size_in_bytes, kPointerSize));
1633
1634 if (needs_rebuild_) RebuildSizeList();
1635 int index = size_in_bytes >> kPointerSizeLog2;
1636 // Check for a perfect fit.
1637 if (free_[index].head_node_ != NULL) {
1638 FreeListNode* node = FreeListNode::FromAddress(free_[index].head_node_);
1639 // If this was the last block of its size, remove the size.
1640 if ((free_[index].head_node_ = node->next()) == NULL) RemoveSize(index);
1641 available_ -= size_in_bytes;
1642 *wasted_bytes = 0;
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001643 ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001644 return node;
1645 }
1646 // Search the size list for the best fit.
1647 int prev = finger_ < index ? finger_ : kHead;
1648 int cur = FindSize(index, &prev);
1649 ASSERT(index < cur);
1650 if (cur == kEnd) {
1651 // No large enough size in list.
1652 *wasted_bytes = 0;
1653 return Failure::RetryAfterGC(size_in_bytes, owner_);
1654 }
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001655 ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001656 int rem = cur - index;
1657 int rem_bytes = rem << kPointerSizeLog2;
1658 FreeListNode* cur_node = FreeListNode::FromAddress(free_[cur].head_node_);
kasper.lund7276f142008-07-30 08:49:36 +00001659 ASSERT(cur_node->Size() == (cur << kPointerSizeLog2));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001660 FreeListNode* rem_node = FreeListNode::FromAddress(free_[cur].head_node_ +
1661 size_in_bytes);
1662 // Distinguish the cases prev < rem < cur and rem <= prev < cur
1663 // to avoid many redundant tests and calls to Insert/RemoveSize.
1664 if (prev < rem) {
1665 // Simple case: insert rem between prev and cur.
1666 finger_ = prev;
1667 free_[prev].next_size_ = rem;
1668 // If this was the last block of size cur, remove the size.
1669 if ((free_[cur].head_node_ = cur_node->next()) == NULL) {
1670 free_[rem].next_size_ = free_[cur].next_size_;
1671 } else {
1672 free_[rem].next_size_ = cur;
1673 }
1674 // Add the remainder block.
1675 rem_node->set_size(rem_bytes);
1676 rem_node->set_next(free_[rem].head_node_);
1677 free_[rem].head_node_ = rem_node->address();
1678 } else {
1679 // If this was the last block of size cur, remove the size.
1680 if ((free_[cur].head_node_ = cur_node->next()) == NULL) {
1681 finger_ = prev;
1682 free_[prev].next_size_ = free_[cur].next_size_;
1683 }
1684 if (rem_bytes < kMinBlockSize) {
1685 // Too-small remainder is wasted.
1686 rem_node->set_size(rem_bytes);
1687 available_ -= size_in_bytes + rem_bytes;
1688 *wasted_bytes = rem_bytes;
1689 return cur_node;
1690 }
1691 // Add the remainder block and, if needed, insert its size.
1692 rem_node->set_size(rem_bytes);
1693 rem_node->set_next(free_[rem].head_node_);
1694 free_[rem].head_node_ = rem_node->address();
1695 if (rem_node->next() == NULL) InsertSize(rem);
1696 }
1697 available_ -= size_in_bytes;
1698 *wasted_bytes = 0;
1699 return cur_node;
1700}
1701
1702
kasper.lund7276f142008-07-30 08:49:36 +00001703#ifdef DEBUG
1704bool OldSpaceFreeList::Contains(FreeListNode* node) {
1705 for (int i = 0; i < kFreeListsLength; i++) {
1706 Address cur_addr = free_[i].head_node_;
1707 while (cur_addr != NULL) {
1708 FreeListNode* cur_node = FreeListNode::FromAddress(cur_addr);
1709 if (cur_node == node) return true;
1710 cur_addr = cur_node->next();
1711 }
1712 }
1713 return false;
1714}
1715#endif
1716
1717
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001718FixedSizeFreeList::FixedSizeFreeList(AllocationSpace owner, int object_size)
1719 : owner_(owner), object_size_(object_size) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001720 Reset();
1721}
1722
1723
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001724void FixedSizeFreeList::Reset() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001725 available_ = 0;
1726 head_ = NULL;
1727}
1728
1729
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001730void FixedSizeFreeList::Free(Address start) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001731#ifdef DEBUG
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001732 for (int i = 0; i < object_size_; i += kPointerSize) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001733 Memory::Address_at(start + i) = kZapValue;
1734 }
1735#endif
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001736 ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001737 FreeListNode* node = FreeListNode::FromAddress(start);
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001738 node->set_size(object_size_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001739 node->set_next(head_);
1740 head_ = node->address();
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001741 available_ += object_size_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001742}
1743
1744
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001745Object* FixedSizeFreeList::Allocate() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001746 if (head_ == NULL) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001747 return Failure::RetryAfterGC(object_size_, owner_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001748 }
1749
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001750 ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001751 FreeListNode* node = FreeListNode::FromAddress(head_);
1752 head_ = node->next();
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001753 available_ -= object_size_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001754 return node;
1755}
1756
1757
1758// -----------------------------------------------------------------------------
1759// OldSpace implementation
1760
1761void OldSpace::PrepareForMarkCompact(bool will_compact) {
1762 if (will_compact) {
1763 // Reset relocation info. During a compacting collection, everything in
1764 // the space is considered 'available' and we will rediscover live data
1765 // and waste during the collection.
1766 MCResetRelocationInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001767 ASSERT(Available() == Capacity());
1768 } else {
1769 // During a non-compacting collection, everything below the linear
1770 // allocation pointer is considered allocated (everything above is
1771 // available) and we will rediscover available and wasted bytes during
1772 // the collection.
1773 accounting_stats_.AllocateBytes(free_list_.available());
1774 accounting_stats_.FillWastedBytes(Waste());
1775 }
1776
kasper.lund7276f142008-07-30 08:49:36 +00001777 // Clear the free list before a full GC---it will be rebuilt afterward.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001778 free_list_.Reset();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001779}
1780
1781
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001782void OldSpace::MCCommitRelocationInfo() {
1783 // Update fast allocation info.
1784 allocation_info_.top = mc_forwarding_info_.top;
1785 allocation_info_.limit = mc_forwarding_info_.limit;
kasper.lund7276f142008-07-30 08:49:36 +00001786 ASSERT(allocation_info_.VerifyPagedAllocation());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001787
1788 // The space is compacted and we haven't yet built free lists or
1789 // wasted any space.
1790 ASSERT(Waste() == 0);
1791 ASSERT(AvailableFree() == 0);
1792
1793 // Build the free list for the space.
1794 int computed_size = 0;
1795 PageIterator it(this, PageIterator::PAGES_USED_BY_MC);
1796 while (it.has_next()) {
1797 Page* p = it.next();
1798 // Space below the relocation pointer is allocated.
1799 computed_size += p->mc_relocation_top - p->ObjectAreaStart();
1800 if (it.has_next()) {
1801 // Free the space at the top of the page. We cannot use
1802 // p->mc_relocation_top after the call to Free (because Free will clear
1803 // remembered set bits).
1804 int extra_size = p->ObjectAreaEnd() - p->mc_relocation_top;
1805 if (extra_size > 0) {
1806 int wasted_bytes = free_list_.Free(p->mc_relocation_top, extra_size);
1807 // The bytes we have just "freed" to add to the free list were
1808 // already accounted as available.
1809 accounting_stats_.WasteBytes(wasted_bytes);
1810 }
1811 }
1812 }
1813
1814 // Make sure the computed size - based on the used portion of the pages in
1815 // use - matches the size obtained while computing forwarding addresses.
1816 ASSERT(computed_size == Size());
1817}
1818
1819
kasper.lund7276f142008-07-30 08:49:36 +00001820// Slow case for normal allocation. Try in order: (1) allocate in the next
1821// page in the space, (2) allocate off the space's free list, (3) expand the
1822// space, (4) fail.
1823HeapObject* OldSpace::SlowAllocateRaw(int size_in_bytes) {
1824 // Linear allocation in this space has failed. If there is another page
1825 // in the space, move to that page and allocate there. This allocation
1826 // should succeed (size_in_bytes should not be greater than a page's
1827 // object area size).
1828 Page* current_page = TopPageOf(allocation_info_);
1829 if (current_page->next_page()->is_valid()) {
1830 return AllocateInNextPage(current_page, size_in_bytes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001831 }
kasper.lund7276f142008-07-30 08:49:36 +00001832
1833 // There is no next page in this space. Try free list allocation.
1834 int wasted_bytes;
1835 Object* result = free_list_.Allocate(size_in_bytes, &wasted_bytes);
1836 accounting_stats_.WasteBytes(wasted_bytes);
1837 if (!result->IsFailure()) {
1838 accounting_stats_.AllocateBytes(size_in_bytes);
1839 return HeapObject::cast(result);
1840 }
1841
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00001842 // Free list allocation failed and there is no next page. Fail if we have
1843 // hit the old generation size limit that should cause a garbage
1844 // collection.
1845 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) {
1846 return NULL;
1847 }
1848
1849 // Try to expand the space and allocate in the new next page.
kasper.lund7276f142008-07-30 08:49:36 +00001850 ASSERT(!current_page->next_page()->is_valid());
1851 if (Expand(current_page)) {
1852 return AllocateInNextPage(current_page, size_in_bytes);
1853 }
1854
1855 // Finally, fail.
1856 return NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001857}
1858
1859
kasper.lund7276f142008-07-30 08:49:36 +00001860// Add the block at the top of the page to the space's free list, set the
1861// allocation info to the next page (assumed to be one), and allocate
1862// linearly there.
1863HeapObject* OldSpace::AllocateInNextPage(Page* current_page,
1864 int size_in_bytes) {
1865 ASSERT(current_page->next_page()->is_valid());
1866 // Add the block at the top of this page to the free list.
1867 int free_size = current_page->ObjectAreaEnd() - allocation_info_.top;
1868 if (free_size > 0) {
1869 int wasted_bytes = free_list_.Free(allocation_info_.top, free_size);
1870 accounting_stats_.WasteBytes(wasted_bytes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001871 }
kasper.lund7276f142008-07-30 08:49:36 +00001872 SetAllocationInfo(&allocation_info_, current_page->next_page());
1873 return AllocateLinearly(&allocation_info_, size_in_bytes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001874}
1875
1876
1877#ifdef DEBUG
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001878struct CommentStatistic {
1879 const char* comment;
1880 int size;
1881 int count;
1882 void Clear() {
1883 comment = NULL;
1884 size = 0;
1885 count = 0;
1886 }
1887};
1888
1889
1890// must be small, since an iteration is used for lookup
1891const int kMaxComments = 64;
1892static CommentStatistic comments_statistics[kMaxComments+1];
1893
1894
1895void PagedSpace::ReportCodeStatistics() {
1896 ReportCodeKindStatistics();
1897 PrintF("Code comment statistics (\" [ comment-txt : size/ "
1898 "count (average)\"):\n");
1899 for (int i = 0; i <= kMaxComments; i++) {
1900 const CommentStatistic& cs = comments_statistics[i];
1901 if (cs.size > 0) {
1902 PrintF(" %-30s: %10d/%6d (%d)\n", cs.comment, cs.size, cs.count,
1903 cs.size/cs.count);
1904 }
1905 }
1906 PrintF("\n");
1907}
1908
1909
1910void PagedSpace::ResetCodeStatistics() {
1911 ClearCodeKindStatistics();
1912 for (int i = 0; i < kMaxComments; i++) comments_statistics[i].Clear();
1913 comments_statistics[kMaxComments].comment = "Unknown";
1914 comments_statistics[kMaxComments].size = 0;
1915 comments_statistics[kMaxComments].count = 0;
1916}
1917
1918
1919// Adds comment to 'comment_statistics' table. Performance OK sa long as
1920// 'kMaxComments' is small
1921static void EnterComment(const char* comment, int delta) {
1922 // Do not count empty comments
1923 if (delta <= 0) return;
1924 CommentStatistic* cs = &comments_statistics[kMaxComments];
1925 // Search for a free or matching entry in 'comments_statistics': 'cs'
1926 // points to result.
1927 for (int i = 0; i < kMaxComments; i++) {
1928 if (comments_statistics[i].comment == NULL) {
1929 cs = &comments_statistics[i];
1930 cs->comment = comment;
1931 break;
1932 } else if (strcmp(comments_statistics[i].comment, comment) == 0) {
1933 cs = &comments_statistics[i];
1934 break;
1935 }
1936 }
1937 // Update entry for 'comment'
1938 cs->size += delta;
1939 cs->count += 1;
1940}
1941
1942
1943// Call for each nested comment start (start marked with '[ xxx', end marked
1944// with ']'. RelocIterator 'it' must point to a comment reloc info.
1945static void CollectCommentStatistics(RelocIterator* it) {
1946 ASSERT(!it->done());
ager@chromium.org236ad962008-09-25 09:45:57 +00001947 ASSERT(it->rinfo()->rmode() == RelocInfo::COMMENT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001948 const char* tmp = reinterpret_cast<const char*>(it->rinfo()->data());
1949 if (tmp[0] != '[') {
1950 // Not a nested comment; skip
1951 return;
1952 }
1953
1954 // Search for end of nested comment or a new nested comment
1955 const char* const comment_txt =
1956 reinterpret_cast<const char*>(it->rinfo()->data());
1957 const byte* prev_pc = it->rinfo()->pc();
1958 int flat_delta = 0;
1959 it->next();
1960 while (true) {
1961 // All nested comments must be terminated properly, and therefore exit
1962 // from loop.
1963 ASSERT(!it->done());
ager@chromium.org236ad962008-09-25 09:45:57 +00001964 if (it->rinfo()->rmode() == RelocInfo::COMMENT) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001965 const char* const txt =
1966 reinterpret_cast<const char*>(it->rinfo()->data());
1967 flat_delta += it->rinfo()->pc() - prev_pc;
1968 if (txt[0] == ']') break; // End of nested comment
1969 // A new comment
1970 CollectCommentStatistics(it);
1971 // Skip code that was covered with previous comment
1972 prev_pc = it->rinfo()->pc();
1973 }
1974 it->next();
1975 }
1976 EnterComment(comment_txt, flat_delta);
1977}
1978
1979
1980// Collects code size statistics:
1981// - by code kind
1982// - by code comment
1983void PagedSpace::CollectCodeStatistics() {
1984 HeapObjectIterator obj_it(this);
1985 while (obj_it.has_next()) {
1986 HeapObject* obj = obj_it.next();
1987 if (obj->IsCode()) {
1988 Code* code = Code::cast(obj);
1989 code_kind_statistics[code->kind()] += code->Size();
1990 RelocIterator it(code);
1991 int delta = 0;
1992 const byte* prev_pc = code->instruction_start();
1993 while (!it.done()) {
ager@chromium.org236ad962008-09-25 09:45:57 +00001994 if (it.rinfo()->rmode() == RelocInfo::COMMENT) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001995 delta += it.rinfo()->pc() - prev_pc;
1996 CollectCommentStatistics(&it);
1997 prev_pc = it.rinfo()->pc();
1998 }
1999 it.next();
2000 }
2001
2002 ASSERT(code->instruction_start() <= prev_pc &&
2003 prev_pc <= code->relocation_start());
2004 delta += code->relocation_start() - prev_pc;
2005 EnterComment("NoComment", delta);
2006 }
2007 }
2008}
2009
2010
2011void OldSpace::ReportStatistics() {
2012 int pct = Available() * 100 / Capacity();
2013 PrintF(" capacity: %d, waste: %d, available: %d, %%%d\n",
2014 Capacity(), Waste(), Available(), pct);
2015
2016 // Report remembered set statistics.
2017 int rset_marked_pointers = 0;
2018 int rset_marked_arrays = 0;
2019 int rset_marked_array_elements = 0;
2020 int cross_gen_pointers = 0;
2021 int cross_gen_array_elements = 0;
2022
2023 PageIterator page_it(this, PageIterator::PAGES_IN_USE);
2024 while (page_it.has_next()) {
2025 Page* p = page_it.next();
2026
2027 for (Address rset_addr = p->RSetStart();
2028 rset_addr < p->RSetEnd();
2029 rset_addr += kIntSize) {
2030 int rset = Memory::int_at(rset_addr);
2031 if (rset != 0) {
2032 // Bits were set
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00002033 int intoff = rset_addr - p->address() - Page::kRSetOffset;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002034 int bitoff = 0;
2035 for (; bitoff < kBitsPerInt; ++bitoff) {
2036 if ((rset & (1 << bitoff)) != 0) {
2037 int bitpos = intoff*kBitsPerByte + bitoff;
2038 Address slot = p->OffsetToAddress(bitpos << kObjectAlignmentBits);
2039 Object** obj = reinterpret_cast<Object**>(slot);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002040 if (*obj == Heap::raw_unchecked_fixed_array_map()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002041 rset_marked_arrays++;
2042 FixedArray* fa = FixedArray::cast(HeapObject::FromAddress(slot));
2043
2044 rset_marked_array_elements += fa->length();
2045 // Manually inline FixedArray::IterateBody
2046 Address elm_start = slot + FixedArray::kHeaderSize;
2047 Address elm_stop = elm_start + fa->length() * kPointerSize;
2048 for (Address elm_addr = elm_start;
2049 elm_addr < elm_stop; elm_addr += kPointerSize) {
2050 // Filter non-heap-object pointers
2051 Object** elm_p = reinterpret_cast<Object**>(elm_addr);
2052 if (Heap::InNewSpace(*elm_p))
2053 cross_gen_array_elements++;
2054 }
2055 } else {
2056 rset_marked_pointers++;
2057 if (Heap::InNewSpace(*obj))
2058 cross_gen_pointers++;
2059 }
2060 }
2061 }
2062 }
2063 }
2064 }
2065
2066 pct = rset_marked_pointers == 0 ?
2067 0 : cross_gen_pointers * 100 / rset_marked_pointers;
2068 PrintF(" rset-marked pointers %d, to-new-space %d (%%%d)\n",
2069 rset_marked_pointers, cross_gen_pointers, pct);
2070 PrintF(" rset_marked arrays %d, ", rset_marked_arrays);
2071 PrintF(" elements %d, ", rset_marked_array_elements);
2072 pct = rset_marked_array_elements == 0 ? 0
2073 : cross_gen_array_elements * 100 / rset_marked_array_elements;
2074 PrintF(" pointers to new space %d (%%%d)\n", cross_gen_array_elements, pct);
2075 PrintF(" total rset-marked bits %d\n",
2076 (rset_marked_pointers + rset_marked_arrays));
2077 pct = (rset_marked_pointers + rset_marked_array_elements) == 0 ? 0
2078 : (cross_gen_pointers + cross_gen_array_elements) * 100 /
2079 (rset_marked_pointers + rset_marked_array_elements);
2080 PrintF(" total rset pointers %d, true cross generation ones %d (%%%d)\n",
2081 (rset_marked_pointers + rset_marked_array_elements),
2082 (cross_gen_pointers + cross_gen_array_elements),
2083 pct);
2084
2085 ClearHistograms();
2086 HeapObjectIterator obj_it(this);
2087 while (obj_it.has_next()) { CollectHistogramInfo(obj_it.next()); }
2088 ReportHistogram(true);
2089}
2090
2091
2092// Dump the range of remembered set words between [start, end) corresponding
2093// to the pointers starting at object_p. The allocation_top is an object
2094// pointer which should not be read past. This is important for large object
2095// pages, where some bits in the remembered set range do not correspond to
2096// allocated addresses.
2097static void PrintRSetRange(Address start, Address end, Object** object_p,
2098 Address allocation_top) {
2099 Address rset_address = start;
2100
2101 // If the range starts on on odd numbered word (eg, for large object extra
2102 // remembered set ranges), print some spaces.
ager@chromium.org9085a012009-05-11 19:22:57 +00002103 if ((reinterpret_cast<uintptr_t>(start) / kIntSize) % 2 == 1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002104 PrintF(" ");
2105 }
2106
2107 // Loop over all the words in the range.
2108 while (rset_address < end) {
2109 uint32_t rset_word = Memory::uint32_at(rset_address);
2110 int bit_position = 0;
2111
2112 // Loop over all the bits in the word.
2113 while (bit_position < kBitsPerInt) {
2114 if (object_p == reinterpret_cast<Object**>(allocation_top)) {
2115 // Print a bar at the allocation pointer.
2116 PrintF("|");
2117 } else if (object_p > reinterpret_cast<Object**>(allocation_top)) {
2118 // Do not dereference object_p past the allocation pointer.
2119 PrintF("#");
2120 } else if ((rset_word & (1 << bit_position)) == 0) {
2121 // Print a dot for zero bits.
2122 PrintF(".");
2123 } else if (Heap::InNewSpace(*object_p)) {
2124 // Print an X for one bits for pointers to new space.
2125 PrintF("X");
2126 } else {
2127 // Print a circle for one bits for pointers to old space.
2128 PrintF("o");
2129 }
2130
2131 // Print a space after every 8th bit except the last.
2132 if (bit_position % 8 == 7 && bit_position != (kBitsPerInt - 1)) {
2133 PrintF(" ");
2134 }
2135
2136 // Advance to next bit.
2137 bit_position++;
2138 object_p++;
2139 }
2140
2141 // Print a newline after every odd numbered word, otherwise a space.
ager@chromium.org9085a012009-05-11 19:22:57 +00002142 if ((reinterpret_cast<uintptr_t>(rset_address) / kIntSize) % 2 == 1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002143 PrintF("\n");
2144 } else {
2145 PrintF(" ");
2146 }
2147
2148 // Advance to next remembered set word.
2149 rset_address += kIntSize;
2150 }
2151}
2152
2153
2154void PagedSpace::DoPrintRSet(const char* space_name) {
2155 PageIterator it(this, PageIterator::PAGES_IN_USE);
2156 while (it.has_next()) {
2157 Page* p = it.next();
2158 PrintF("%s page 0x%x:\n", space_name, p);
2159 PrintRSetRange(p->RSetStart(), p->RSetEnd(),
2160 reinterpret_cast<Object**>(p->ObjectAreaStart()),
2161 p->AllocationTop());
2162 PrintF("\n");
2163 }
2164}
2165
2166
2167void OldSpace::PrintRSet() { DoPrintRSet("old"); }
2168#endif
2169
2170// -----------------------------------------------------------------------------
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002171// FixedSpace implementation
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002172
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002173void FixedSpace::PrepareForMarkCompact(bool will_compact) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002174 if (will_compact) {
2175 // Reset relocation info.
2176 MCResetRelocationInfo();
2177
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002178 // During a compacting collection, everything in the space is considered
2179 // 'available' (set by the call to MCResetRelocationInfo) and we will
2180 // rediscover live and wasted bytes during the collection.
2181 ASSERT(Available() == Capacity());
2182 } else {
2183 // During a non-compacting collection, everything below the linear
2184 // allocation pointer except wasted top-of-page blocks is considered
2185 // allocated and we will rediscover available bytes during the
2186 // collection.
2187 accounting_stats_.AllocateBytes(free_list_.available());
2188 }
2189
kasper.lund7276f142008-07-30 08:49:36 +00002190 // Clear the free list before a full GC---it will be rebuilt afterward.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002191 free_list_.Reset();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002192}
2193
2194
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002195void FixedSpace::MCCommitRelocationInfo() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002196 // Update fast allocation info.
2197 allocation_info_.top = mc_forwarding_info_.top;
2198 allocation_info_.limit = mc_forwarding_info_.limit;
kasper.lund7276f142008-07-30 08:49:36 +00002199 ASSERT(allocation_info_.VerifyPagedAllocation());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002200
2201 // The space is compacted and we haven't yet wasted any space.
2202 ASSERT(Waste() == 0);
2203
2204 // Update allocation_top of each page in use and compute waste.
2205 int computed_size = 0;
2206 PageIterator it(this, PageIterator::PAGES_USED_BY_MC);
2207 while (it.has_next()) {
2208 Page* page = it.next();
2209 Address page_top = page->AllocationTop();
2210 computed_size += page_top - page->ObjectAreaStart();
2211 if (it.has_next()) {
2212 accounting_stats_.WasteBytes(page->ObjectAreaEnd() - page_top);
2213 }
2214 }
2215
2216 // Make sure the computed size - based on the used portion of the
2217 // pages in use - matches the size we adjust during allocation.
2218 ASSERT(computed_size == Size());
2219}
2220
2221
kasper.lund7276f142008-07-30 08:49:36 +00002222// Slow case for normal allocation. Try in order: (1) allocate in the next
2223// page in the space, (2) allocate off the space's free list, (3) expand the
2224// space, (4) fail.
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002225HeapObject* FixedSpace::SlowAllocateRaw(int size_in_bytes) {
2226 ASSERT_EQ(object_size_in_bytes_, size_in_bytes);
kasper.lund7276f142008-07-30 08:49:36 +00002227 // Linear allocation in this space has failed. If there is another page
2228 // in the space, move to that page and allocate there. This allocation
2229 // should succeed.
2230 Page* current_page = TopPageOf(allocation_info_);
2231 if (current_page->next_page()->is_valid()) {
2232 return AllocateInNextPage(current_page, size_in_bytes);
2233 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002234
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002235 // There is no next page in this space. Try free list allocation.
2236 // The fixed space free list implicitly assumes that all free blocks
2237 // are of the fixed size.
2238 if (size_in_bytes == object_size_in_bytes_) {
kasper.lund7276f142008-07-30 08:49:36 +00002239 Object* result = free_list_.Allocate();
2240 if (!result->IsFailure()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002241 accounting_stats_.AllocateBytes(size_in_bytes);
kasper.lund7276f142008-07-30 08:49:36 +00002242 return HeapObject::cast(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002243 }
2244 }
kasper.lund7276f142008-07-30 08:49:36 +00002245
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00002246 // Free list allocation failed and there is no next page. Fail if we have
2247 // hit the old generation size limit that should cause a garbage
2248 // collection.
2249 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) {
2250 return NULL;
2251 }
2252
2253 // Try to expand the space and allocate in the new next page.
kasper.lund7276f142008-07-30 08:49:36 +00002254 ASSERT(!current_page->next_page()->is_valid());
2255 if (Expand(current_page)) {
2256 return AllocateInNextPage(current_page, size_in_bytes);
2257 }
2258
2259 // Finally, fail.
2260 return NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002261}
2262
2263
kasper.lund7276f142008-07-30 08:49:36 +00002264// Move to the next page (there is assumed to be one) and allocate there.
2265// The top of page block is always wasted, because it is too small to hold a
2266// map.
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002267HeapObject* FixedSpace::AllocateInNextPage(Page* current_page,
2268 int size_in_bytes) {
kasper.lund7276f142008-07-30 08:49:36 +00002269 ASSERT(current_page->next_page()->is_valid());
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002270 ASSERT(current_page->ObjectAreaEnd() - allocation_info_.top == page_extra_);
2271 ASSERT_EQ(object_size_in_bytes_, size_in_bytes);
2272 accounting_stats_.WasteBytes(page_extra_);
kasper.lund7276f142008-07-30 08:49:36 +00002273 SetAllocationInfo(&allocation_info_, current_page->next_page());
2274 return AllocateLinearly(&allocation_info_, size_in_bytes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002275}
2276
2277
2278#ifdef DEBUG
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002279void FixedSpace::ReportStatistics() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002280 int pct = Available() * 100 / Capacity();
2281 PrintF(" capacity: %d, waste: %d, available: %d, %%%d\n",
2282 Capacity(), Waste(), Available(), pct);
2283
2284 // Report remembered set statistics.
2285 int rset_marked_pointers = 0;
2286 int cross_gen_pointers = 0;
2287
2288 PageIterator page_it(this, PageIterator::PAGES_IN_USE);
2289 while (page_it.has_next()) {
2290 Page* p = page_it.next();
2291
2292 for (Address rset_addr = p->RSetStart();
2293 rset_addr < p->RSetEnd();
2294 rset_addr += kIntSize) {
2295 int rset = Memory::int_at(rset_addr);
2296 if (rset != 0) {
2297 // Bits were set
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +00002298 int intoff = rset_addr - p->address() - Page::kRSetOffset;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002299 int bitoff = 0;
2300 for (; bitoff < kBitsPerInt; ++bitoff) {
2301 if ((rset & (1 << bitoff)) != 0) {
2302 int bitpos = intoff*kBitsPerByte + bitoff;
2303 Address slot = p->OffsetToAddress(bitpos << kObjectAlignmentBits);
2304 Object** obj = reinterpret_cast<Object**>(slot);
2305 rset_marked_pointers++;
2306 if (Heap::InNewSpace(*obj))
2307 cross_gen_pointers++;
2308 }
2309 }
2310 }
2311 }
2312 }
2313
2314 pct = rset_marked_pointers == 0 ?
2315 0 : cross_gen_pointers * 100 / rset_marked_pointers;
2316 PrintF(" rset-marked pointers %d, to-new-space %d (%%%d)\n",
2317 rset_marked_pointers, cross_gen_pointers, pct);
2318
2319 ClearHistograms();
2320 HeapObjectIterator obj_it(this);
2321 while (obj_it.has_next()) { CollectHistogramInfo(obj_it.next()); }
2322 ReportHistogram(false);
2323}
2324
2325
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002326void FixedSpace::PrintRSet() { DoPrintRSet(name_); }
2327#endif
2328
2329
2330// -----------------------------------------------------------------------------
2331// MapSpace implementation
2332
2333void MapSpace::PrepareForMarkCompact(bool will_compact) {
2334 // Call prepare of the super class.
2335 FixedSpace::PrepareForMarkCompact(will_compact);
2336
2337 if (will_compact) {
2338 // Initialize map index entry.
2339 int page_count = 0;
2340 PageIterator it(this, PageIterator::ALL_PAGES);
2341 while (it.has_next()) {
2342 ASSERT_MAP_PAGE_INDEX(page_count);
2343
2344 Page* p = it.next();
2345 ASSERT(p->mc_page_index == page_count);
2346
2347 page_addresses_[page_count++] = p->address();
2348 }
2349 }
2350}
2351
2352
2353#ifdef DEBUG
2354void MapSpace::VerifyObject(HeapObject* object) {
2355 // The object should be a map or a free-list node.
2356 ASSERT(object->IsMap() || object->IsByteArray());
2357}
2358#endif
2359
2360
2361// -----------------------------------------------------------------------------
2362// GlobalPropertyCellSpace implementation
2363
2364#ifdef DEBUG
2365void CellSpace::VerifyObject(HeapObject* object) {
2366 // The object should be a global object property cell or a free-list node.
2367 ASSERT(object->IsJSGlobalPropertyCell() ||
2368 object->map() == Heap::two_pointer_filler_map());
2369}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002370#endif
2371
2372
2373// -----------------------------------------------------------------------------
2374// LargeObjectIterator
2375
2376LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space) {
2377 current_ = space->first_chunk_;
2378 size_func_ = NULL;
2379}
2380
2381
2382LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space,
2383 HeapObjectCallback size_func) {
2384 current_ = space->first_chunk_;
2385 size_func_ = size_func;
2386}
2387
2388
2389HeapObject* LargeObjectIterator::next() {
2390 ASSERT(has_next());
2391 HeapObject* object = current_->GetObject();
2392 current_ = current_->next();
2393 return object;
2394}
2395
2396
2397// -----------------------------------------------------------------------------
2398// LargeObjectChunk
2399
2400LargeObjectChunk* LargeObjectChunk::New(int size_in_bytes,
kasper.lund7276f142008-07-30 08:49:36 +00002401 size_t* chunk_size,
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002402 Executability executable) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002403 size_t requested = ChunkSizeFor(size_in_bytes);
kasper.lund7276f142008-07-30 08:49:36 +00002404 void* mem = MemoryAllocator::AllocateRawMemory(requested,
2405 chunk_size,
2406 executable);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002407 if (mem == NULL) return NULL;
2408 LOG(NewEvent("LargeObjectChunk", mem, *chunk_size));
2409 if (*chunk_size < requested) {
2410 MemoryAllocator::FreeRawMemory(mem, *chunk_size);
2411 LOG(DeleteEvent("LargeObjectChunk", mem));
2412 return NULL;
2413 }
2414 return reinterpret_cast<LargeObjectChunk*>(mem);
2415}
2416
2417
2418int LargeObjectChunk::ChunkSizeFor(int size_in_bytes) {
2419 int os_alignment = OS::AllocateAlignment();
2420 if (os_alignment < Page::kPageSize)
2421 size_in_bytes += (Page::kPageSize - os_alignment);
2422 return size_in_bytes + Page::kObjectStartOffset;
2423}
2424
2425// -----------------------------------------------------------------------------
2426// LargeObjectSpace
2427
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002428LargeObjectSpace::LargeObjectSpace(AllocationSpace id)
2429 : Space(id, NOT_EXECUTABLE), // Managed on a per-allocation basis
kasper.lund7276f142008-07-30 08:49:36 +00002430 first_chunk_(NULL),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002431 size_(0),
2432 page_count_(0) {}
2433
2434
2435bool LargeObjectSpace::Setup() {
2436 first_chunk_ = NULL;
2437 size_ = 0;
2438 page_count_ = 0;
2439 return true;
2440}
2441
2442
2443void LargeObjectSpace::TearDown() {
2444 while (first_chunk_ != NULL) {
2445 LargeObjectChunk* chunk = first_chunk_;
2446 first_chunk_ = first_chunk_->next();
2447 LOG(DeleteEvent("LargeObjectChunk", chunk->address()));
2448 MemoryAllocator::FreeRawMemory(chunk->address(), chunk->size());
2449 }
2450
2451 size_ = 0;
2452 page_count_ = 0;
2453}
2454
2455
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +00002456#ifdef ENABLE_HEAP_PROTECTION
2457
2458void LargeObjectSpace::Protect() {
2459 LargeObjectChunk* chunk = first_chunk_;
2460 while (chunk != NULL) {
2461 MemoryAllocator::Protect(chunk->address(), chunk->size());
2462 chunk = chunk->next();
2463 }
2464}
2465
2466
2467void LargeObjectSpace::Unprotect() {
2468 LargeObjectChunk* chunk = first_chunk_;
2469 while (chunk != NULL) {
2470 bool is_code = chunk->GetObject()->IsCode();
2471 MemoryAllocator::Unprotect(chunk->address(), chunk->size(),
2472 is_code ? EXECUTABLE : NOT_EXECUTABLE);
2473 chunk = chunk->next();
2474 }
2475}
2476
2477#endif
2478
2479
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002480Object* LargeObjectSpace::AllocateRawInternal(int requested_size,
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002481 int object_size,
2482 Executability executable) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002483 ASSERT(0 < object_size && object_size <= requested_size);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00002484
2485 // Check if we want to force a GC before growing the old space further.
2486 // If so, fail the allocation.
2487 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) {
2488 return Failure::RetryAfterGC(requested_size, identity());
2489 }
2490
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002491 size_t chunk_size;
2492 LargeObjectChunk* chunk =
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002493 LargeObjectChunk::New(requested_size, &chunk_size, executable);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002494 if (chunk == NULL) {
kasper.lund7276f142008-07-30 08:49:36 +00002495 return Failure::RetryAfterGC(requested_size, identity());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002496 }
2497
2498 size_ += chunk_size;
2499 page_count_++;
2500 chunk->set_next(first_chunk_);
2501 chunk->set_size(chunk_size);
2502 first_chunk_ = chunk;
2503
2504 // Set the object address and size in the page header and clear its
2505 // remembered set.
2506 Page* page = Page::FromAddress(RoundUp(chunk->address(), Page::kPageSize));
2507 Address object_address = page->ObjectAreaStart();
2508 // Clear the low order bit of the second word in the page to flag it as a
2509 // large object page. If the chunk_size happened to be written there, its
2510 // low order bit should already be clear.
2511 ASSERT((chunk_size & 0x1) == 0);
2512 page->is_normal_page &= ~0x1;
2513 page->ClearRSet();
2514 int extra_bytes = requested_size - object_size;
2515 if (extra_bytes > 0) {
2516 // The extra memory for the remembered set should be cleared.
2517 memset(object_address + object_size, 0, extra_bytes);
2518 }
2519
2520 return HeapObject::FromAddress(object_address);
2521}
2522
2523
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002524Object* LargeObjectSpace::AllocateRawCode(int size_in_bytes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002525 ASSERT(0 < size_in_bytes);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002526 return AllocateRawInternal(size_in_bytes,
2527 size_in_bytes,
2528 EXECUTABLE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002529}
2530
2531
2532Object* LargeObjectSpace::AllocateRawFixedArray(int size_in_bytes) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002533 ASSERT(0 < size_in_bytes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002534 int extra_rset_bytes = ExtraRSetBytesFor(size_in_bytes);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002535 return AllocateRawInternal(size_in_bytes + extra_rset_bytes,
2536 size_in_bytes,
2537 NOT_EXECUTABLE);
2538}
2539
2540
2541Object* LargeObjectSpace::AllocateRaw(int size_in_bytes) {
2542 ASSERT(0 < size_in_bytes);
2543 return AllocateRawInternal(size_in_bytes,
2544 size_in_bytes,
2545 NOT_EXECUTABLE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002546}
2547
2548
2549// GC support
2550Object* LargeObjectSpace::FindObject(Address a) {
2551 for (LargeObjectChunk* chunk = first_chunk_;
2552 chunk != NULL;
2553 chunk = chunk->next()) {
2554 Address chunk_address = chunk->address();
2555 if (chunk_address <= a && a < chunk_address + chunk->size()) {
2556 return chunk->GetObject();
2557 }
2558 }
2559 return Failure::Exception();
2560}
2561
2562
2563void LargeObjectSpace::ClearRSet() {
2564 ASSERT(Page::is_rset_in_use());
2565
2566 LargeObjectIterator it(this);
2567 while (it.has_next()) {
2568 HeapObject* object = it.next();
2569 // We only have code, sequential strings, or fixed arrays in large
2570 // object space, and only fixed arrays need remembered set support.
2571 if (object->IsFixedArray()) {
2572 // Clear the normal remembered set region of the page;
2573 Page* page = Page::FromAddress(object->address());
2574 page->ClearRSet();
2575
2576 // Clear the extra remembered set.
2577 int size = object->Size();
2578 int extra_rset_bytes = ExtraRSetBytesFor(size);
2579 memset(object->address() + size, 0, extra_rset_bytes);
2580 }
2581 }
2582}
2583
2584
2585void LargeObjectSpace::IterateRSet(ObjectSlotCallback copy_object_func) {
2586 ASSERT(Page::is_rset_in_use());
2587
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002588 static void* lo_rset_histogram = StatsTable::CreateHistogram(
2589 "V8.RSetLO",
2590 0,
2591 // Keeping this histogram's buckets the same as the paged space histogram.
2592 Page::kObjectAreaSize / kPointerSize,
2593 30);
2594
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002595 LargeObjectIterator it(this);
2596 while (it.has_next()) {
2597 // We only have code, sequential strings, or fixed arrays in large
2598 // object space, and only fixed arrays can possibly contain pointers to
2599 // the young generation.
2600 HeapObject* object = it.next();
2601 if (object->IsFixedArray()) {
2602 // Iterate the normal page remembered set range.
2603 Page* page = Page::FromAddress(object->address());
2604 Address object_end = object->address() + object->Size();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002605 int count = Heap::IterateRSetRange(page->ObjectAreaStart(),
2606 Min(page->ObjectAreaEnd(), object_end),
2607 page->RSetStart(),
2608 copy_object_func);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002609
2610 // Iterate the extra array elements.
2611 if (object_end > page->ObjectAreaEnd()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002612 count += Heap::IterateRSetRange(page->ObjectAreaEnd(), object_end,
2613 object_end, copy_object_func);
2614 }
2615 if (lo_rset_histogram != NULL) {
2616 StatsTable::AddHistogramSample(lo_rset_histogram, count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002617 }
2618 }
2619 }
2620}
2621
2622
2623void LargeObjectSpace::FreeUnmarkedObjects() {
2624 LargeObjectChunk* previous = NULL;
2625 LargeObjectChunk* current = first_chunk_;
2626 while (current != NULL) {
2627 HeapObject* object = current->GetObject();
kasper.lund7276f142008-07-30 08:49:36 +00002628 if (object->IsMarked()) {
2629 object->ClearMark();
2630 MarkCompactCollector::tracer()->decrement_marked_count();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002631 previous = current;
2632 current = current->next();
2633 } else {
2634 Address chunk_address = current->address();
2635 size_t chunk_size = current->size();
2636
2637 // Cut the chunk out from the chunk list.
2638 current = current->next();
2639 if (previous == NULL) {
2640 first_chunk_ = current;
2641 } else {
2642 previous->set_next(current);
2643 }
2644
2645 // Free the chunk.
2646 if (object->IsCode()) {
2647 LOG(CodeDeleteEvent(object->address()));
2648 }
2649 size_ -= chunk_size;
2650 page_count_--;
2651 MemoryAllocator::FreeRawMemory(chunk_address, chunk_size);
2652 LOG(DeleteEvent("LargeObjectChunk", chunk_address));
2653 }
2654 }
2655}
2656
2657
2658bool LargeObjectSpace::Contains(HeapObject* object) {
2659 Address address = object->address();
2660 Page* page = Page::FromAddress(address);
2661
2662 SLOW_ASSERT(!page->IsLargeObjectPage()
2663 || !FindObject(address)->IsFailure());
2664
2665 return page->IsLargeObjectPage();
2666}
2667
2668
2669#ifdef DEBUG
2670// We do not assume that the large object iterator works, because it depends
2671// on the invariants we are checking during verification.
2672void LargeObjectSpace::Verify() {
2673 for (LargeObjectChunk* chunk = first_chunk_;
2674 chunk != NULL;
2675 chunk = chunk->next()) {
2676 // Each chunk contains an object that starts at the large object page's
2677 // object area start.
2678 HeapObject* object = chunk->GetObject();
2679 Page* page = Page::FromAddress(object->address());
2680 ASSERT(object->address() == page->ObjectAreaStart());
2681
2682 // The first word should be a map, and we expect all map pointers to be
2683 // in map space.
2684 Map* map = object->map();
2685 ASSERT(map->IsMap());
2686 ASSERT(Heap::map_space()->Contains(map));
2687
ager@chromium.orga1645e22009-09-09 19:27:10 +00002688 // We have only code, sequential strings, external strings
2689 // (sequential strings that have been morphed into external
2690 // strings), fixed arrays, and byte arrays in large object space.
2691 ASSERT(object->IsCode() || object->IsSeqString() ||
2692 object->IsExternalString() || object->IsFixedArray() ||
2693 object->IsByteArray());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002694
2695 // The object itself should look OK.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002696 object->Verify();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002697
2698 // Byte arrays and strings don't have interior pointers.
2699 if (object->IsCode()) {
2700 VerifyPointersVisitor code_visitor;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002701 object->IterateBody(map->instance_type(),
2702 object->Size(),
2703 &code_visitor);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002704 } else if (object->IsFixedArray()) {
2705 // We loop over fixed arrays ourselves, rather then using the visitor,
2706 // because the visitor doesn't support the start/offset iteration
2707 // needed for IsRSetSet.
2708 FixedArray* array = FixedArray::cast(object);
2709 for (int j = 0; j < array->length(); j++) {
2710 Object* element = array->get(j);
2711 if (element->IsHeapObject()) {
2712 HeapObject* element_object = HeapObject::cast(element);
2713 ASSERT(Heap::Contains(element_object));
2714 ASSERT(element_object->map()->IsMap());
2715 if (Heap::InNewSpace(element_object)) {
2716 ASSERT(Page::IsRSetSet(object->address(),
2717 FixedArray::kHeaderSize + j * kPointerSize));
2718 }
2719 }
2720 }
2721 }
2722 }
2723}
2724
2725
2726void LargeObjectSpace::Print() {
2727 LargeObjectIterator it(this);
2728 while (it.has_next()) {
2729 it.next()->Print();
2730 }
2731}
2732
2733
2734void LargeObjectSpace::ReportStatistics() {
2735 PrintF(" size: %d\n", size_);
2736 int num_objects = 0;
2737 ClearHistograms();
2738 LargeObjectIterator it(this);
2739 while (it.has_next()) {
2740 num_objects++;
2741 CollectHistogramInfo(it.next());
2742 }
2743
2744 PrintF(" number of objects %d\n", num_objects);
2745 if (num_objects > 0) ReportHistogram(false);
2746}
2747
2748
2749void LargeObjectSpace::CollectCodeStatistics() {
2750 LargeObjectIterator obj_it(this);
2751 while (obj_it.has_next()) {
2752 HeapObject* obj = obj_it.next();
2753 if (obj->IsCode()) {
2754 Code* code = Code::cast(obj);
2755 code_kind_statistics[code->kind()] += code->Size();
2756 }
2757 }
2758}
2759
2760
2761void LargeObjectSpace::PrintRSet() {
2762 LargeObjectIterator it(this);
2763 while (it.has_next()) {
2764 HeapObject* object = it.next();
2765 if (object->IsFixedArray()) {
2766 Page* page = Page::FromAddress(object->address());
2767
2768 Address allocation_top = object->address() + object->Size();
2769 PrintF("large page 0x%x:\n", page);
2770 PrintRSetRange(page->RSetStart(), page->RSetEnd(),
2771 reinterpret_cast<Object**>(object->address()),
2772 allocation_top);
2773 int extra_array_bytes = object->Size() - Page::kObjectAreaSize;
2774 int extra_rset_bits = RoundUp(extra_array_bytes / kPointerSize,
2775 kBitsPerInt);
2776 PrintF("------------------------------------------------------------"
2777 "-----------\n");
2778 PrintRSetRange(allocation_top,
2779 allocation_top + extra_rset_bits / kBitsPerByte,
2780 reinterpret_cast<Object**>(object->address()
2781 + Page::kObjectAreaSize),
2782 allocation_top);
2783 PrintF("\n");
2784 }
2785 }
2786}
2787#endif // DEBUG
2788
2789} } // namespace v8::internal