blob: f3b6b9f639116f18b9f2ed456a2de90f957e8879 [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 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000357 int alloced = static_cast<int>(*allocated);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000358 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 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000370 Counters::memory_allocated.Decrement(static_cast<int>(length));
371 size_ -= static_cast<int>(length);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000372 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));
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000390 size_ += static_cast<int>(requested);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000391 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.
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000400 return static_cast<int>((RoundDown(start + size, Page::kPageSize)
401 - RoundUp(start, Page::kPageSize)) >> Page::kPageSizeBits);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000402}
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_;
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000415 requested_pages = static_cast<int>(chunk_size >> Page::kPageSizeBits);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000416
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 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000448 Counters::memory_allocated.Increment(static_cast<int>(size));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000449
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;
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000469 Counters::memory_allocated.Increment(static_cast<int>(size));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000470 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;
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000481 Counters::memory_allocated.Decrement(static_cast<int>(size));
ager@chromium.orgadd848f2009-08-13 12:44:13 +0000482 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());
ager@chromium.orgc4c92722009-11-18 14:12:51 +0000561 Counters::memory_allocated.Decrement(static_cast<int>(c.size()));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000562 } else {
563 LOG(DeleteEvent("PagedChunk", c.address()));
564 FreeRawMemory(c.address(), c.size());
565 }
566 c.init(NULL, 0, NULL);
567 Push(chunk_id);
568}
569
570
571Page* MemoryAllocator::FindFirstPageInSameChunk(Page* p) {
572 int chunk_id = GetChunkId(p);
573 ASSERT(IsValidChunk(chunk_id));
574
575 Address low = RoundUp(chunks_[chunk_id].address(), Page::kPageSize);
576 return Page::FromAddress(low);
577}
578
579
580Page* MemoryAllocator::FindLastPageInSameChunk(Page* p) {
581 int chunk_id = GetChunkId(p);
582 ASSERT(IsValidChunk(chunk_id));
583
584 Address chunk_start = chunks_[chunk_id].address();
585 size_t chunk_size = chunks_[chunk_id].size();
586
587 Address high = RoundDown(chunk_start + chunk_size, Page::kPageSize);
588 ASSERT(chunk_start <= p->address() && p->address() < high);
589
590 return Page::FromAddress(high - Page::kPageSize);
591}
592
593
594#ifdef DEBUG
595void MemoryAllocator::ReportStatistics() {
596 float pct = static_cast<float>(capacity_ - size_) / capacity_;
597 PrintF(" capacity: %d, used: %d, available: %%%d\n\n",
598 capacity_, size_, static_cast<int>(pct*100));
599}
600#endif
601
602
603// -----------------------------------------------------------------------------
604// PagedSpace implementation
605
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000606PagedSpace::PagedSpace(int max_capacity,
607 AllocationSpace id,
608 Executability executable)
kasper.lund7276f142008-07-30 08:49:36 +0000609 : Space(id, executable) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000610 max_capacity_ = (RoundDown(max_capacity, Page::kPageSize) / Page::kPageSize)
611 * Page::kObjectAreaSize;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000612 accounting_stats_.Clear();
613
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000614 allocation_info_.top = NULL;
615 allocation_info_.limit = NULL;
616
617 mc_forwarding_info_.top = NULL;
618 mc_forwarding_info_.limit = NULL;
619}
620
621
622bool PagedSpace::Setup(Address start, size_t size) {
623 if (HasBeenSetup()) return false;
624
625 int num_pages = 0;
626 // Try to use the virtual memory range passed to us. If it is too small to
627 // contain at least one page, ignore it and allocate instead.
ager@chromium.org9258b6b2008-09-11 09:11:10 +0000628 int pages_in_chunk = PagesInChunk(start, size);
629 if (pages_in_chunk > 0) {
630 first_page_ = MemoryAllocator::CommitPages(RoundUp(start, Page::kPageSize),
631 Page::kPageSize * pages_in_chunk,
632 this, &num_pages);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000633 } else {
634 int requested_pages = Min(MemoryAllocator::kPagesPerChunk,
635 max_capacity_ / Page::kObjectAreaSize);
636 first_page_ =
637 MemoryAllocator::AllocatePages(requested_pages, &num_pages, this);
638 if (!first_page_->is_valid()) return false;
639 }
640
641 // We are sure that the first page is valid and that we have at least one
642 // page.
643 ASSERT(first_page_->is_valid());
644 ASSERT(num_pages > 0);
645 accounting_stats_.ExpandSpace(num_pages * Page::kObjectAreaSize);
646 ASSERT(Capacity() <= max_capacity_);
647
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000648 // Sequentially initialize remembered sets in the newly allocated
649 // pages and cache the current last page in the space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000650 for (Page* p = first_page_; p->is_valid(); p = p->next_page()) {
651 p->ClearRSet();
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000652 last_page_ = p;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000653 }
654
655 // Use first_page_ for allocation.
656 SetAllocationInfo(&allocation_info_, first_page_);
657
658 return true;
659}
660
661
662bool PagedSpace::HasBeenSetup() {
663 return (Capacity() > 0);
664}
665
666
667void PagedSpace::TearDown() {
668 first_page_ = MemoryAllocator::FreePages(first_page_);
669 ASSERT(!first_page_->is_valid());
670
671 accounting_stats_.Clear();
672}
673
674
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +0000675#ifdef ENABLE_HEAP_PROTECTION
676
677void PagedSpace::Protect() {
678 Page* page = first_page_;
679 while (page->is_valid()) {
680 MemoryAllocator::ProtectChunkFromPage(page);
681 page = MemoryAllocator::FindLastPageInSameChunk(page)->next_page();
682 }
683}
684
685
686void PagedSpace::Unprotect() {
687 Page* page = first_page_;
688 while (page->is_valid()) {
689 MemoryAllocator::UnprotectChunkFromPage(page);
690 page = MemoryAllocator::FindLastPageInSameChunk(page)->next_page();
691 }
692}
693
694#endif
695
696
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000697void PagedSpace::ClearRSet() {
698 PageIterator it(this, PageIterator::ALL_PAGES);
699 while (it.has_next()) {
700 it.next()->ClearRSet();
701 }
702}
703
704
705Object* PagedSpace::FindObject(Address addr) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000706 // Note: this function can only be called before or after mark-compact GC
707 // because it accesses map pointers.
708 ASSERT(!MarkCompactCollector::in_use());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000709
710 if (!Contains(addr)) return Failure::Exception();
711
712 Page* p = Page::FromAddress(addr);
kasper.lund7276f142008-07-30 08:49:36 +0000713 ASSERT(IsUsed(p));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000714 Address cur = p->ObjectAreaStart();
715 Address end = p->AllocationTop();
716 while (cur < end) {
717 HeapObject* obj = HeapObject::FromAddress(cur);
718 Address next = cur + obj->Size();
719 if ((cur <= addr) && (addr < next)) return obj;
720 cur = next;
721 }
722
kasper.lund7276f142008-07-30 08:49:36 +0000723 UNREACHABLE();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000724 return Failure::Exception();
725}
726
727
kasper.lund7276f142008-07-30 08:49:36 +0000728bool PagedSpace::IsUsed(Page* page) {
729 PageIterator it(this, PageIterator::PAGES_IN_USE);
730 while (it.has_next()) {
731 if (page == it.next()) return true;
732 }
733 return false;
734}
735
736
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000737void PagedSpace::SetAllocationInfo(AllocationInfo* alloc_info, Page* p) {
738 alloc_info->top = p->ObjectAreaStart();
739 alloc_info->limit = p->ObjectAreaEnd();
kasper.lund7276f142008-07-30 08:49:36 +0000740 ASSERT(alloc_info->VerifyPagedAllocation());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000741}
742
743
744void PagedSpace::MCResetRelocationInfo() {
745 // Set page indexes.
746 int i = 0;
747 PageIterator it(this, PageIterator::ALL_PAGES);
748 while (it.has_next()) {
749 Page* p = it.next();
750 p->mc_page_index = i++;
751 }
752
753 // Set mc_forwarding_info_ to the first page in the space.
754 SetAllocationInfo(&mc_forwarding_info_, first_page_);
755 // All the bytes in the space are 'available'. We will rediscover
756 // allocated and wasted bytes during GC.
757 accounting_stats_.Reset();
758}
759
760
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000761int PagedSpace::MCSpaceOffsetForAddress(Address addr) {
762#ifdef DEBUG
763 // The Contains function considers the address at the beginning of a
764 // page in the page, MCSpaceOffsetForAddress considers it is in the
765 // previous page.
766 if (Page::IsAlignedToPageSize(addr)) {
767 ASSERT(Contains(addr - kPointerSize));
768 } else {
769 ASSERT(Contains(addr));
770 }
771#endif
772
773 // If addr is at the end of a page, it belongs to previous page
774 Page* p = Page::IsAlignedToPageSize(addr)
775 ? Page::FromAllocationTop(addr)
776 : Page::FromAddress(addr);
777 int index = p->mc_page_index;
778 return (index * Page::kPageSize) + p->Offset(addr);
779}
780
781
kasper.lund7276f142008-07-30 08:49:36 +0000782// Slow case for reallocating and promoting objects during a compacting
783// collection. This function is not space-specific.
784HeapObject* PagedSpace::SlowMCAllocateRaw(int size_in_bytes) {
785 Page* current_page = TopPageOf(mc_forwarding_info_);
786 if (!current_page->next_page()->is_valid()) {
787 if (!Expand(current_page)) {
788 return NULL;
789 }
790 }
791
792 // There are surely more pages in the space now.
793 ASSERT(current_page->next_page()->is_valid());
794 // We do not add the top of page block for current page to the space's
795 // free list---the block may contain live objects so we cannot write
796 // bookkeeping information to it. Instead, we will recover top of page
797 // blocks when we move objects to their new locations.
798 //
799 // We do however write the allocation pointer to the page. The encoding
800 // of forwarding addresses is as an offset in terms of live bytes, so we
801 // need quick access to the allocation top of each page to decode
802 // forwarding addresses.
803 current_page->mc_relocation_top = mc_forwarding_info_.top;
804 SetAllocationInfo(&mc_forwarding_info_, current_page->next_page());
805 return AllocateLinearly(&mc_forwarding_info_, size_in_bytes);
806}
807
808
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000809bool PagedSpace::Expand(Page* last_page) {
810 ASSERT(max_capacity_ % Page::kObjectAreaSize == 0);
811 ASSERT(Capacity() % Page::kObjectAreaSize == 0);
812
813 if (Capacity() == max_capacity_) return false;
814
815 ASSERT(Capacity() < max_capacity_);
816 // Last page must be valid and its next page is invalid.
817 ASSERT(last_page->is_valid() && !last_page->next_page()->is_valid());
818
819 int available_pages = (max_capacity_ - Capacity()) / Page::kObjectAreaSize;
820 if (available_pages <= 0) return false;
821
822 int desired_pages = Min(available_pages, MemoryAllocator::kPagesPerChunk);
823 Page* p = MemoryAllocator::AllocatePages(desired_pages, &desired_pages, this);
824 if (!p->is_valid()) return false;
825
826 accounting_stats_.ExpandSpace(desired_pages * Page::kObjectAreaSize);
827 ASSERT(Capacity() <= max_capacity_);
828
829 MemoryAllocator::SetNextPage(last_page, p);
830
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000831 // Sequentially clear remembered set of new pages and and cache the
832 // new last page in the space.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000833 while (p->is_valid()) {
834 p->ClearRSet();
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000835 last_page_ = p;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000836 p = p->next_page();
837 }
838
839 return true;
840}
841
842
843#ifdef DEBUG
844int PagedSpace::CountTotalPages() {
845 int count = 0;
846 for (Page* p = first_page_; p->is_valid(); p = p->next_page()) {
847 count++;
848 }
849 return count;
850}
851#endif
852
853
854void PagedSpace::Shrink() {
855 // Release half of free pages.
856 Page* top_page = AllocationTopPage();
857 ASSERT(top_page->is_valid());
858
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000859 // Count the number of pages we would like to free.
860 int pages_to_free = 0;
861 for (Page* p = top_page->next_page(); p->is_valid(); p = p->next_page()) {
862 pages_to_free++;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000863 }
864
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000865 // Free pages after top_page.
866 Page* p = MemoryAllocator::FreePages(top_page->next_page());
867 MemoryAllocator::SetNextPage(top_page, p);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000868
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000869 // Find out how many pages we failed to free and update last_page_.
870 // Please note pages can only be freed in whole chunks.
871 last_page_ = top_page;
872 for (Page* p = top_page->next_page(); p->is_valid(); p = p->next_page()) {
873 pages_to_free--;
kasperl@chromium.orgb3284ad2009-05-18 06:12:45 +0000874 last_page_ = p;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000875 }
876
sgjesse@chromium.org911335c2009-08-19 12:59:44 +0000877 accounting_stats_.ShrinkSpace(pages_to_free * Page::kObjectAreaSize);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000878 ASSERT(Capacity() == CountTotalPages() * Page::kObjectAreaSize);
879}
880
881
882bool PagedSpace::EnsureCapacity(int capacity) {
883 if (Capacity() >= capacity) return true;
884
885 // Start from the allocation top and loop to the last page in the space.
886 Page* last_page = AllocationTopPage();
887 Page* next_page = last_page->next_page();
888 while (next_page->is_valid()) {
889 last_page = MemoryAllocator::FindLastPageInSameChunk(next_page);
890 next_page = last_page->next_page();
891 }
892
893 // Expand the space until it has the required capacity or expansion fails.
894 do {
895 if (!Expand(last_page)) return false;
896 ASSERT(last_page->next_page()->is_valid());
897 last_page =
898 MemoryAllocator::FindLastPageInSameChunk(last_page->next_page());
899 } while (Capacity() < capacity);
900
901 return true;
902}
903
904
905#ifdef DEBUG
906void PagedSpace::Print() { }
907#endif
908
909
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +0000910#ifdef DEBUG
911// We do not assume that the PageIterator works, because it depends on the
912// invariants we are checking during verification.
913void PagedSpace::Verify(ObjectVisitor* visitor) {
914 // The allocation pointer should be valid, and it should be in a page in the
915 // space.
916 ASSERT(allocation_info_.VerifyPagedAllocation());
917 Page* top_page = Page::FromAllocationTop(allocation_info_.top);
918 ASSERT(MemoryAllocator::IsPageInSpace(top_page, this));
919
920 // Loop over all the pages.
921 bool above_allocation_top = false;
922 Page* current_page = first_page_;
923 while (current_page->is_valid()) {
924 if (above_allocation_top) {
925 // We don't care what's above the allocation top.
926 } else {
927 // Unless this is the last page in the space containing allocated
928 // objects, the allocation top should be at a constant offset from the
929 // object area end.
930 Address top = current_page->AllocationTop();
931 if (current_page == top_page) {
932 ASSERT(top == allocation_info_.top);
933 // The next page will be above the allocation top.
934 above_allocation_top = true;
935 } else {
936 ASSERT(top == current_page->ObjectAreaEnd() - page_extra_);
937 }
938
939 // It should be packed with objects from the bottom to the top.
940 Address current = current_page->ObjectAreaStart();
941 while (current < top) {
942 HeapObject* object = HeapObject::FromAddress(current);
943
944 // The first word should be a map, and we expect all map pointers to
945 // be in map space.
946 Map* map = object->map();
947 ASSERT(map->IsMap());
948 ASSERT(Heap::map_space()->Contains(map));
949
950 // Perform space-specific object verification.
951 VerifyObject(object);
952
953 // The object itself should look OK.
954 object->Verify();
955
956 // All the interior pointers should be contained in the heap and
957 // have their remembered set bits set if required as determined
958 // by the visitor.
959 int size = object->Size();
christian.plesner.hansen@gmail.com2bc58ef2009-09-22 10:00:30 +0000960 object->IterateBody(map->instance_type(), size, visitor);
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +0000961
962 current += size;
963 }
964
965 // The allocation pointer should not be in the middle of an object.
966 ASSERT(current == top);
967 }
968
969 current_page = current_page->next_page();
970 }
971}
972#endif
973
974
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000975// -----------------------------------------------------------------------------
976// NewSpace implementation
977
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000978
979bool NewSpace::Setup(Address start, int size) {
980 // Setup new space based on the preallocated memory block defined by
981 // start and size. The provided space is divided into two semi-spaces.
982 // To support fast containment testing in the new space, the size of
983 // this chunk must be a power of two and it must be aligned to its size.
984 int initial_semispace_capacity = Heap::InitialSemiSpaceSize();
ager@chromium.org3811b432009-10-28 14:53:37 +0000985 int maximum_semispace_capacity = Heap::MaxSemiSpaceSize();
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +0000986
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000987 ASSERT(initial_semispace_capacity <= maximum_semispace_capacity);
988 ASSERT(IsPowerOf2(maximum_semispace_capacity));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +0000989
990 // Allocate and setup the histogram arrays if necessary.
991#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
992 allocated_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1);
993 promoted_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1);
994
995#define SET_NAME(name) allocated_histogram_[name].set_name(#name); \
996 promoted_histogram_[name].set_name(#name);
997 INSTANCE_TYPE_LIST(SET_NAME)
998#undef SET_NAME
999#endif
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001000
ager@chromium.org3811b432009-10-28 14:53:37 +00001001 ASSERT(size == 2 * Heap::ReservedSemiSpaceSize());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001002 ASSERT(IsAddressAligned(start, size, 0));
1003
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001004 if (!to_space_.Setup(start,
1005 initial_semispace_capacity,
1006 maximum_semispace_capacity)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001007 return false;
1008 }
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001009 if (!from_space_.Setup(start + maximum_semispace_capacity,
1010 initial_semispace_capacity,
1011 maximum_semispace_capacity)) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001012 return false;
1013 }
1014
1015 start_ = start;
1016 address_mask_ = ~(size - 1);
1017 object_mask_ = address_mask_ | kHeapObjectTag;
ager@chromium.org9085a012009-05-11 19:22:57 +00001018 object_expected_ = reinterpret_cast<uintptr_t>(start) | kHeapObjectTag;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001019
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001020 allocation_info_.top = to_space_.low();
1021 allocation_info_.limit = to_space_.high();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001022 mc_forwarding_info_.top = NULL;
1023 mc_forwarding_info_.limit = NULL;
1024
1025 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1026 return true;
1027}
1028
1029
1030void NewSpace::TearDown() {
1031#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
1032 if (allocated_histogram_) {
1033 DeleteArray(allocated_histogram_);
1034 allocated_histogram_ = NULL;
1035 }
1036 if (promoted_histogram_) {
1037 DeleteArray(promoted_histogram_);
1038 promoted_histogram_ = NULL;
1039 }
1040#endif
1041
1042 start_ = NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001043 allocation_info_.top = NULL;
1044 allocation_info_.limit = NULL;
1045 mc_forwarding_info_.top = NULL;
1046 mc_forwarding_info_.limit = NULL;
1047
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001048 to_space_.TearDown();
1049 from_space_.TearDown();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001050}
1051
1052
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +00001053#ifdef ENABLE_HEAP_PROTECTION
1054
1055void NewSpace::Protect() {
1056 MemoryAllocator::Protect(ToSpaceLow(), Capacity());
1057 MemoryAllocator::Protect(FromSpaceLow(), Capacity());
1058}
1059
1060
1061void NewSpace::Unprotect() {
1062 MemoryAllocator::Unprotect(ToSpaceLow(), Capacity(),
1063 to_space_.executable());
1064 MemoryAllocator::Unprotect(FromSpaceLow(), Capacity(),
1065 from_space_.executable());
1066}
1067
1068#endif
1069
1070
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001071void NewSpace::Flip() {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001072 SemiSpace tmp = from_space_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001073 from_space_ = to_space_;
1074 to_space_ = tmp;
1075}
1076
1077
ager@chromium.orgab99eea2009-08-25 07:05:41 +00001078void NewSpace::Grow() {
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001079 ASSERT(Capacity() < MaximumCapacity());
ager@chromium.orgab99eea2009-08-25 07:05:41 +00001080 if (to_space_.Grow()) {
1081 // Only grow from space if we managed to grow to space.
1082 if (!from_space_.Grow()) {
1083 // If we managed to grow to space but couldn't grow from space,
1084 // attempt to shrink to space.
1085 if (!to_space_.ShrinkTo(from_space_.Capacity())) {
1086 // We are in an inconsistent state because we could not
1087 // commit/uncommit memory from new space.
1088 V8::FatalProcessOutOfMemory("Failed to grow new space.");
1089 }
1090 }
1091 }
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001092 allocation_info_.limit = to_space_.high();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001093 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
ager@chromium.orgab99eea2009-08-25 07:05:41 +00001094}
1095
1096
1097void NewSpace::Shrink() {
1098 int new_capacity = Max(InitialCapacity(), 2 * Size());
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001099 int rounded_new_capacity =
1100 RoundUp(new_capacity, static_cast<int>(OS::AllocateAlignment()));
ager@chromium.orgab99eea2009-08-25 07:05:41 +00001101 if (rounded_new_capacity < Capacity() &&
1102 to_space_.ShrinkTo(rounded_new_capacity)) {
1103 // Only shrink from space if we managed to shrink to space.
1104 if (!from_space_.ShrinkTo(rounded_new_capacity)) {
1105 // If we managed to shrink to space but couldn't shrink from
1106 // space, attempt to grow to space again.
1107 if (!to_space_.GrowTo(from_space_.Capacity())) {
1108 // We are in an inconsistent state because we could not
1109 // commit/uncommit memory from new space.
1110 V8::FatalProcessOutOfMemory("Failed to shrink new space.");
1111 }
1112 }
1113 }
1114 allocation_info_.limit = to_space_.high();
1115 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001116}
1117
1118
1119void NewSpace::ResetAllocationInfo() {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001120 allocation_info_.top = to_space_.low();
1121 allocation_info_.limit = to_space_.high();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001122 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1123}
1124
1125
1126void NewSpace::MCResetRelocationInfo() {
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001127 mc_forwarding_info_.top = from_space_.low();
1128 mc_forwarding_info_.limit = from_space_.high();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001129 ASSERT_SEMISPACE_ALLOCATION_INFO(mc_forwarding_info_, from_space_);
1130}
1131
1132
1133void NewSpace::MCCommitRelocationInfo() {
1134 // Assumes that the spaces have been flipped so that mc_forwarding_info_ is
1135 // valid allocation info for the to space.
1136 allocation_info_.top = mc_forwarding_info_.top;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001137 allocation_info_.limit = to_space_.high();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001138 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1139}
1140
1141
1142#ifdef DEBUG
1143// We do not use the SemispaceIterator because verification doesn't assume
1144// that it works (it depends on the invariants we are checking).
1145void NewSpace::Verify() {
1146 // The allocation pointer should be in the space or at the very end.
1147 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1148
1149 // There should be objects packed in from the low address up to the
1150 // allocation pointer.
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001151 Address current = to_space_.low();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001152 while (current < top()) {
1153 HeapObject* object = HeapObject::FromAddress(current);
1154
1155 // The first word should be a map, and we expect all map pointers to
1156 // be in map space.
1157 Map* map = object->map();
1158 ASSERT(map->IsMap());
1159 ASSERT(Heap::map_space()->Contains(map));
1160
1161 // The object should not be code or a map.
1162 ASSERT(!object->IsMap());
1163 ASSERT(!object->IsCode());
1164
1165 // The object itself should look OK.
1166 object->Verify();
1167
1168 // All the interior pointers should be contained in the heap.
1169 VerifyPointersVisitor visitor;
1170 int size = object->Size();
1171 object->IterateBody(map->instance_type(), size, &visitor);
1172
1173 current += size;
1174 }
1175
1176 // The allocation pointer should not be in the middle of an object.
1177 ASSERT(current == top());
1178}
1179#endif
1180
1181
ager@chromium.orgadd848f2009-08-13 12:44:13 +00001182bool SemiSpace::Commit() {
1183 ASSERT(!is_committed());
1184 if (!MemoryAllocator::CommitBlock(start_, capacity_, executable())) {
1185 return false;
1186 }
1187 committed_ = true;
1188 return true;
1189}
1190
1191
1192bool SemiSpace::Uncommit() {
1193 ASSERT(is_committed());
1194 if (!MemoryAllocator::UncommitBlock(start_, capacity_)) {
1195 return false;
1196 }
1197 committed_ = false;
1198 return true;
1199}
1200
1201
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001202// -----------------------------------------------------------------------------
1203// SemiSpace implementation
1204
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001205bool SemiSpace::Setup(Address start,
1206 int initial_capacity,
1207 int maximum_capacity) {
1208 // Creates a space in the young generation. The constructor does not
1209 // allocate memory from the OS. A SemiSpace is given a contiguous chunk of
1210 // memory of size 'capacity' when set up, and does not grow or shrink
1211 // otherwise. In the mark-compact collector, the memory region of the from
1212 // space is used as the marking stack. It requires contiguous memory
1213 // addresses.
ager@chromium.orgab99eea2009-08-25 07:05:41 +00001214 initial_capacity_ = initial_capacity;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001215 capacity_ = initial_capacity;
1216 maximum_capacity_ = maximum_capacity;
ager@chromium.orgadd848f2009-08-13 12:44:13 +00001217 committed_ = false;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001218
1219 start_ = start;
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001220 address_mask_ = ~(maximum_capacity - 1);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001221 object_mask_ = address_mask_ | kHeapObjectTag;
ager@chromium.org9085a012009-05-11 19:22:57 +00001222 object_expected_ = reinterpret_cast<uintptr_t>(start) | kHeapObjectTag;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001223 age_mark_ = start_;
ager@chromium.orgadd848f2009-08-13 12:44:13 +00001224
1225 return Commit();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001226}
1227
1228
1229void SemiSpace::TearDown() {
1230 start_ = NULL;
1231 capacity_ = 0;
1232}
1233
1234
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00001235bool SemiSpace::Grow() {
sgjesse@chromium.orgc81c8942009-08-21 10:54:26 +00001236 // Double the semispace size but only up to maximum capacity.
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001237 int maximum_extra = maximum_capacity_ - capacity_;
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001238 int extra = Min(RoundUp(capacity_, static_cast<int>(OS::AllocateAlignment())),
sgjesse@chromium.org911335c2009-08-19 12:59:44 +00001239 maximum_extra);
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00001240 if (!MemoryAllocator::CommitBlock(high(), extra, executable())) {
kasper.lund7276f142008-07-30 08:49:36 +00001241 return false;
1242 }
christian.plesner.hansen@gmail.com5a6af922009-08-12 14:20:51 +00001243 capacity_ += extra;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001244 return true;
1245}
1246
1247
ager@chromium.orgab99eea2009-08-25 07:05:41 +00001248bool SemiSpace::GrowTo(int new_capacity) {
1249 ASSERT(new_capacity <= maximum_capacity_);
1250 ASSERT(new_capacity > capacity_);
1251 size_t delta = new_capacity - capacity_;
1252 ASSERT(IsAligned(delta, OS::AllocateAlignment()));
1253 if (!MemoryAllocator::CommitBlock(high(), delta, executable())) {
1254 return false;
1255 }
1256 capacity_ = new_capacity;
1257 return true;
1258}
1259
1260
1261bool SemiSpace::ShrinkTo(int new_capacity) {
1262 ASSERT(new_capacity >= initial_capacity_);
1263 ASSERT(new_capacity < capacity_);
1264 size_t delta = capacity_ - new_capacity;
1265 ASSERT(IsAligned(delta, OS::AllocateAlignment()));
1266 if (!MemoryAllocator::UncommitBlock(high() - delta, delta)) {
1267 return false;
1268 }
1269 capacity_ = new_capacity;
1270 return true;
1271}
1272
1273
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001274#ifdef DEBUG
1275void SemiSpace::Print() { }
ager@chromium.org9258b6b2008-09-11 09:11:10 +00001276
1277
1278void SemiSpace::Verify() { }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001279#endif
1280
1281
1282// -----------------------------------------------------------------------------
1283// SemiSpaceIterator implementation.
1284SemiSpaceIterator::SemiSpaceIterator(NewSpace* space) {
1285 Initialize(space, space->bottom(), space->top(), NULL);
1286}
1287
1288
1289SemiSpaceIterator::SemiSpaceIterator(NewSpace* space,
1290 HeapObjectCallback size_func) {
1291 Initialize(space, space->bottom(), space->top(), size_func);
1292}
1293
1294
1295SemiSpaceIterator::SemiSpaceIterator(NewSpace* space, Address start) {
1296 Initialize(space, start, space->top(), NULL);
1297}
1298
1299
1300void SemiSpaceIterator::Initialize(NewSpace* space, Address start,
1301 Address end,
1302 HeapObjectCallback size_func) {
1303 ASSERT(space->ToSpaceContains(start));
1304 ASSERT(space->ToSpaceLow() <= end
1305 && end <= space->ToSpaceHigh());
kasperl@chromium.org5a8ca6c2008-10-23 13:57:19 +00001306 space_ = &space->to_space_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001307 current_ = start;
1308 limit_ = end;
1309 size_func_ = size_func;
1310}
1311
1312
1313#ifdef DEBUG
1314// A static array of histogram info for each type.
1315static HistogramInfo heap_histograms[LAST_TYPE+1];
1316static JSObject::SpillInformation js_spill_information;
1317
1318// heap_histograms is shared, always clear it before using it.
1319static void ClearHistograms() {
1320 // We reset the name each time, though it hasn't changed.
1321#define DEF_TYPE_NAME(name) heap_histograms[name].set_name(#name);
1322 INSTANCE_TYPE_LIST(DEF_TYPE_NAME)
1323#undef DEF_TYPE_NAME
1324
1325#define CLEAR_HISTOGRAM(name) heap_histograms[name].clear();
1326 INSTANCE_TYPE_LIST(CLEAR_HISTOGRAM)
1327#undef CLEAR_HISTOGRAM
1328
1329 js_spill_information.Clear();
1330}
1331
1332
1333static int code_kind_statistics[Code::NUMBER_OF_KINDS];
1334
1335
1336static void ClearCodeKindStatistics() {
1337 for (int i = 0; i < Code::NUMBER_OF_KINDS; i++) {
1338 code_kind_statistics[i] = 0;
1339 }
1340}
1341
1342
1343static void ReportCodeKindStatistics() {
1344 const char* table[Code::NUMBER_OF_KINDS];
1345
1346#define CASE(name) \
1347 case Code::name: table[Code::name] = #name; \
1348 break
1349
1350 for (int i = 0; i < Code::NUMBER_OF_KINDS; i++) {
1351 switch (static_cast<Code::Kind>(i)) {
1352 CASE(FUNCTION);
1353 CASE(STUB);
1354 CASE(BUILTIN);
1355 CASE(LOAD_IC);
1356 CASE(KEYED_LOAD_IC);
1357 CASE(STORE_IC);
1358 CASE(KEYED_STORE_IC);
1359 CASE(CALL_IC);
1360 }
1361 }
1362
1363#undef CASE
1364
1365 PrintF("\n Code kind histograms: \n");
1366 for (int i = 0; i < Code::NUMBER_OF_KINDS; i++) {
1367 if (code_kind_statistics[i] > 0) {
1368 PrintF(" %-20s: %10d bytes\n", table[i], code_kind_statistics[i]);
1369 }
1370 }
1371 PrintF("\n");
1372}
1373
1374
1375static int CollectHistogramInfo(HeapObject* obj) {
1376 InstanceType type = obj->map()->instance_type();
1377 ASSERT(0 <= type && type <= LAST_TYPE);
1378 ASSERT(heap_histograms[type].name() != NULL);
1379 heap_histograms[type].increment_number(1);
1380 heap_histograms[type].increment_bytes(obj->Size());
1381
1382 if (FLAG_collect_heap_spill_statistics && obj->IsJSObject()) {
1383 JSObject::cast(obj)->IncrementSpillStatistics(&js_spill_information);
1384 }
1385
1386 return obj->Size();
1387}
1388
1389
1390static void ReportHistogram(bool print_spill) {
1391 PrintF("\n Object Histogram:\n");
1392 for (int i = 0; i <= LAST_TYPE; i++) {
1393 if (heap_histograms[i].number() > 0) {
1394 PrintF(" %-33s%10d (%10d bytes)\n",
1395 heap_histograms[i].name(),
1396 heap_histograms[i].number(),
1397 heap_histograms[i].bytes());
1398 }
1399 }
1400 PrintF("\n");
1401
1402 // Summarize string types.
1403 int string_number = 0;
1404 int string_bytes = 0;
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001405#define INCREMENT(type, size, name, camel_name) \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001406 string_number += heap_histograms[type].number(); \
1407 string_bytes += heap_histograms[type].bytes();
1408 STRING_TYPE_LIST(INCREMENT)
1409#undef INCREMENT
1410 if (string_number > 0) {
1411 PrintF(" %-33s%10d (%10d bytes)\n\n", "STRING_TYPE", string_number,
1412 string_bytes);
1413 }
1414
1415 if (FLAG_collect_heap_spill_statistics && print_spill) {
1416 js_spill_information.Print();
1417 }
1418}
1419#endif // DEBUG
1420
1421
1422// Support for statistics gathering for --heap-stats and --log-gc.
1423#if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
1424void NewSpace::ClearHistograms() {
1425 for (int i = 0; i <= LAST_TYPE; i++) {
1426 allocated_histogram_[i].clear();
1427 promoted_histogram_[i].clear();
1428 }
1429}
1430
1431// Because the copying collector does not touch garbage objects, we iterate
1432// the new space before a collection to get a histogram of allocated objects.
1433// This only happens (1) when compiled with DEBUG and the --heap-stats flag is
1434// set, or when compiled with ENABLE_LOGGING_AND_PROFILING and the --log-gc
1435// flag is set.
1436void NewSpace::CollectStatistics() {
1437 ClearHistograms();
1438 SemiSpaceIterator it(this);
1439 while (it.has_next()) RecordAllocation(it.next());
1440}
1441
1442
1443#ifdef ENABLE_LOGGING_AND_PROFILING
1444static void DoReportStatistics(HistogramInfo* info, const char* description) {
1445 LOG(HeapSampleBeginEvent("NewSpace", description));
1446 // Lump all the string types together.
1447 int string_number = 0;
1448 int string_bytes = 0;
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001449#define INCREMENT(type, size, name, camel_name) \
1450 string_number += info[type].number(); \
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001451 string_bytes += info[type].bytes();
1452 STRING_TYPE_LIST(INCREMENT)
1453#undef INCREMENT
1454 if (string_number > 0) {
1455 LOG(HeapSampleItemEvent("STRING_TYPE", string_number, string_bytes));
1456 }
1457
1458 // Then do the other types.
1459 for (int i = FIRST_NONSTRING_TYPE; i <= LAST_TYPE; ++i) {
1460 if (info[i].number() > 0) {
1461 LOG(HeapSampleItemEvent(info[i].name(), info[i].number(),
1462 info[i].bytes()));
1463 }
1464 }
1465 LOG(HeapSampleEndEvent("NewSpace", description));
1466}
1467#endif // ENABLE_LOGGING_AND_PROFILING
1468
1469
1470void NewSpace::ReportStatistics() {
1471#ifdef DEBUG
1472 if (FLAG_heap_stats) {
1473 float pct = static_cast<float>(Available()) / Capacity();
1474 PrintF(" capacity: %d, available: %d, %%%d\n",
1475 Capacity(), Available(), static_cast<int>(pct*100));
1476 PrintF("\n Object Histogram:\n");
1477 for (int i = 0; i <= LAST_TYPE; i++) {
1478 if (allocated_histogram_[i].number() > 0) {
1479 PrintF(" %-33s%10d (%10d bytes)\n",
1480 allocated_histogram_[i].name(),
1481 allocated_histogram_[i].number(),
1482 allocated_histogram_[i].bytes());
1483 }
1484 }
1485 PrintF("\n");
1486 }
1487#endif // DEBUG
1488
1489#ifdef ENABLE_LOGGING_AND_PROFILING
1490 if (FLAG_log_gc) {
1491 DoReportStatistics(allocated_histogram_, "allocated");
1492 DoReportStatistics(promoted_histogram_, "promoted");
1493 }
1494#endif // ENABLE_LOGGING_AND_PROFILING
1495}
1496
1497
1498void NewSpace::RecordAllocation(HeapObject* obj) {
1499 InstanceType type = obj->map()->instance_type();
1500 ASSERT(0 <= type && type <= LAST_TYPE);
1501 allocated_histogram_[type].increment_number(1);
1502 allocated_histogram_[type].increment_bytes(obj->Size());
1503}
1504
1505
1506void NewSpace::RecordPromotion(HeapObject* obj) {
1507 InstanceType type = obj->map()->instance_type();
1508 ASSERT(0 <= type && type <= LAST_TYPE);
1509 promoted_histogram_[type].increment_number(1);
1510 promoted_histogram_[type].increment_bytes(obj->Size());
1511}
1512#endif // defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
1513
1514
1515// -----------------------------------------------------------------------------
1516// Free lists for old object spaces implementation
1517
1518void FreeListNode::set_size(int size_in_bytes) {
1519 ASSERT(size_in_bytes > 0);
1520 ASSERT(IsAligned(size_in_bytes, kPointerSize));
1521
1522 // We write a map and possibly size information to the block. If the block
1523 // is big enough to be a ByteArray with at least one extra word (the next
1524 // pointer), we set its map to be the byte array map and its size to an
1525 // appropriate array length for the desired size from HeapObject::Size().
1526 // If the block is too small (eg, one or two words), to hold both a size
1527 // field and a next pointer, we give it a filler map that gives it the
1528 // correct size.
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001529 if (size_in_bytes > ByteArray::kAlignedSize) {
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00001530 set_map(Heap::raw_unchecked_byte_array_map());
ager@chromium.org3811b432009-10-28 14:53:37 +00001531 // Can't use ByteArray::cast because it fails during deserialization.
1532 ByteArray* this_as_byte_array = reinterpret_cast<ByteArray*>(this);
1533 this_as_byte_array->set_length(ByteArray::LengthFor(size_in_bytes));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001534 } else if (size_in_bytes == kPointerSize) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001535 set_map(Heap::raw_unchecked_one_pointer_filler_map());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001536 } else if (size_in_bytes == 2 * kPointerSize) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001537 set_map(Heap::raw_unchecked_two_pointer_filler_map());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001538 } else {
1539 UNREACHABLE();
1540 }
ager@chromium.org3811b432009-10-28 14:53:37 +00001541 // We would like to ASSERT(Size() == size_in_bytes) but this would fail during
1542 // deserialization because the byte array map is not done yet.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001543}
1544
1545
1546Address FreeListNode::next() {
ager@chromium.org3811b432009-10-28 14:53:37 +00001547 ASSERT(IsFreeListNode(this));
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001548 if (map() == Heap::raw_unchecked_byte_array_map()) {
1549 ASSERT(Size() >= kNextOffset + kPointerSize);
1550 return Memory::Address_at(address() + kNextOffset);
1551 } else {
1552 return Memory::Address_at(address() + kPointerSize);
1553 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001554}
1555
1556
1557void FreeListNode::set_next(Address next) {
ager@chromium.org3811b432009-10-28 14:53:37 +00001558 ASSERT(IsFreeListNode(this));
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001559 if (map() == Heap::raw_unchecked_byte_array_map()) {
1560 ASSERT(Size() >= kNextOffset + kPointerSize);
1561 Memory::Address_at(address() + kNextOffset) = next;
1562 } else {
1563 Memory::Address_at(address() + kPointerSize) = next;
1564 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001565}
1566
1567
1568OldSpaceFreeList::OldSpaceFreeList(AllocationSpace owner) : owner_(owner) {
1569 Reset();
1570}
1571
1572
1573void OldSpaceFreeList::Reset() {
1574 available_ = 0;
1575 for (int i = 0; i < kFreeListsLength; i++) {
1576 free_[i].head_node_ = NULL;
1577 }
1578 needs_rebuild_ = false;
1579 finger_ = kHead;
1580 free_[kHead].next_size_ = kEnd;
1581}
1582
1583
1584void OldSpaceFreeList::RebuildSizeList() {
1585 ASSERT(needs_rebuild_);
1586 int cur = kHead;
1587 for (int i = cur + 1; i < kFreeListsLength; i++) {
1588 if (free_[i].head_node_ != NULL) {
1589 free_[cur].next_size_ = i;
1590 cur = i;
1591 }
1592 }
1593 free_[cur].next_size_ = kEnd;
1594 needs_rebuild_ = false;
1595}
1596
1597
1598int OldSpaceFreeList::Free(Address start, int size_in_bytes) {
1599#ifdef DEBUG
1600 for (int i = 0; i < size_in_bytes; i += kPointerSize) {
1601 Memory::Address_at(start + i) = kZapValue;
1602 }
1603#endif
1604 FreeListNode* node = FreeListNode::FromAddress(start);
1605 node->set_size(size_in_bytes);
1606
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001607 // We don't use the freelists in compacting mode. This makes it more like a
1608 // GC that only has mark-sweep-compact and doesn't have a mark-sweep
1609 // collector.
1610 if (FLAG_always_compact) {
1611 return size_in_bytes;
1612 }
1613
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001614 // Early return to drop too-small blocks on the floor (one or two word
1615 // blocks cannot hold a map pointer, a size field, and a pointer to the
1616 // next block in the free list).
1617 if (size_in_bytes < kMinBlockSize) {
1618 return size_in_bytes;
1619 }
1620
1621 // Insert other blocks at the head of an exact free list.
1622 int index = size_in_bytes >> kPointerSizeLog2;
1623 node->set_next(free_[index].head_node_);
1624 free_[index].head_node_ = node->address();
1625 available_ += size_in_bytes;
1626 needs_rebuild_ = true;
1627 return 0;
1628}
1629
1630
1631Object* OldSpaceFreeList::Allocate(int size_in_bytes, int* wasted_bytes) {
1632 ASSERT(0 < size_in_bytes);
1633 ASSERT(size_in_bytes <= kMaxBlockSize);
1634 ASSERT(IsAligned(size_in_bytes, kPointerSize));
1635
1636 if (needs_rebuild_) RebuildSizeList();
1637 int index = size_in_bytes >> kPointerSizeLog2;
1638 // Check for a perfect fit.
1639 if (free_[index].head_node_ != NULL) {
1640 FreeListNode* node = FreeListNode::FromAddress(free_[index].head_node_);
1641 // If this was the last block of its size, remove the size.
1642 if ((free_[index].head_node_ = node->next()) == NULL) RemoveSize(index);
1643 available_ -= size_in_bytes;
1644 *wasted_bytes = 0;
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001645 ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001646 return node;
1647 }
1648 // Search the size list for the best fit.
1649 int prev = finger_ < index ? finger_ : kHead;
1650 int cur = FindSize(index, &prev);
1651 ASSERT(index < cur);
1652 if (cur == kEnd) {
1653 // No large enough size in list.
1654 *wasted_bytes = 0;
1655 return Failure::RetryAfterGC(size_in_bytes, owner_);
1656 }
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001657 ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001658 int rem = cur - index;
1659 int rem_bytes = rem << kPointerSizeLog2;
1660 FreeListNode* cur_node = FreeListNode::FromAddress(free_[cur].head_node_);
kasper.lund7276f142008-07-30 08:49:36 +00001661 ASSERT(cur_node->Size() == (cur << kPointerSizeLog2));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001662 FreeListNode* rem_node = FreeListNode::FromAddress(free_[cur].head_node_ +
1663 size_in_bytes);
1664 // Distinguish the cases prev < rem < cur and rem <= prev < cur
1665 // to avoid many redundant tests and calls to Insert/RemoveSize.
1666 if (prev < rem) {
1667 // Simple case: insert rem between prev and cur.
1668 finger_ = prev;
1669 free_[prev].next_size_ = rem;
1670 // If this was the last block of size cur, remove the size.
1671 if ((free_[cur].head_node_ = cur_node->next()) == NULL) {
1672 free_[rem].next_size_ = free_[cur].next_size_;
1673 } else {
1674 free_[rem].next_size_ = cur;
1675 }
1676 // Add the remainder block.
1677 rem_node->set_size(rem_bytes);
1678 rem_node->set_next(free_[rem].head_node_);
1679 free_[rem].head_node_ = rem_node->address();
1680 } else {
1681 // If this was the last block of size cur, remove the size.
1682 if ((free_[cur].head_node_ = cur_node->next()) == NULL) {
1683 finger_ = prev;
1684 free_[prev].next_size_ = free_[cur].next_size_;
1685 }
1686 if (rem_bytes < kMinBlockSize) {
1687 // Too-small remainder is wasted.
1688 rem_node->set_size(rem_bytes);
1689 available_ -= size_in_bytes + rem_bytes;
1690 *wasted_bytes = rem_bytes;
1691 return cur_node;
1692 }
1693 // Add the remainder block and, if needed, insert its size.
1694 rem_node->set_size(rem_bytes);
1695 rem_node->set_next(free_[rem].head_node_);
1696 free_[rem].head_node_ = rem_node->address();
1697 if (rem_node->next() == NULL) InsertSize(rem);
1698 }
1699 available_ -= size_in_bytes;
1700 *wasted_bytes = 0;
1701 return cur_node;
1702}
1703
1704
kasper.lund7276f142008-07-30 08:49:36 +00001705#ifdef DEBUG
1706bool OldSpaceFreeList::Contains(FreeListNode* node) {
1707 for (int i = 0; i < kFreeListsLength; i++) {
1708 Address cur_addr = free_[i].head_node_;
1709 while (cur_addr != NULL) {
1710 FreeListNode* cur_node = FreeListNode::FromAddress(cur_addr);
1711 if (cur_node == node) return true;
1712 cur_addr = cur_node->next();
1713 }
1714 }
1715 return false;
1716}
1717#endif
1718
1719
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001720FixedSizeFreeList::FixedSizeFreeList(AllocationSpace owner, int object_size)
1721 : owner_(owner), object_size_(object_size) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001722 Reset();
1723}
1724
1725
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001726void FixedSizeFreeList::Reset() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001727 available_ = 0;
1728 head_ = NULL;
1729}
1730
1731
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001732void FixedSizeFreeList::Free(Address start) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001733#ifdef DEBUG
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001734 for (int i = 0; i < object_size_; i += kPointerSize) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001735 Memory::Address_at(start + i) = kZapValue;
1736 }
1737#endif
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001738 ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001739 FreeListNode* node = FreeListNode::FromAddress(start);
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001740 node->set_size(object_size_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001741 node->set_next(head_);
1742 head_ = node->address();
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001743 available_ += object_size_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001744}
1745
1746
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001747Object* FixedSizeFreeList::Allocate() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001748 if (head_ == NULL) {
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001749 return Failure::RetryAfterGC(object_size_, owner_);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001750 }
1751
sgjesse@chromium.org755c5b12009-05-29 11:04:38 +00001752 ASSERT(!FLAG_always_compact); // We only use the freelists with mark-sweep.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001753 FreeListNode* node = FreeListNode::FromAddress(head_);
1754 head_ = node->next();
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00001755 available_ -= object_size_;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001756 return node;
1757}
1758
1759
1760// -----------------------------------------------------------------------------
1761// OldSpace implementation
1762
1763void OldSpace::PrepareForMarkCompact(bool will_compact) {
1764 if (will_compact) {
1765 // Reset relocation info. During a compacting collection, everything in
1766 // the space is considered 'available' and we will rediscover live data
1767 // and waste during the collection.
1768 MCResetRelocationInfo();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001769 ASSERT(Available() == Capacity());
1770 } else {
1771 // During a non-compacting collection, everything below the linear
1772 // allocation pointer is considered allocated (everything above is
1773 // available) and we will rediscover available and wasted bytes during
1774 // the collection.
1775 accounting_stats_.AllocateBytes(free_list_.available());
1776 accounting_stats_.FillWastedBytes(Waste());
1777 }
1778
kasper.lund7276f142008-07-30 08:49:36 +00001779 // Clear the free list before a full GC---it will be rebuilt afterward.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001780 free_list_.Reset();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001781}
1782
1783
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001784void OldSpace::MCCommitRelocationInfo() {
1785 // Update fast allocation info.
1786 allocation_info_.top = mc_forwarding_info_.top;
1787 allocation_info_.limit = mc_forwarding_info_.limit;
kasper.lund7276f142008-07-30 08:49:36 +00001788 ASSERT(allocation_info_.VerifyPagedAllocation());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001789
1790 // The space is compacted and we haven't yet built free lists or
1791 // wasted any space.
1792 ASSERT(Waste() == 0);
1793 ASSERT(AvailableFree() == 0);
1794
1795 // Build the free list for the space.
1796 int computed_size = 0;
1797 PageIterator it(this, PageIterator::PAGES_USED_BY_MC);
1798 while (it.has_next()) {
1799 Page* p = it.next();
1800 // Space below the relocation pointer is allocated.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001801 computed_size +=
1802 static_cast<int>(p->mc_relocation_top - p->ObjectAreaStart());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001803 if (it.has_next()) {
1804 // Free the space at the top of the page. We cannot use
1805 // p->mc_relocation_top after the call to Free (because Free will clear
1806 // remembered set bits).
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001807 int extra_size =
1808 static_cast<int>(p->ObjectAreaEnd() - p->mc_relocation_top);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001809 if (extra_size > 0) {
1810 int wasted_bytes = free_list_.Free(p->mc_relocation_top, extra_size);
1811 // The bytes we have just "freed" to add to the free list were
1812 // already accounted as available.
1813 accounting_stats_.WasteBytes(wasted_bytes);
1814 }
1815 }
1816 }
1817
1818 // Make sure the computed size - based on the used portion of the pages in
1819 // use - matches the size obtained while computing forwarding addresses.
1820 ASSERT(computed_size == Size());
1821}
1822
1823
kasper.lund7276f142008-07-30 08:49:36 +00001824// Slow case for normal allocation. Try in order: (1) allocate in the next
1825// page in the space, (2) allocate off the space's free list, (3) expand the
1826// space, (4) fail.
1827HeapObject* OldSpace::SlowAllocateRaw(int size_in_bytes) {
1828 // Linear allocation in this space has failed. If there is another page
1829 // in the space, move to that page and allocate there. This allocation
1830 // should succeed (size_in_bytes should not be greater than a page's
1831 // object area size).
1832 Page* current_page = TopPageOf(allocation_info_);
1833 if (current_page->next_page()->is_valid()) {
1834 return AllocateInNextPage(current_page, size_in_bytes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001835 }
kasper.lund7276f142008-07-30 08:49:36 +00001836
ager@chromium.org3811b432009-10-28 14:53:37 +00001837 // There is no next page in this space. Try free list allocation unless that
1838 // is currently forbidden.
1839 if (!Heap::linear_allocation()) {
1840 int wasted_bytes;
1841 Object* result = free_list_.Allocate(size_in_bytes, &wasted_bytes);
1842 accounting_stats_.WasteBytes(wasted_bytes);
1843 if (!result->IsFailure()) {
1844 accounting_stats_.AllocateBytes(size_in_bytes);
1845 return HeapObject::cast(result);
1846 }
kasper.lund7276f142008-07-30 08:49:36 +00001847 }
1848
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00001849 // Free list allocation failed and there is no next page. Fail if we have
1850 // hit the old generation size limit that should cause a garbage
1851 // collection.
1852 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) {
1853 return NULL;
1854 }
1855
1856 // Try to expand the space and allocate in the new next page.
kasper.lund7276f142008-07-30 08:49:36 +00001857 ASSERT(!current_page->next_page()->is_valid());
1858 if (Expand(current_page)) {
1859 return AllocateInNextPage(current_page, size_in_bytes);
1860 }
1861
1862 // Finally, fail.
1863 return NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001864}
1865
1866
kasper.lund7276f142008-07-30 08:49:36 +00001867// Add the block at the top of the page to the space's free list, set the
1868// allocation info to the next page (assumed to be one), and allocate
1869// linearly there.
1870HeapObject* OldSpace::AllocateInNextPage(Page* current_page,
1871 int size_in_bytes) {
1872 ASSERT(current_page->next_page()->is_valid());
1873 // Add the block at the top of this page to the free list.
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001874 int free_size =
1875 static_cast<int>(current_page->ObjectAreaEnd() - allocation_info_.top);
kasper.lund7276f142008-07-30 08:49:36 +00001876 if (free_size > 0) {
1877 int wasted_bytes = free_list_.Free(allocation_info_.top, free_size);
1878 accounting_stats_.WasteBytes(wasted_bytes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001879 }
kasper.lund7276f142008-07-30 08:49:36 +00001880 SetAllocationInfo(&allocation_info_, current_page->next_page());
1881 return AllocateLinearly(&allocation_info_, size_in_bytes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001882}
1883
1884
1885#ifdef DEBUG
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001886struct CommentStatistic {
1887 const char* comment;
1888 int size;
1889 int count;
1890 void Clear() {
1891 comment = NULL;
1892 size = 0;
1893 count = 0;
1894 }
1895};
1896
1897
1898// must be small, since an iteration is used for lookup
1899const int kMaxComments = 64;
1900static CommentStatistic comments_statistics[kMaxComments+1];
1901
1902
1903void PagedSpace::ReportCodeStatistics() {
1904 ReportCodeKindStatistics();
1905 PrintF("Code comment statistics (\" [ comment-txt : size/ "
1906 "count (average)\"):\n");
1907 for (int i = 0; i <= kMaxComments; i++) {
1908 const CommentStatistic& cs = comments_statistics[i];
1909 if (cs.size > 0) {
1910 PrintF(" %-30s: %10d/%6d (%d)\n", cs.comment, cs.size, cs.count,
1911 cs.size/cs.count);
1912 }
1913 }
1914 PrintF("\n");
1915}
1916
1917
1918void PagedSpace::ResetCodeStatistics() {
1919 ClearCodeKindStatistics();
1920 for (int i = 0; i < kMaxComments; i++) comments_statistics[i].Clear();
1921 comments_statistics[kMaxComments].comment = "Unknown";
1922 comments_statistics[kMaxComments].size = 0;
1923 comments_statistics[kMaxComments].count = 0;
1924}
1925
1926
1927// Adds comment to 'comment_statistics' table. Performance OK sa long as
1928// 'kMaxComments' is small
1929static void EnterComment(const char* comment, int delta) {
1930 // Do not count empty comments
1931 if (delta <= 0) return;
1932 CommentStatistic* cs = &comments_statistics[kMaxComments];
1933 // Search for a free or matching entry in 'comments_statistics': 'cs'
1934 // points to result.
1935 for (int i = 0; i < kMaxComments; i++) {
1936 if (comments_statistics[i].comment == NULL) {
1937 cs = &comments_statistics[i];
1938 cs->comment = comment;
1939 break;
1940 } else if (strcmp(comments_statistics[i].comment, comment) == 0) {
1941 cs = &comments_statistics[i];
1942 break;
1943 }
1944 }
1945 // Update entry for 'comment'
1946 cs->size += delta;
1947 cs->count += 1;
1948}
1949
1950
1951// Call for each nested comment start (start marked with '[ xxx', end marked
1952// with ']'. RelocIterator 'it' must point to a comment reloc info.
1953static void CollectCommentStatistics(RelocIterator* it) {
1954 ASSERT(!it->done());
ager@chromium.org236ad962008-09-25 09:45:57 +00001955 ASSERT(it->rinfo()->rmode() == RelocInfo::COMMENT);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001956 const char* tmp = reinterpret_cast<const char*>(it->rinfo()->data());
1957 if (tmp[0] != '[') {
1958 // Not a nested comment; skip
1959 return;
1960 }
1961
1962 // Search for end of nested comment or a new nested comment
1963 const char* const comment_txt =
1964 reinterpret_cast<const char*>(it->rinfo()->data());
1965 const byte* prev_pc = it->rinfo()->pc();
1966 int flat_delta = 0;
1967 it->next();
1968 while (true) {
1969 // All nested comments must be terminated properly, and therefore exit
1970 // from loop.
1971 ASSERT(!it->done());
ager@chromium.org236ad962008-09-25 09:45:57 +00001972 if (it->rinfo()->rmode() == RelocInfo::COMMENT) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001973 const char* const txt =
1974 reinterpret_cast<const char*>(it->rinfo()->data());
ager@chromium.orgc4c92722009-11-18 14:12:51 +00001975 flat_delta += static_cast<int>(it->rinfo()->pc() - prev_pc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00001976 if (txt[0] == ']') break; // End of nested comment
1977 // A new comment
1978 CollectCommentStatistics(it);
1979 // Skip code that was covered with previous comment
1980 prev_pc = it->rinfo()->pc();
1981 }
1982 it->next();
1983 }
1984 EnterComment(comment_txt, flat_delta);
1985}
1986
1987
1988// Collects code size statistics:
1989// - by code kind
1990// - by code comment
1991void PagedSpace::CollectCodeStatistics() {
1992 HeapObjectIterator obj_it(this);
1993 while (obj_it.has_next()) {
1994 HeapObject* obj = obj_it.next();
1995 if (obj->IsCode()) {
1996 Code* code = Code::cast(obj);
1997 code_kind_statistics[code->kind()] += code->Size();
1998 RelocIterator it(code);
1999 int delta = 0;
2000 const byte* prev_pc = code->instruction_start();
2001 while (!it.done()) {
ager@chromium.org236ad962008-09-25 09:45:57 +00002002 if (it.rinfo()->rmode() == RelocInfo::COMMENT) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002003 delta += static_cast<int>(it.rinfo()->pc() - prev_pc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002004 CollectCommentStatistics(&it);
2005 prev_pc = it.rinfo()->pc();
2006 }
2007 it.next();
2008 }
2009
2010 ASSERT(code->instruction_start() <= prev_pc &&
2011 prev_pc <= code->relocation_start());
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002012 delta += static_cast<int>(code->relocation_start() - prev_pc);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002013 EnterComment("NoComment", delta);
2014 }
2015 }
2016}
2017
2018
2019void OldSpace::ReportStatistics() {
2020 int pct = Available() * 100 / Capacity();
2021 PrintF(" capacity: %d, waste: %d, available: %d, %%%d\n",
2022 Capacity(), Waste(), Available(), pct);
2023
2024 // Report remembered set statistics.
2025 int rset_marked_pointers = 0;
2026 int rset_marked_arrays = 0;
2027 int rset_marked_array_elements = 0;
2028 int cross_gen_pointers = 0;
2029 int cross_gen_array_elements = 0;
2030
2031 PageIterator page_it(this, PageIterator::PAGES_IN_USE);
2032 while (page_it.has_next()) {
2033 Page* p = page_it.next();
2034
2035 for (Address rset_addr = p->RSetStart();
2036 rset_addr < p->RSetEnd();
2037 rset_addr += kIntSize) {
2038 int rset = Memory::int_at(rset_addr);
2039 if (rset != 0) {
2040 // Bits were set
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002041 int intoff =
2042 static_cast<int>(rset_addr - p->address() - Page::kRSetOffset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002043 int bitoff = 0;
2044 for (; bitoff < kBitsPerInt; ++bitoff) {
2045 if ((rset & (1 << bitoff)) != 0) {
2046 int bitpos = intoff*kBitsPerByte + bitoff;
2047 Address slot = p->OffsetToAddress(bitpos << kObjectAlignmentBits);
2048 Object** obj = reinterpret_cast<Object**>(slot);
kasperl@chromium.org68ac0092009-07-09 06:00:35 +00002049 if (*obj == Heap::raw_unchecked_fixed_array_map()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002050 rset_marked_arrays++;
2051 FixedArray* fa = FixedArray::cast(HeapObject::FromAddress(slot));
2052
2053 rset_marked_array_elements += fa->length();
2054 // Manually inline FixedArray::IterateBody
2055 Address elm_start = slot + FixedArray::kHeaderSize;
2056 Address elm_stop = elm_start + fa->length() * kPointerSize;
2057 for (Address elm_addr = elm_start;
2058 elm_addr < elm_stop; elm_addr += kPointerSize) {
2059 // Filter non-heap-object pointers
2060 Object** elm_p = reinterpret_cast<Object**>(elm_addr);
2061 if (Heap::InNewSpace(*elm_p))
2062 cross_gen_array_elements++;
2063 }
2064 } else {
2065 rset_marked_pointers++;
2066 if (Heap::InNewSpace(*obj))
2067 cross_gen_pointers++;
2068 }
2069 }
2070 }
2071 }
2072 }
2073 }
2074
2075 pct = rset_marked_pointers == 0 ?
2076 0 : cross_gen_pointers * 100 / rset_marked_pointers;
2077 PrintF(" rset-marked pointers %d, to-new-space %d (%%%d)\n",
2078 rset_marked_pointers, cross_gen_pointers, pct);
2079 PrintF(" rset_marked arrays %d, ", rset_marked_arrays);
2080 PrintF(" elements %d, ", rset_marked_array_elements);
2081 pct = rset_marked_array_elements == 0 ? 0
2082 : cross_gen_array_elements * 100 / rset_marked_array_elements;
2083 PrintF(" pointers to new space %d (%%%d)\n", cross_gen_array_elements, pct);
2084 PrintF(" total rset-marked bits %d\n",
2085 (rset_marked_pointers + rset_marked_arrays));
2086 pct = (rset_marked_pointers + rset_marked_array_elements) == 0 ? 0
2087 : (cross_gen_pointers + cross_gen_array_elements) * 100 /
2088 (rset_marked_pointers + rset_marked_array_elements);
2089 PrintF(" total rset pointers %d, true cross generation ones %d (%%%d)\n",
2090 (rset_marked_pointers + rset_marked_array_elements),
2091 (cross_gen_pointers + cross_gen_array_elements),
2092 pct);
2093
2094 ClearHistograms();
2095 HeapObjectIterator obj_it(this);
2096 while (obj_it.has_next()) { CollectHistogramInfo(obj_it.next()); }
2097 ReportHistogram(true);
2098}
2099
2100
2101// Dump the range of remembered set words between [start, end) corresponding
2102// to the pointers starting at object_p. The allocation_top is an object
2103// pointer which should not be read past. This is important for large object
2104// pages, where some bits in the remembered set range do not correspond to
2105// allocated addresses.
2106static void PrintRSetRange(Address start, Address end, Object** object_p,
2107 Address allocation_top) {
2108 Address rset_address = start;
2109
2110 // If the range starts on on odd numbered word (eg, for large object extra
2111 // remembered set ranges), print some spaces.
ager@chromium.org9085a012009-05-11 19:22:57 +00002112 if ((reinterpret_cast<uintptr_t>(start) / kIntSize) % 2 == 1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002113 PrintF(" ");
2114 }
2115
2116 // Loop over all the words in the range.
2117 while (rset_address < end) {
2118 uint32_t rset_word = Memory::uint32_at(rset_address);
2119 int bit_position = 0;
2120
2121 // Loop over all the bits in the word.
2122 while (bit_position < kBitsPerInt) {
2123 if (object_p == reinterpret_cast<Object**>(allocation_top)) {
2124 // Print a bar at the allocation pointer.
2125 PrintF("|");
2126 } else if (object_p > reinterpret_cast<Object**>(allocation_top)) {
2127 // Do not dereference object_p past the allocation pointer.
2128 PrintF("#");
2129 } else if ((rset_word & (1 << bit_position)) == 0) {
2130 // Print a dot for zero bits.
2131 PrintF(".");
2132 } else if (Heap::InNewSpace(*object_p)) {
2133 // Print an X for one bits for pointers to new space.
2134 PrintF("X");
2135 } else {
2136 // Print a circle for one bits for pointers to old space.
2137 PrintF("o");
2138 }
2139
2140 // Print a space after every 8th bit except the last.
2141 if (bit_position % 8 == 7 && bit_position != (kBitsPerInt - 1)) {
2142 PrintF(" ");
2143 }
2144
2145 // Advance to next bit.
2146 bit_position++;
2147 object_p++;
2148 }
2149
2150 // Print a newline after every odd numbered word, otherwise a space.
ager@chromium.org9085a012009-05-11 19:22:57 +00002151 if ((reinterpret_cast<uintptr_t>(rset_address) / kIntSize) % 2 == 1) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002152 PrintF("\n");
2153 } else {
2154 PrintF(" ");
2155 }
2156
2157 // Advance to next remembered set word.
2158 rset_address += kIntSize;
2159 }
2160}
2161
2162
2163void PagedSpace::DoPrintRSet(const char* space_name) {
2164 PageIterator it(this, PageIterator::PAGES_IN_USE);
2165 while (it.has_next()) {
2166 Page* p = it.next();
2167 PrintF("%s page 0x%x:\n", space_name, p);
2168 PrintRSetRange(p->RSetStart(), p->RSetEnd(),
2169 reinterpret_cast<Object**>(p->ObjectAreaStart()),
2170 p->AllocationTop());
2171 PrintF("\n");
2172 }
2173}
2174
2175
2176void OldSpace::PrintRSet() { DoPrintRSet("old"); }
2177#endif
2178
2179// -----------------------------------------------------------------------------
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002180// FixedSpace implementation
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002181
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002182void FixedSpace::PrepareForMarkCompact(bool will_compact) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002183 if (will_compact) {
2184 // Reset relocation info.
2185 MCResetRelocationInfo();
2186
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002187 // During a compacting collection, everything in the space is considered
2188 // 'available' (set by the call to MCResetRelocationInfo) and we will
2189 // rediscover live and wasted bytes during the collection.
2190 ASSERT(Available() == Capacity());
2191 } else {
2192 // During a non-compacting collection, everything below the linear
2193 // allocation pointer except wasted top-of-page blocks is considered
2194 // allocated and we will rediscover available bytes during the
2195 // collection.
2196 accounting_stats_.AllocateBytes(free_list_.available());
2197 }
2198
kasper.lund7276f142008-07-30 08:49:36 +00002199 // Clear the free list before a full GC---it will be rebuilt afterward.
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002200 free_list_.Reset();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002201}
2202
2203
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002204void FixedSpace::MCCommitRelocationInfo() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002205 // Update fast allocation info.
2206 allocation_info_.top = mc_forwarding_info_.top;
2207 allocation_info_.limit = mc_forwarding_info_.limit;
kasper.lund7276f142008-07-30 08:49:36 +00002208 ASSERT(allocation_info_.VerifyPagedAllocation());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002209
2210 // The space is compacted and we haven't yet wasted any space.
2211 ASSERT(Waste() == 0);
2212
2213 // Update allocation_top of each page in use and compute waste.
2214 int computed_size = 0;
2215 PageIterator it(this, PageIterator::PAGES_USED_BY_MC);
2216 while (it.has_next()) {
2217 Page* page = it.next();
2218 Address page_top = page->AllocationTop();
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002219 computed_size += static_cast<int>(page_top - page->ObjectAreaStart());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002220 if (it.has_next()) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002221 accounting_stats_.WasteBytes(
2222 static_cast<int>(page->ObjectAreaEnd() - page_top));
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002223 }
2224 }
2225
2226 // Make sure the computed size - based on the used portion of the
2227 // pages in use - matches the size we adjust during allocation.
2228 ASSERT(computed_size == Size());
2229}
2230
2231
kasper.lund7276f142008-07-30 08:49:36 +00002232// Slow case for normal allocation. Try in order: (1) allocate in the next
2233// page in the space, (2) allocate off the space's free list, (3) expand the
2234// space, (4) fail.
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002235HeapObject* FixedSpace::SlowAllocateRaw(int size_in_bytes) {
2236 ASSERT_EQ(object_size_in_bytes_, size_in_bytes);
kasper.lund7276f142008-07-30 08:49:36 +00002237 // Linear allocation in this space has failed. If there is another page
2238 // in the space, move to that page and allocate there. This allocation
2239 // should succeed.
2240 Page* current_page = TopPageOf(allocation_info_);
2241 if (current_page->next_page()->is_valid()) {
2242 return AllocateInNextPage(current_page, size_in_bytes);
2243 }
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002244
ager@chromium.org3811b432009-10-28 14:53:37 +00002245 // There is no next page in this space. Try free list allocation unless
2246 // that is currently forbidden. The fixed space free list implicitly assumes
2247 // that all free blocks are of the fixed size.
2248 if (!Heap::linear_allocation()) {
kasper.lund7276f142008-07-30 08:49:36 +00002249 Object* result = free_list_.Allocate();
2250 if (!result->IsFailure()) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002251 accounting_stats_.AllocateBytes(size_in_bytes);
kasper.lund7276f142008-07-30 08:49:36 +00002252 return HeapObject::cast(result);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002253 }
2254 }
kasper.lund7276f142008-07-30 08:49:36 +00002255
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00002256 // Free list allocation failed and there is no next page. Fail if we have
2257 // hit the old generation size limit that should cause a garbage
2258 // collection.
2259 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) {
2260 return NULL;
2261 }
2262
2263 // Try to expand the space and allocate in the new next page.
kasper.lund7276f142008-07-30 08:49:36 +00002264 ASSERT(!current_page->next_page()->is_valid());
2265 if (Expand(current_page)) {
2266 return AllocateInNextPage(current_page, size_in_bytes);
2267 }
2268
2269 // Finally, fail.
2270 return NULL;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002271}
2272
2273
kasper.lund7276f142008-07-30 08:49:36 +00002274// Move to the next page (there is assumed to be one) and allocate there.
2275// The top of page block is always wasted, because it is too small to hold a
2276// map.
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002277HeapObject* FixedSpace::AllocateInNextPage(Page* current_page,
2278 int size_in_bytes) {
kasper.lund7276f142008-07-30 08:49:36 +00002279 ASSERT(current_page->next_page()->is_valid());
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002280 ASSERT(current_page->ObjectAreaEnd() - allocation_info_.top == page_extra_);
2281 ASSERT_EQ(object_size_in_bytes_, size_in_bytes);
2282 accounting_stats_.WasteBytes(page_extra_);
kasper.lund7276f142008-07-30 08:49:36 +00002283 SetAllocationInfo(&allocation_info_, current_page->next_page());
2284 return AllocateLinearly(&allocation_info_, size_in_bytes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002285}
2286
2287
2288#ifdef DEBUG
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002289void FixedSpace::ReportStatistics() {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002290 int pct = Available() * 100 / Capacity();
2291 PrintF(" capacity: %d, waste: %d, available: %d, %%%d\n",
2292 Capacity(), Waste(), Available(), pct);
2293
2294 // Report remembered set statistics.
2295 int rset_marked_pointers = 0;
2296 int cross_gen_pointers = 0;
2297
2298 PageIterator page_it(this, PageIterator::PAGES_IN_USE);
2299 while (page_it.has_next()) {
2300 Page* p = page_it.next();
2301
2302 for (Address rset_addr = p->RSetStart();
2303 rset_addr < p->RSetEnd();
2304 rset_addr += kIntSize) {
2305 int rset = Memory::int_at(rset_addr);
2306 if (rset != 0) {
2307 // Bits were set
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002308 int intoff =
2309 static_cast<int>(rset_addr - p->address() - Page::kRSetOffset);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002310 int bitoff = 0;
2311 for (; bitoff < kBitsPerInt; ++bitoff) {
2312 if ((rset & (1 << bitoff)) != 0) {
2313 int bitpos = intoff*kBitsPerByte + bitoff;
2314 Address slot = p->OffsetToAddress(bitpos << kObjectAlignmentBits);
2315 Object** obj = reinterpret_cast<Object**>(slot);
2316 rset_marked_pointers++;
2317 if (Heap::InNewSpace(*obj))
2318 cross_gen_pointers++;
2319 }
2320 }
2321 }
2322 }
2323 }
2324
2325 pct = rset_marked_pointers == 0 ?
2326 0 : cross_gen_pointers * 100 / rset_marked_pointers;
2327 PrintF(" rset-marked pointers %d, to-new-space %d (%%%d)\n",
2328 rset_marked_pointers, cross_gen_pointers, pct);
2329
2330 ClearHistograms();
2331 HeapObjectIterator obj_it(this);
2332 while (obj_it.has_next()) { CollectHistogramInfo(obj_it.next()); }
2333 ReportHistogram(false);
2334}
2335
2336
kasperl@chromium.orgdefbd102009-07-13 14:04:26 +00002337void FixedSpace::PrintRSet() { DoPrintRSet(name_); }
2338#endif
2339
2340
2341// -----------------------------------------------------------------------------
2342// MapSpace implementation
2343
2344void MapSpace::PrepareForMarkCompact(bool will_compact) {
2345 // Call prepare of the super class.
2346 FixedSpace::PrepareForMarkCompact(will_compact);
2347
2348 if (will_compact) {
2349 // Initialize map index entry.
2350 int page_count = 0;
2351 PageIterator it(this, PageIterator::ALL_PAGES);
2352 while (it.has_next()) {
2353 ASSERT_MAP_PAGE_INDEX(page_count);
2354
2355 Page* p = it.next();
2356 ASSERT(p->mc_page_index == page_count);
2357
2358 page_addresses_[page_count++] = p->address();
2359 }
2360 }
2361}
2362
2363
2364#ifdef DEBUG
2365void MapSpace::VerifyObject(HeapObject* object) {
2366 // The object should be a map or a free-list node.
2367 ASSERT(object->IsMap() || object->IsByteArray());
2368}
2369#endif
2370
2371
2372// -----------------------------------------------------------------------------
2373// GlobalPropertyCellSpace implementation
2374
2375#ifdef DEBUG
2376void CellSpace::VerifyObject(HeapObject* object) {
2377 // The object should be a global object property cell or a free-list node.
2378 ASSERT(object->IsJSGlobalPropertyCell() ||
2379 object->map() == Heap::two_pointer_filler_map());
2380}
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002381#endif
2382
2383
2384// -----------------------------------------------------------------------------
2385// LargeObjectIterator
2386
2387LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space) {
2388 current_ = space->first_chunk_;
2389 size_func_ = NULL;
2390}
2391
2392
2393LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space,
2394 HeapObjectCallback size_func) {
2395 current_ = space->first_chunk_;
2396 size_func_ = size_func;
2397}
2398
2399
2400HeapObject* LargeObjectIterator::next() {
2401 ASSERT(has_next());
2402 HeapObject* object = current_->GetObject();
2403 current_ = current_->next();
2404 return object;
2405}
2406
2407
2408// -----------------------------------------------------------------------------
2409// LargeObjectChunk
2410
2411LargeObjectChunk* LargeObjectChunk::New(int size_in_bytes,
kasper.lund7276f142008-07-30 08:49:36 +00002412 size_t* chunk_size,
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002413 Executability executable) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002414 size_t requested = ChunkSizeFor(size_in_bytes);
kasper.lund7276f142008-07-30 08:49:36 +00002415 void* mem = MemoryAllocator::AllocateRawMemory(requested,
2416 chunk_size,
2417 executable);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002418 if (mem == NULL) return NULL;
2419 LOG(NewEvent("LargeObjectChunk", mem, *chunk_size));
2420 if (*chunk_size < requested) {
2421 MemoryAllocator::FreeRawMemory(mem, *chunk_size);
2422 LOG(DeleteEvent("LargeObjectChunk", mem));
2423 return NULL;
2424 }
2425 return reinterpret_cast<LargeObjectChunk*>(mem);
2426}
2427
2428
2429int LargeObjectChunk::ChunkSizeFor(int size_in_bytes) {
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002430 int os_alignment = static_cast<int>(OS::AllocateAlignment());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002431 if (os_alignment < Page::kPageSize)
2432 size_in_bytes += (Page::kPageSize - os_alignment);
2433 return size_in_bytes + Page::kObjectStartOffset;
2434}
2435
2436// -----------------------------------------------------------------------------
2437// LargeObjectSpace
2438
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002439LargeObjectSpace::LargeObjectSpace(AllocationSpace id)
2440 : Space(id, NOT_EXECUTABLE), // Managed on a per-allocation basis
kasper.lund7276f142008-07-30 08:49:36 +00002441 first_chunk_(NULL),
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002442 size_(0),
2443 page_count_(0) {}
2444
2445
2446bool LargeObjectSpace::Setup() {
2447 first_chunk_ = NULL;
2448 size_ = 0;
2449 page_count_ = 0;
2450 return true;
2451}
2452
2453
2454void LargeObjectSpace::TearDown() {
2455 while (first_chunk_ != NULL) {
2456 LargeObjectChunk* chunk = first_chunk_;
2457 first_chunk_ = first_chunk_->next();
2458 LOG(DeleteEvent("LargeObjectChunk", chunk->address()));
2459 MemoryAllocator::FreeRawMemory(chunk->address(), chunk->size());
2460 }
2461
2462 size_ = 0;
2463 page_count_ = 0;
2464}
2465
2466
kasperl@chromium.orgf5aa8372009-03-24 14:47:14 +00002467#ifdef ENABLE_HEAP_PROTECTION
2468
2469void LargeObjectSpace::Protect() {
2470 LargeObjectChunk* chunk = first_chunk_;
2471 while (chunk != NULL) {
2472 MemoryAllocator::Protect(chunk->address(), chunk->size());
2473 chunk = chunk->next();
2474 }
2475}
2476
2477
2478void LargeObjectSpace::Unprotect() {
2479 LargeObjectChunk* chunk = first_chunk_;
2480 while (chunk != NULL) {
2481 bool is_code = chunk->GetObject()->IsCode();
2482 MemoryAllocator::Unprotect(chunk->address(), chunk->size(),
2483 is_code ? EXECUTABLE : NOT_EXECUTABLE);
2484 chunk = chunk->next();
2485 }
2486}
2487
2488#endif
2489
2490
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002491Object* LargeObjectSpace::AllocateRawInternal(int requested_size,
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002492 int object_size,
2493 Executability executable) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002494 ASSERT(0 < object_size && object_size <= requested_size);
kasperl@chromium.org9bbf9682008-10-30 11:53:07 +00002495
2496 // Check if we want to force a GC before growing the old space further.
2497 // If so, fail the allocation.
2498 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) {
2499 return Failure::RetryAfterGC(requested_size, identity());
2500 }
2501
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002502 size_t chunk_size;
2503 LargeObjectChunk* chunk =
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002504 LargeObjectChunk::New(requested_size, &chunk_size, executable);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002505 if (chunk == NULL) {
kasper.lund7276f142008-07-30 08:49:36 +00002506 return Failure::RetryAfterGC(requested_size, identity());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002507 }
2508
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002509 size_ += static_cast<int>(chunk_size);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002510 page_count_++;
2511 chunk->set_next(first_chunk_);
2512 chunk->set_size(chunk_size);
2513 first_chunk_ = chunk;
2514
2515 // Set the object address and size in the page header and clear its
2516 // remembered set.
2517 Page* page = Page::FromAddress(RoundUp(chunk->address(), Page::kPageSize));
2518 Address object_address = page->ObjectAreaStart();
2519 // Clear the low order bit of the second word in the page to flag it as a
2520 // large object page. If the chunk_size happened to be written there, its
2521 // low order bit should already be clear.
2522 ASSERT((chunk_size & 0x1) == 0);
2523 page->is_normal_page &= ~0x1;
2524 page->ClearRSet();
2525 int extra_bytes = requested_size - object_size;
2526 if (extra_bytes > 0) {
2527 // The extra memory for the remembered set should be cleared.
2528 memset(object_address + object_size, 0, extra_bytes);
2529 }
2530
2531 return HeapObject::FromAddress(object_address);
2532}
2533
2534
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002535Object* LargeObjectSpace::AllocateRawCode(int size_in_bytes) {
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002536 ASSERT(0 < size_in_bytes);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002537 return AllocateRawInternal(size_in_bytes,
2538 size_in_bytes,
2539 EXECUTABLE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002540}
2541
2542
2543Object* LargeObjectSpace::AllocateRawFixedArray(int size_in_bytes) {
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002544 ASSERT(0 < size_in_bytes);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002545 int extra_rset_bytes = ExtraRSetBytesFor(size_in_bytes);
ager@chromium.org9258b6b2008-09-11 09:11:10 +00002546 return AllocateRawInternal(size_in_bytes + extra_rset_bytes,
2547 size_in_bytes,
2548 NOT_EXECUTABLE);
2549}
2550
2551
2552Object* LargeObjectSpace::AllocateRaw(int size_in_bytes) {
2553 ASSERT(0 < size_in_bytes);
2554 return AllocateRawInternal(size_in_bytes,
2555 size_in_bytes,
2556 NOT_EXECUTABLE);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002557}
2558
2559
2560// GC support
2561Object* LargeObjectSpace::FindObject(Address a) {
2562 for (LargeObjectChunk* chunk = first_chunk_;
2563 chunk != NULL;
2564 chunk = chunk->next()) {
2565 Address chunk_address = chunk->address();
2566 if (chunk_address <= a && a < chunk_address + chunk->size()) {
2567 return chunk->GetObject();
2568 }
2569 }
2570 return Failure::Exception();
2571}
2572
2573
2574void LargeObjectSpace::ClearRSet() {
2575 ASSERT(Page::is_rset_in_use());
2576
2577 LargeObjectIterator it(this);
2578 while (it.has_next()) {
2579 HeapObject* object = it.next();
2580 // We only have code, sequential strings, or fixed arrays in large
2581 // object space, and only fixed arrays need remembered set support.
2582 if (object->IsFixedArray()) {
2583 // Clear the normal remembered set region of the page;
2584 Page* page = Page::FromAddress(object->address());
2585 page->ClearRSet();
2586
2587 // Clear the extra remembered set.
2588 int size = object->Size();
2589 int extra_rset_bytes = ExtraRSetBytesFor(size);
2590 memset(object->address() + size, 0, extra_rset_bytes);
2591 }
2592 }
2593}
2594
2595
2596void LargeObjectSpace::IterateRSet(ObjectSlotCallback copy_object_func) {
2597 ASSERT(Page::is_rset_in_use());
2598
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002599 static void* lo_rset_histogram = StatsTable::CreateHistogram(
2600 "V8.RSetLO",
2601 0,
2602 // Keeping this histogram's buckets the same as the paged space histogram.
2603 Page::kObjectAreaSize / kPointerSize,
2604 30);
2605
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002606 LargeObjectIterator it(this);
2607 while (it.has_next()) {
2608 // We only have code, sequential strings, or fixed arrays in large
2609 // object space, and only fixed arrays can possibly contain pointers to
2610 // the young generation.
2611 HeapObject* object = it.next();
2612 if (object->IsFixedArray()) {
2613 // Iterate the normal page remembered set range.
2614 Page* page = Page::FromAddress(object->address());
2615 Address object_end = object->address() + object->Size();
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002616 int count = Heap::IterateRSetRange(page->ObjectAreaStart(),
2617 Min(page->ObjectAreaEnd(), object_end),
2618 page->RSetStart(),
2619 copy_object_func);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002620
2621 // Iterate the extra array elements.
2622 if (object_end > page->ObjectAreaEnd()) {
kasperl@chromium.org71affb52009-05-26 05:44:31 +00002623 count += Heap::IterateRSetRange(page->ObjectAreaEnd(), object_end,
2624 object_end, copy_object_func);
2625 }
2626 if (lo_rset_histogram != NULL) {
2627 StatsTable::AddHistogramSample(lo_rset_histogram, count);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002628 }
2629 }
2630 }
2631}
2632
2633
2634void LargeObjectSpace::FreeUnmarkedObjects() {
2635 LargeObjectChunk* previous = NULL;
2636 LargeObjectChunk* current = first_chunk_;
2637 while (current != NULL) {
2638 HeapObject* object = current->GetObject();
kasper.lund7276f142008-07-30 08:49:36 +00002639 if (object->IsMarked()) {
2640 object->ClearMark();
2641 MarkCompactCollector::tracer()->decrement_marked_count();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002642 previous = current;
2643 current = current->next();
2644 } else {
2645 Address chunk_address = current->address();
2646 size_t chunk_size = current->size();
2647
2648 // Cut the chunk out from the chunk list.
2649 current = current->next();
2650 if (previous == NULL) {
2651 first_chunk_ = current;
2652 } else {
2653 previous->set_next(current);
2654 }
2655
2656 // Free the chunk.
2657 if (object->IsCode()) {
2658 LOG(CodeDeleteEvent(object->address()));
2659 }
ager@chromium.orgc4c92722009-11-18 14:12:51 +00002660 size_ -= static_cast<int>(chunk_size);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002661 page_count_--;
2662 MemoryAllocator::FreeRawMemory(chunk_address, chunk_size);
2663 LOG(DeleteEvent("LargeObjectChunk", chunk_address));
2664 }
2665 }
2666}
2667
2668
2669bool LargeObjectSpace::Contains(HeapObject* object) {
2670 Address address = object->address();
2671 Page* page = Page::FromAddress(address);
2672
2673 SLOW_ASSERT(!page->IsLargeObjectPage()
2674 || !FindObject(address)->IsFailure());
2675
2676 return page->IsLargeObjectPage();
2677}
2678
2679
2680#ifdef DEBUG
2681// We do not assume that the large object iterator works, because it depends
2682// on the invariants we are checking during verification.
2683void LargeObjectSpace::Verify() {
2684 for (LargeObjectChunk* chunk = first_chunk_;
2685 chunk != NULL;
2686 chunk = chunk->next()) {
2687 // Each chunk contains an object that starts at the large object page's
2688 // object area start.
2689 HeapObject* object = chunk->GetObject();
2690 Page* page = Page::FromAddress(object->address());
2691 ASSERT(object->address() == page->ObjectAreaStart());
2692
2693 // The first word should be a map, and we expect all map pointers to be
2694 // in map space.
2695 Map* map = object->map();
2696 ASSERT(map->IsMap());
2697 ASSERT(Heap::map_space()->Contains(map));
2698
ager@chromium.orga1645e22009-09-09 19:27:10 +00002699 // We have only code, sequential strings, external strings
2700 // (sequential strings that have been morphed into external
2701 // strings), fixed arrays, and byte arrays in large object space.
2702 ASSERT(object->IsCode() || object->IsSeqString() ||
2703 object->IsExternalString() || object->IsFixedArray() ||
2704 object->IsByteArray());
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002705
2706 // The object itself should look OK.
mads.s.ager@gmail.com9a4089a2008-09-01 08:55:01 +00002707 object->Verify();
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002708
2709 // Byte arrays and strings don't have interior pointers.
2710 if (object->IsCode()) {
2711 VerifyPointersVisitor code_visitor;
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002712 object->IterateBody(map->instance_type(),
2713 object->Size(),
2714 &code_visitor);
christian.plesner.hansen43d26ec2008-07-03 15:10:15 +00002715 } else if (object->IsFixedArray()) {
2716 // We loop over fixed arrays ourselves, rather then using the visitor,
2717 // because the visitor doesn't support the start/offset iteration
2718 // needed for IsRSetSet.
2719 FixedArray* array = FixedArray::cast(object);
2720 for (int j = 0; j < array->length(); j++) {
2721 Object* element = array->get(j);
2722 if (element->IsHeapObject()) {
2723 HeapObject* element_object = HeapObject::cast(element);
2724 ASSERT(Heap::Contains(element_object));
2725 ASSERT(element_object->map()->IsMap());
2726 if (Heap::InNewSpace(element_object)) {
2727 ASSERT(Page::IsRSetSet(object->address(),
2728 FixedArray::kHeaderSize + j * kPointerSize));
2729 }
2730 }
2731 }
2732 }
2733 }
2734}
2735
2736
2737void LargeObjectSpace::Print() {
2738 LargeObjectIterator it(this);
2739 while (it.has_next()) {
2740 it.next()->Print();
2741 }
2742}
2743
2744
2745void LargeObjectSpace::ReportStatistics() {
2746 PrintF(" size: %d\n", size_);
2747 int num_objects = 0;
2748 ClearHistograms();
2749 LargeObjectIterator it(this);
2750 while (it.has_next()) {
2751 num_objects++;
2752 CollectHistogramInfo(it.next());
2753 }
2754
2755 PrintF(" number of objects %d\n", num_objects);
2756 if (num_objects > 0) ReportHistogram(false);
2757}
2758
2759
2760void LargeObjectSpace::CollectCodeStatistics() {
2761 LargeObjectIterator obj_it(this);
2762 while (obj_it.has_next()) {
2763 HeapObject* obj = obj_it.next();
2764 if (obj->IsCode()) {
2765 Code* code = Code::cast(obj);
2766 code_kind_statistics[code->kind()] += code->Size();
2767 }
2768 }
2769}
2770
2771
2772void LargeObjectSpace::PrintRSet() {
2773 LargeObjectIterator it(this);
2774 while (it.has_next()) {
2775 HeapObject* object = it.next();
2776 if (object->IsFixedArray()) {
2777 Page* page = Page::FromAddress(object->address());
2778
2779 Address allocation_top = object->address() + object->Size();
2780 PrintF("large page 0x%x:\n", page);
2781 PrintRSetRange(page->RSetStart(), page->RSetEnd(),
2782 reinterpret_cast<Object**>(object->address()),
2783 allocation_top);
2784 int extra_array_bytes = object->Size() - Page::kObjectAreaSize;
2785 int extra_rset_bits = RoundUp(extra_array_bytes / kPointerSize,
2786 kBitsPerInt);
2787 PrintF("------------------------------------------------------------"
2788 "-----------\n");
2789 PrintRSetRange(allocation_top,
2790 allocation_top + extra_rset_bits / kBitsPerByte,
2791 reinterpret_cast<Object**>(object->address()
2792 + Page::kObjectAreaSize),
2793 allocation_top);
2794 PrintF("\n");
2795 }
2796 }
2797}
2798#endif // DEBUG
2799
2800} } // namespace v8::internal