Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(106)

Side by Side Diff: src/spaces.cc

Issue 5987005: Refactor MemoryAllocator to allow big normal pages (Closed) Base URL: https://v8.googlecode.com/svn/branches/experimental/gc
Patch Set: remove rogue printf Created 9 years, 12 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/spaces.h ('k') | src/spaces-inl.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2006-2010 the V8 project authors. All rights reserved. 1 // Copyright 2006-2010 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without 2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are 3 // modification, are permitted provided that the following conditions are
4 // met: 4 // met:
5 // 5 //
6 // * Redistributions of source code must retain the above copyright 6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer. 7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above 8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following 9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided 10 // disclaimer in the documentation and/or other materials provided
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after
119 119
120 // ----------------------------------------------------------------------------- 120 // -----------------------------------------------------------------------------
121 // PageIterator 121 // PageIterator
122 122
123 PageIterator::PageIterator(PagedSpace* space, Mode mode) : space_(space) { 123 PageIterator::PageIterator(PagedSpace* space, Mode mode) : space_(space) {
124 prev_page_ = NULL; 124 prev_page_ = NULL;
125 switch (mode) { 125 switch (mode) {
126 case PAGES_IN_USE: 126 case PAGES_IN_USE:
127 stop_page_ = space->AllocationTopPage(); 127 stop_page_ = space->AllocationTopPage();
128 break; 128 break;
129 case PAGES_USED_BY_MC:
130 stop_page_ = space->MCRelocationTopPage();
131 break;
132 case ALL_PAGES: 129 case ALL_PAGES:
133 #ifdef DEBUG 130 #ifdef DEBUG
134 // Verify that the cached last page in the space is actually the 131 // Verify that the cached last page in the space is actually the
135 // last page. 132 // last page.
136 for (Page* p = space->first_page_; p->is_valid(); p = p->next_page()) { 133 for (Page* p = space->first_page_; p->is_valid(); p = p->next_page()) {
137 if (!p->next_page()->is_valid()) { 134 if (!p->next_page()->is_valid()) {
138 ASSERT(space->last_page_ == p); 135 ASSERT(space->last_page_ == p);
139 } 136 }
140 } 137 }
141 #endif 138 #endif
(...skipping 19 matching lines...) Expand all
161 CHECK(code_range_ != NULL); 158 CHECK(code_range_ != NULL);
162 if (!code_range_->IsReserved()) { 159 if (!code_range_->IsReserved()) {
163 delete code_range_; 160 delete code_range_;
164 code_range_ = NULL; 161 code_range_ = NULL;
165 return false; 162 return false;
166 } 163 }
167 164
168 // We are sure that we have mapped a block of requested addresses. 165 // We are sure that we have mapped a block of requested addresses.
169 ASSERT(code_range_->size() == requested); 166 ASSERT(code_range_->size() == requested);
170 LOG(NewEvent("CodeRange", code_range_->address(), requested)); 167 LOG(NewEvent("CodeRange", code_range_->address(), requested));
171 allocation_list_.Add(FreeBlock(code_range_->address(), code_range_->size())); 168 Address base = reinterpret_cast<Address>(code_range_->address());
169 Address aligned_base =
170 RoundUp(reinterpret_cast<Address>(code_range_->address()),
171 MemoryChunk::kAlignment);
172 int size = code_range_->size() - (aligned_base - base);
173 allocation_list_.Add(FreeBlock(aligned_base, size));
172 current_allocation_block_index_ = 0; 174 current_allocation_block_index_ = 0;
173 return true; 175 return true;
174 } 176 }
175 177
176 178
177 int CodeRange::CompareFreeBlockAddress(const FreeBlock* left, 179 int CodeRange::CompareFreeBlockAddress(const FreeBlock* left,
178 const FreeBlock* right) { 180 const FreeBlock* right) {
179 // The entire point of CodeRange is that the difference between two 181 // The entire point of CodeRange is that the difference between two
180 // addresses in the range can be represented as a signed 32-bit int, 182 // addresses in the range can be represented as a signed 32-bit int,
181 // so the cast is semantically correct. 183 // so the cast is semantically correct.
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
218 return; // Found a large enough allocation block. 220 return; // Found a large enough allocation block.
219 } 221 }
220 } 222 }
221 223
222 // Code range is full or too fragmented. 224 // Code range is full or too fragmented.
223 V8::FatalProcessOutOfMemory("CodeRange::GetNextAllocationBlock"); 225 V8::FatalProcessOutOfMemory("CodeRange::GetNextAllocationBlock");
224 } 226 }
225 227
226 228
227 229
228 void* CodeRange::AllocateRawMemory(const size_t requested, size_t* allocated) { 230 Address CodeRange::AllocateRawMemory(const size_t requested,
231 size_t* allocated) {
229 ASSERT(current_allocation_block_index_ < allocation_list_.length()); 232 ASSERT(current_allocation_block_index_ < allocation_list_.length());
230 if (requested > allocation_list_[current_allocation_block_index_].size) { 233 if (requested > allocation_list_[current_allocation_block_index_].size) {
231 // Find an allocation block large enough. This function call may 234 // Find an allocation block large enough. This function call may
232 // call V8::FatalProcessOutOfMemory if it cannot find a large enough block. 235 // call V8::FatalProcessOutOfMemory if it cannot find a large enough block.
233 GetNextAllocationBlock(requested); 236 GetNextAllocationBlock(requested);
234 } 237 }
235 // Commit the requested memory at the start of the current allocation block. 238 // Commit the requested memory at the start of the current allocation block.
236 *allocated = RoundUp(requested, Page::kPageSize); 239 size_t aligned_requested = RoundUp(requested, MemoryChunk::kAlignment);
237 FreeBlock current = allocation_list_[current_allocation_block_index_]; 240 FreeBlock current = allocation_list_[current_allocation_block_index_];
238 if (*allocated >= current.size - Page::kPageSize) { 241 if (aligned_requested >= (current.size - Page::kPageSize)) {
239 // Don't leave a small free block, useless for a large object or chunk. 242 // Don't leave a small free block, useless for a large object or chunk.
240 *allocated = current.size; 243 *allocated = current.size;
244 } else {
245 *allocated = aligned_requested;
241 } 246 }
242 ASSERT(*allocated <= current.size); 247 ASSERT(*allocated <= current.size);
248 ASSERT(IsAddressAligned(current.start, MemoryChunk::kAlignment));
243 if (!code_range_->Commit(current.start, *allocated, true)) { 249 if (!code_range_->Commit(current.start, *allocated, true)) {
244 *allocated = 0; 250 *allocated = 0;
245 return NULL; 251 return NULL;
246 } 252 }
247 allocation_list_[current_allocation_block_index_].start += *allocated; 253 allocation_list_[current_allocation_block_index_].start += *allocated;
248 allocation_list_[current_allocation_block_index_].size -= *allocated; 254 allocation_list_[current_allocation_block_index_].size -= *allocated;
249 if (*allocated == current.size) { 255 if (*allocated == current.size) {
250 GetNextAllocationBlock(0); // This block is used up, get the next one. 256 GetNextAllocationBlock(0); // This block is used up, get the next one.
251 } 257 }
252 return current.start; 258 return current.start;
253 } 259 }
254 260
255 261
256 void CodeRange::FreeRawMemory(void* address, size_t length) { 262 void CodeRange::FreeRawMemory(Address address, size_t length) {
263 ASSERT(IsAddressAligned(address, MemoryChunk::kAlignment));
257 free_list_.Add(FreeBlock(address, length)); 264 free_list_.Add(FreeBlock(address, length));
258 code_range_->Uncommit(address, length); 265 code_range_->Uncommit(address, length);
259 } 266 }
260 267
261 268
262 void CodeRange::TearDown() { 269 void CodeRange::TearDown() {
263 delete code_range_; // Frees all memory in the virtual memory range. 270 delete code_range_; // Frees all memory in the virtual memory range.
264 code_range_ = NULL; 271 code_range_ = NULL;
265 free_list_.Free(); 272 free_list_.Free();
266 allocation_list_.Free(); 273 allocation_list_.Free();
267 } 274 }
268 275
269 276
270 // ----------------------------------------------------------------------------- 277 // -----------------------------------------------------------------------------
271 // MemoryAllocator 278 // MemoryAllocator
272 // 279 //
273 intptr_t MemoryAllocator::capacity_ = 0; 280 size_t MemoryAllocator::capacity_ = 0;
274 intptr_t MemoryAllocator::capacity_executable_ = 0; 281 size_t MemoryAllocator::capacity_executable_ = 0;
275 intptr_t MemoryAllocator::size_ = 0; 282 size_t MemoryAllocator::size_ = 0;
276 intptr_t MemoryAllocator::size_executable_ = 0; 283 size_t MemoryAllocator::size_executable_ = 0;
277 284
278 List<MemoryAllocator::MemoryAllocationCallbackRegistration> 285 List<MemoryAllocator::MemoryAllocationCallbackRegistration>
279 MemoryAllocator::memory_allocation_callbacks_; 286 MemoryAllocator::memory_allocation_callbacks_;
280 287
281 VirtualMemory* MemoryAllocator::initial_chunk_ = NULL;
282
283 // 270 is an estimate based on the static default heap size of a pair of 256K
284 // semispaces and a 64M old generation.
285 const int kEstimatedNumberOfChunks = 270;
286 List<MemoryAllocator::ChunkInfo> MemoryAllocator::chunks_(
287 kEstimatedNumberOfChunks);
288 List<int> MemoryAllocator::free_chunk_ids_(kEstimatedNumberOfChunks);
289 int MemoryAllocator::max_nof_chunks_ = 0;
290 int MemoryAllocator::top_ = 0;
291
292
293 void MemoryAllocator::Push(int free_chunk_id) {
294 ASSERT(max_nof_chunks_ > 0);
295 ASSERT(top_ < max_nof_chunks_);
296 free_chunk_ids_[top_++] = free_chunk_id;
297 }
298
299
300 int MemoryAllocator::Pop() {
301 ASSERT(top_ > 0);
302 return free_chunk_ids_[--top_];
303 }
304
305
306 bool MemoryAllocator::Setup(intptr_t capacity, intptr_t capacity_executable) { 288 bool MemoryAllocator::Setup(intptr_t capacity, intptr_t capacity_executable) {
307 capacity_ = RoundUp(capacity, Page::kPageSize); 289 capacity_ = RoundUp(capacity, Page::kPageSize);
308 capacity_executable_ = RoundUp(capacity_executable, Page::kPageSize); 290 capacity_executable_ = RoundUp(capacity_executable, Page::kPageSize);
309 ASSERT_GE(capacity_, capacity_executable_); 291 ASSERT_GE(capacity_, capacity_executable_);
310 292
311 // Over-estimate the size of chunks_ array. It assumes the expansion of old
312 // space is always in the unit of a chunk (kChunkSize) except the last
313 // expansion.
314 //
315 // Due to alignment, allocated space might be one page less than required
316 // number (kPagesPerChunk) of pages for old spaces.
317 //
318 // Reserve two chunk ids for semispaces, one for map space, one for old
319 // space, and one for code space.
320 max_nof_chunks_ =
321 static_cast<int>((capacity_ / (kChunkSize - Page::kPageSize))) + 5;
322 if (max_nof_chunks_ > kMaxNofChunks) return false;
323
324 size_ = 0; 293 size_ = 0;
325 size_executable_ = 0; 294 size_executable_ = 0;
326 ChunkInfo info; // uninitialized element. 295
327 for (int i = max_nof_chunks_ - 1; i >= 0; i--) {
328 chunks_.Add(info);
329 free_chunk_ids_.Add(i);
330 }
331 top_ = max_nof_chunks_;
332 return true; 296 return true;
333 } 297 }
334 298
335 299
336 bool MemoryAllocator::SafeIsInAPageChunk(Address addr) {
337 return InInitialChunk(addr) || InAllocatedChunks(addr);
338 }
339
340
341 void MemoryAllocator::TearDown() { 300 void MemoryAllocator::TearDown() {
342 for (int i = 0; i < max_nof_chunks_; i++) { 301 // Check that spaces were teared down before MemoryAllocator.
343 if (chunks_[i].address() != NULL) DeleteChunk(i); 302 ASSERT(size_ == 0);
344 } 303 ASSERT(size_executable_ == 0);
345 chunks_.Clear();
346 free_chunk_ids_.Clear();
347
348 if (initial_chunk_ != NULL) {
349 LOG(DeleteEvent("InitialChunk", initial_chunk_->address()));
350 delete initial_chunk_;
351 initial_chunk_ = NULL;
352 }
353
354 FreeChunkTables(&chunk_table_[0],
355 kChunkTableTopLevelEntries,
356 kChunkTableLevels);
357
358 ASSERT(top_ == max_nof_chunks_); // all chunks are free
359 top_ = 0;
360 capacity_ = 0; 304 capacity_ = 0;
361 capacity_executable_ = 0; 305 capacity_executable_ = 0;
362 size_ = 0; 306 }
363 max_nof_chunks_ = 0; 307
364 } 308
365 309 void MemoryAllocator::FreeMemory(Address base,
366 310 size_t size,
367 void MemoryAllocator::FreeChunkTables(uintptr_t* array, int len, int level) { 311 Executability executable) {
368 for (int i = 0; i < len; i++) { 312 if (CodeRange::contains(static_cast<Address>(base))) {
369 if (array[i] != kUnusedChunkTableEntry) { 313 ASSERT(executable == EXECUTABLE);
370 uintptr_t* subarray = reinterpret_cast<uintptr_t*>(array[i]); 314 CodeRange::FreeRawMemory(base, size);
371 if (level > 1) { 315 } else {
372 array[i] = kUnusedChunkTableEntry; 316 ASSERT(executable == NOT_EXECUTABLE || !CodeRange::exists());
373 FreeChunkTables(subarray, 1 << kChunkTableBitsPerLevel, level - 1); 317 VirtualMemory::ReleaseRegion(base, size);
374 } else { 318 }
375 array[i] = kUnusedChunkTableEntry; 319
376 } 320 Counters::memory_allocated.Decrement(static_cast<int>(size));
377 delete[] subarray; 321
378 } 322 ASSERT(size_ >= size);
379 } 323 size_ -= size;
380 } 324
381 325 if (executable == EXECUTABLE) {
382 326 ASSERT(size_executable_ >= size);
383 void* MemoryAllocator::AllocateRawMemory(const size_t requested, 327 size_executable_ -= size;
384 size_t* allocated, 328 }
385 Executability executable) { 329 }
386 if (size_ + static_cast<size_t>(requested) > static_cast<size_t>(capacity_)) { 330
331
332 Address MemoryAllocator::ReserveAlignedMemory(const size_t requested,
333 size_t alignment,
334 size_t* allocated_size) {
335 ASSERT(IsAligned(alignment, OS::AllocateAlignment()));
336 if (size_ + requested > capacity_) return NULL;
337
338 size_t allocated = RoundUp(requested + alignment, OS::AllocateAlignment());
339
340 Address base = reinterpret_cast<Address>(
341 VirtualMemory::ReserveRegion(allocated));
342
343 Address end = base + allocated;
344
345 if (base == 0) return NULL;
346
347 Address aligned_base = RoundUp(base, alignment);
348
349 ASSERT(aligned_base + requested <= base + allocated);
350
351 // The difference between re-aligned base address and base address is
352 // multiple of OS::AllocateAlignment().
353 if (aligned_base != base) {
354 ASSERT(aligned_base > base);
355 // TODO(gc) check result of operation?
356 VirtualMemory::ReleaseRegion(reinterpret_cast<void*>(base),
357 aligned_base - base);
358 allocated -= (aligned_base - base);
359 base = aligned_base;
360 }
361
362 ASSERT(base + allocated == end);
363
364 Address requested_end = base + requested;
365 Address aligned_requested_end =
366 RoundUp(requested_end, OS::AllocateAlignment());
367
368 if (aligned_requested_end < end) {
369 // TODO(gc) check result of operation?
370 VirtualMemory::ReleaseRegion(reinterpret_cast<void*>(aligned_requested_end),
371 end - aligned_requested_end);
372 allocated = aligned_requested_end - base;
373 }
374
375 size_ += allocated;
376 *allocated_size = allocated;
377 return base;
378 }
379
380
381 Address MemoryAllocator::AllocateAlignedMemory(const size_t requested,
382 size_t alignment,
383 Executability executable,
384 size_t* allocated_size) {
385 Address base =
386 ReserveAlignedMemory(requested, Page::kPageSize, allocated_size);
387
388 if (base == NULL) return NULL;
389
390 if (!VirtualMemory::CommitRegion(base,
391 *allocated_size,
392 executable == EXECUTABLE)) {
393 VirtualMemory::ReleaseRegion(base, *allocated_size);
394 size_ -= *allocated_size;
387 return NULL; 395 return NULL;
388 } 396 }
389 397
390 void* mem; 398 return base;
399 }
400
401
402 MemoryChunk* MemoryAllocator::AllocateChunk(intptr_t body_size,
403 Executability executable,
404 Space* owner) {
405 size_t chunk_size = MemoryChunk::kBodyOffset + body_size;
406 Address base = NULL;
391 if (executable == EXECUTABLE) { 407 if (executable == EXECUTABLE) {
392 // Check executable memory limit. 408 // Check executable memory limit.
393 if (size_executable_ + requested > 409 if (size_executable_ + chunk_size > capacity_executable_) {
394 static_cast<size_t>(capacity_executable_)) {
395 LOG(StringEvent("MemoryAllocator::AllocateRawMemory", 410 LOG(StringEvent("MemoryAllocator::AllocateRawMemory",
396 "V8 Executable Allocation capacity exceeded")); 411 "V8 Executable Allocation capacity exceeded"));
397 return NULL; 412 return NULL;
398 } 413 }
414
399 // Allocate executable memory either from code range or from the 415 // Allocate executable memory either from code range or from the
400 // OS. 416 // OS.
401 if (CodeRange::exists()) { 417 if (CodeRange::exists()) {
402 mem = CodeRange::AllocateRawMemory(requested, allocated); 418 base = CodeRange::AllocateRawMemory(chunk_size, &chunk_size);
419 ASSERT(IsAligned(reinterpret_cast<intptr_t>(base),
420 MemoryChunk::kAlignment));
421 size_ += chunk_size;
403 } else { 422 } else {
404 mem = OS::Allocate(requested, allocated, true); 423 base = AllocateAlignedMemory(chunk_size,
424 MemoryChunk::kAlignment,
425 executable,
426 &chunk_size);
405 } 427 }
428
429 if (base == NULL) return NULL;
430
406 // Update executable memory size. 431 // Update executable memory size.
407 size_executable_ += static_cast<int>(*allocated); 432 size_executable_ += chunk_size;
408 } else { 433 } else {
409 mem = OS::Allocate(requested, allocated, false); 434 base = AllocateAlignedMemory(chunk_size,
410 } 435 MemoryChunk::kAlignment,
411 int alloced = static_cast<int>(*allocated); 436 executable,
412 size_ += alloced; 437 &chunk_size);
438
439 if (base == NULL) return NULL;
440 }
413 441
414 #ifdef DEBUG 442 #ifdef DEBUG
415 ZapBlock(reinterpret_cast<Address>(mem), alloced); 443 ZapBlock(base, chunk_size);
416 #endif 444 #endif
417 Counters::memory_allocated.Increment(alloced); 445 Counters::memory_allocated.Increment(chunk_size);
418 return mem; 446
419 } 447 LOG(NewEvent("MemoryChunk", base, chunk_size));
420 448 if (owner != NULL) {
421 449 ObjectSpace space = static_cast<ObjectSpace>(1 << owner->identity());
422 void MemoryAllocator::FreeRawMemory(void* mem, 450 PerformAllocationCallback(space, kAllocationActionAllocate, chunk_size);
423 size_t length, 451 }
452
453 return MemoryChunk::Initialize(base, chunk_size, executable, owner);
454 }
455
456
457 Page* MemoryAllocator::AllocatePage(PagedSpace* owner,
424 Executability executable) { 458 Executability executable) {
425 #ifdef DEBUG 459 MemoryChunk* chunk = AllocateChunk(Page::kObjectAreaSize, executable, owner);
426 ZapBlock(reinterpret_cast<Address>(mem), length); 460
427 #endif 461 if (chunk == NULL) return NULL;
428 if (CodeRange::contains(static_cast<Address>(mem))) { 462
429 CodeRange::FreeRawMemory(mem, length); 463 return Page::Initialize(chunk);
430 } else { 464 }
431 OS::Free(mem, length); 465
432 } 466
433 Counters::memory_allocated.Decrement(static_cast<int>(length)); 467 LargePage* MemoryAllocator::AllocateLargePage(intptr_t object_size,
434 size_ -= static_cast<int>(length); 468 Executability executable,
435 if (executable == EXECUTABLE) size_executable_ -= static_cast<int>(length); 469 Space* owner) {
436 470 MemoryChunk* chunk = AllocateChunk(object_size, executable, owner);
437 ASSERT(size_ >= 0); 471 if (chunk == NULL) return NULL;
438 ASSERT(size_executable_ >= 0); 472 return LargePage::Initialize(chunk);
439 } 473 }
440 474
441 475
442 void MemoryAllocator::PerformAllocationCallback(ObjectSpace space, 476 void MemoryAllocator::Free(MemoryChunk* chunk) {
443 AllocationAction action, 477 LOG(DeleteEvent("MemoryChunk", chunk));
444 size_t size) { 478 if (chunk->owner() != NULL) {
445 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { 479 ObjectSpace space =
446 MemoryAllocationCallbackRegistration registration = 480 static_cast<ObjectSpace>(1 << chunk->owner()->identity());
447 memory_allocation_callbacks_[i]; 481 PerformAllocationCallback(space, kAllocationActionFree, chunk->size());
448 if ((registration.space & space) == space && 482 }
449 (registration.action & action) == action) 483
450 registration.callback(space, action, static_cast<int>(size)); 484 FreeMemory(chunk->address(),
451 } 485 chunk->size(),
452 } 486 chunk->executable());
453 487 }
454 488
455 bool MemoryAllocator::MemoryAllocationCallbackRegistered( 489
456 MemoryAllocationCallback callback) { 490 bool MemoryAllocator::CommitBlock(Address start,
457 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { 491 size_t size,
458 if (memory_allocation_callbacks_[i].callback == callback) return true; 492 Executability executable) {
459 } 493 if (!VirtualMemory::CommitRegion(start, size, executable)) return false;
460 return false;
461 }
462
463
464 void MemoryAllocator::AddMemoryAllocationCallback(
465 MemoryAllocationCallback callback,
466 ObjectSpace space,
467 AllocationAction action) {
468 ASSERT(callback != NULL);
469 MemoryAllocationCallbackRegistration registration(callback, space, action);
470 ASSERT(!MemoryAllocator::MemoryAllocationCallbackRegistered(callback));
471 return memory_allocation_callbacks_.Add(registration);
472 }
473
474
475 void MemoryAllocator::RemoveMemoryAllocationCallback(
476 MemoryAllocationCallback callback) {
477 ASSERT(callback != NULL);
478 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) {
479 if (memory_allocation_callbacks_[i].callback == callback) {
480 memory_allocation_callbacks_.Remove(i);
481 return;
482 }
483 }
484 UNREACHABLE();
485 }
486
487 void* MemoryAllocator::ReserveInitialChunk(const size_t requested) {
488 ASSERT(initial_chunk_ == NULL);
489
490 initial_chunk_ = new VirtualMemory(requested);
491 CHECK(initial_chunk_ != NULL);
492 if (!initial_chunk_->IsReserved()) {
493 delete initial_chunk_;
494 initial_chunk_ = NULL;
495 return NULL;
496 }
497
498 // We are sure that we have mapped a block of requested addresses.
499 ASSERT(initial_chunk_->size() == requested);
500 LOG(NewEvent("InitialChunk", initial_chunk_->address(), requested));
501 size_ += static_cast<int>(requested);
502 return initial_chunk_->address();
503 }
504
505
506 static int PagesInChunk(Address start, size_t size) {
507 // The first page starts on the first page-aligned address from start onward
508 // and the last page ends on the last page-aligned address before
509 // start+size. Page::kPageSize is a power of two so we can divide by
510 // shifting.
511 return static_cast<int>((RoundDown(start + size, Page::kPageSize)
512 - RoundUp(start, Page::kPageSize)) >> kPageSizeBits);
513 }
514
515
516 Page* MemoryAllocator::AllocatePages(int requested_pages,
517 int* allocated_pages,
518 PagedSpace* owner) {
519 if (requested_pages <= 0) return Page::FromAddress(NULL);
520 size_t chunk_size = requested_pages * Page::kPageSize;
521
522 void* chunk = AllocateRawMemory(chunk_size, &chunk_size, owner->executable());
523 if (chunk == NULL) return Page::FromAddress(NULL);
524 LOG(NewEvent("PagedChunk", chunk, chunk_size));
525
526 *allocated_pages = PagesInChunk(static_cast<Address>(chunk), chunk_size);
527 // We may 'lose' a page due to alignment.
528 ASSERT(*allocated_pages >= kPagesPerChunk - 1);
529 if (*allocated_pages == 0) {
530 FreeRawMemory(chunk, chunk_size, owner->executable());
531 LOG(DeleteEvent("PagedChunk", chunk));
532 return Page::FromAddress(NULL);
533 }
534
535 int chunk_id = Pop();
536 chunks_[chunk_id].init(static_cast<Address>(chunk), chunk_size, owner);
537
538 ObjectSpace space = static_cast<ObjectSpace>(1 << owner->identity());
539 PerformAllocationCallback(space, kAllocationActionAllocate, chunk_size);
540 Page* new_pages = InitializePagesInChunk(chunk_id, *allocated_pages, owner);
541
542 AddToAllocatedChunks(static_cast<Address>(chunk), chunk_size);
543
544 return new_pages;
545 }
546
547
548 Page* MemoryAllocator::CommitPages(Address start, size_t size,
549 PagedSpace* owner, int* num_pages) {
550 ASSERT(start != NULL);
551 *num_pages = PagesInChunk(start, size);
552 ASSERT(*num_pages > 0);
553 ASSERT(initial_chunk_ != NULL);
554 ASSERT(InInitialChunk(start));
555 ASSERT(InInitialChunk(start + size - 1));
556 if (!initial_chunk_->Commit(start, size, owner->executable() == EXECUTABLE)) {
557 return Page::FromAddress(NULL);
558 }
559 #ifdef DEBUG 494 #ifdef DEBUG
560 ZapBlock(start, size); 495 ZapBlock(start, size);
561 #endif 496 #endif
562 Counters::memory_allocated.Increment(static_cast<int>(size));
563
564 // So long as we correctly overestimated the number of chunks we should not
565 // run out of chunk ids.
566 CHECK(!OutOfChunkIds());
567 int chunk_id = Pop();
568 chunks_[chunk_id].init(start, size, owner);
569 return InitializePagesInChunk(chunk_id, *num_pages, owner);
570 }
571
572
573 bool MemoryAllocator::CommitBlock(Address start,
574 size_t size,
575 Executability executable) {
576 ASSERT(start != NULL);
577 ASSERT(size > 0);
578 ASSERT(initial_chunk_ != NULL);
579 ASSERT(InInitialChunk(start));
580 ASSERT(InInitialChunk(start + size - 1));
581
582 if (!initial_chunk_->Commit(start, size, executable)) return false;
583 #ifdef DEBUG
584 ZapBlock(start, size);
585 #endif
586 Counters::memory_allocated.Increment(static_cast<int>(size)); 497 Counters::memory_allocated.Increment(static_cast<int>(size));
587 return true; 498 return true;
588 } 499 }
589 500
590 501
591 bool MemoryAllocator::UncommitBlock(Address start, size_t size) { 502 bool MemoryAllocator::UncommitBlock(Address start, size_t size) {
592 ASSERT(start != NULL); 503 if (!VirtualMemory::UncommitRegion(start, size)) return false;
593 ASSERT(size > 0);
594 ASSERT(initial_chunk_ != NULL);
595 ASSERT(InInitialChunk(start));
596 ASSERT(InInitialChunk(start + size - 1));
597
598 if (!initial_chunk_->Uncommit(start, size)) return false;
599 Counters::memory_allocated.Decrement(static_cast<int>(size)); 504 Counters::memory_allocated.Decrement(static_cast<int>(size));
600 return true; 505 return true;
601 } 506 }
602 507
603 508
604 void MemoryAllocator::ZapBlock(Address start, size_t size) { 509 void MemoryAllocator::ZapBlock(Address start, size_t size) {
605 for (size_t s = 0; s + kPointerSize <= size; s += kPointerSize) { 510 for (size_t s = 0; s + kPointerSize <= size; s += kPointerSize) {
606 Memory::Address_at(start + s) = kZapValue; 511 Memory::Address_at(start + s) = kZapValue;
607 } 512 }
608 } 513 }
609 514
610 515
611 Page* MemoryAllocator::InitializePagesInChunk(int chunk_id, int pages_in_chunk, 516 void MemoryAllocator::PerformAllocationCallback(ObjectSpace space,
612 PagedSpace* owner) { 517 AllocationAction action,
613 ASSERT(IsValidChunk(chunk_id)); 518 size_t size) {
614 ASSERT(pages_in_chunk > 0); 519 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) {
615 520 MemoryAllocationCallbackRegistration registration =
616 Address chunk_start = chunks_[chunk_id].address(); 521 memory_allocation_callbacks_[i];
617 522 if ((registration.space & space) == space &&
618 Address low = RoundUp(chunk_start, Page::kPageSize); 523 (registration.action & action) == action)
619 524 registration.callback(space, action, static_cast<int>(size));
620 #ifdef DEBUG
621 size_t chunk_size = chunks_[chunk_id].size();
622 Address high = RoundDown(chunk_start + chunk_size, Page::kPageSize);
623 ASSERT(pages_in_chunk <=
624 ((OffsetFrom(high) - OffsetFrom(low)) / Page::kPageSize));
625 #endif
626
627 Address page_addr = low;
628 for (int i = 0; i < pages_in_chunk; i++) {
629 Page* p = Page::FromAddress(page_addr);
630 p->opaque_header = OffsetFrom(page_addr + Page::kPageSize) | chunk_id;
631 p->InvalidateWatermark(true);
632 p->SetIsLargeObjectPage(false);
633 p->SetAllocationWatermark(p->ObjectAreaStart());
634 p->SetCachedAllocationWatermark(p->ObjectAreaStart());
635 page_addr += Page::kPageSize;
636 }
637
638 // Set the next page of the last page to 0.
639 Page* last_page = Page::FromAddress(page_addr - Page::kPageSize);
640 last_page->opaque_header = OffsetFrom(0) | chunk_id;
641
642 return Page::FromAddress(low);
643 }
644
645
646 Page* MemoryAllocator::FreePages(Page* p) {
647 if (!p->is_valid()) return p;
648
649 // Find the first page in the same chunk as 'p'
650 Page* first_page = FindFirstPageInSameChunk(p);
651 Page* page_to_return = Page::FromAddress(NULL);
652
653 if (p != first_page) {
654 // Find the last page in the same chunk as 'prev'.
655 Page* last_page = FindLastPageInSameChunk(p);
656 first_page = GetNextPage(last_page); // first page in next chunk
657
658 // set the next_page of last_page to NULL
659 SetNextPage(last_page, Page::FromAddress(NULL));
660 page_to_return = p; // return 'p' when exiting
661 }
662
663 while (first_page->is_valid()) {
664 int chunk_id = GetChunkId(first_page);
665 ASSERT(IsValidChunk(chunk_id));
666
667 // Find the first page of the next chunk before deleting this chunk.
668 first_page = GetNextPage(FindLastPageInSameChunk(first_page));
669
670 // Free the current chunk.
671 DeleteChunk(chunk_id);
672 }
673
674 return page_to_return;
675 }
676
677
678 void MemoryAllocator::FreeAllPages(PagedSpace* space) {
679 for (int i = 0, length = chunks_.length(); i < length; i++) {
680 if (chunks_[i].owner() == space) {
681 DeleteChunk(i);
682 }
683 } 525 }
684 } 526 }
685 527
686 528
687 void MemoryAllocator::DeleteChunk(int chunk_id) { 529 bool MemoryAllocator::MemoryAllocationCallbackRegistered(
688 ASSERT(IsValidChunk(chunk_id)); 530 MemoryAllocationCallback callback) {
689 531 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) {
690 ChunkInfo& c = chunks_[chunk_id]; 532 if (memory_allocation_callbacks_[i].callback == callback) return true;
691
692 // We cannot free a chunk contained in the initial chunk because it was not
693 // allocated with AllocateRawMemory. Instead we uncommit the virtual
694 // memory.
695 if (InInitialChunk(c.address())) {
696 // TODO(1240712): VirtualMemory::Uncommit has a return value which
697 // is ignored here.
698 initial_chunk_->Uncommit(c.address(), c.size());
699 Counters::memory_allocated.Decrement(static_cast<int>(c.size()));
700 } else {
701 RemoveFromAllocatedChunks(c.address(), c.size());
702 LOG(DeleteEvent("PagedChunk", c.address()));
703 ObjectSpace space = static_cast<ObjectSpace>(1 << c.owner()->identity());
704 size_t size = c.size();
705 FreeRawMemory(c.address(), size, c.executable());
706 PerformAllocationCallback(space, kAllocationActionFree, size);
707 } 533 }
708 c.init(NULL, 0, NULL); 534 return false;
709 Push(chunk_id);
710 } 535 }
711 536
712 537
713 Page* MemoryAllocator::FindFirstPageInSameChunk(Page* p) { 538 void MemoryAllocator::AddMemoryAllocationCallback(
714 int chunk_id = GetChunkId(p); 539 MemoryAllocationCallback callback,
715 ASSERT(IsValidChunk(chunk_id)); 540 ObjectSpace space,
716 541 AllocationAction action) {
717 Address low = RoundUp(chunks_[chunk_id].address(), Page::kPageSize); 542 ASSERT(callback != NULL);
718 return Page::FromAddress(low); 543 MemoryAllocationCallbackRegistration registration(callback, space, action);
544 ASSERT(!MemoryAllocator::MemoryAllocationCallbackRegistered(callback));
545 return memory_allocation_callbacks_.Add(registration);
719 } 546 }
720 547
721 548
722 Page* MemoryAllocator::FindLastPageInSameChunk(Page* p) { 549 void MemoryAllocator::RemoveMemoryAllocationCallback(
723 int chunk_id = GetChunkId(p); 550 MemoryAllocationCallback callback) {
724 ASSERT(IsValidChunk(chunk_id)); 551 ASSERT(callback != NULL);
725 552 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) {
726 Address chunk_start = chunks_[chunk_id].address(); 553 if (memory_allocation_callbacks_[i].callback == callback) {
727 size_t chunk_size = chunks_[chunk_id].size(); 554 memory_allocation_callbacks_.Remove(i);
728 555 return;
729 Address high = RoundDown(chunk_start + chunk_size, Page::kPageSize); 556 }
730 ASSERT(chunk_start <= p->address() && p->address() < high); 557 }
731 558 UNREACHABLE();
732 return Page::FromAddress(high - Page::kPageSize);
733 } 559 }
734 560
735 561
736 #ifdef DEBUG 562 #ifdef DEBUG
737 void MemoryAllocator::ReportStatistics() { 563 void MemoryAllocator::ReportStatistics() {
738 float pct = static_cast<float>(capacity_ - size_) / capacity_; 564 float pct = static_cast<float>(capacity_ - size_) / capacity_;
739 PrintF(" capacity: %" V8_PTR_PREFIX "d" 565 PrintF(" capacity: %" V8_PTR_PREFIX "d"
740 ", used: %" V8_PTR_PREFIX "d" 566 ", used: %" V8_PTR_PREFIX "d"
741 ", available: %%%d\n\n", 567 ", available: %%%d\n\n",
742 capacity_, size_, static_cast<int>(pct*100)); 568 capacity_, size_, static_cast<int>(pct*100));
743 } 569 }
744 #endif 570 #endif
745 571
746
747 void MemoryAllocator::RelinkPageListInChunkOrder(PagedSpace* space,
748 Page** first_page,
749 Page** last_page,
750 Page** last_page_in_use) {
751 Page* first = NULL;
752 Page* last = NULL;
753
754 for (int i = 0, length = chunks_.length(); i < length; i++) {
755 ChunkInfo& chunk = chunks_[i];
756
757 if (chunk.owner() == space) {
758 if (first == NULL) {
759 Address low = RoundUp(chunk.address(), Page::kPageSize);
760 first = Page::FromAddress(low);
761 }
762 last = RelinkPagesInChunk(i,
763 chunk.address(),
764 chunk.size(),
765 last,
766 last_page_in_use);
767 }
768 }
769
770 if (first_page != NULL) {
771 *first_page = first;
772 }
773
774 if (last_page != NULL) {
775 *last_page = last;
776 }
777 }
778
779
780 Page* MemoryAllocator::RelinkPagesInChunk(int chunk_id,
781 Address chunk_start,
782 size_t chunk_size,
783 Page* prev,
784 Page** last_page_in_use) {
785 Address page_addr = RoundUp(chunk_start, Page::kPageSize);
786 int pages_in_chunk = PagesInChunk(chunk_start, chunk_size);
787
788 if (prev->is_valid()) {
789 SetNextPage(prev, Page::FromAddress(page_addr));
790 }
791
792 for (int i = 0; i < pages_in_chunk; i++) {
793 Page* p = Page::FromAddress(page_addr);
794 p->opaque_header = OffsetFrom(page_addr + Page::kPageSize) | chunk_id;
795 page_addr += Page::kPageSize;
796
797 p->InvalidateWatermark(true);
798 if (p->WasInUseBeforeMC()) {
799 *last_page_in_use = p;
800 }
801 }
802
803 // Set the next page of the last page to 0.
804 Page* last_page = Page::FromAddress(page_addr - Page::kPageSize);
805 last_page->opaque_header = OffsetFrom(0) | chunk_id;
806
807 if (last_page->WasInUseBeforeMC()) {
808 *last_page_in_use = last_page;
809 }
810
811 return last_page;
812 }
813
814
815 void MemoryAllocator::AddToAllocatedChunks(Address addr, intptr_t size) {
816 ASSERT(size == kChunkSize);
817 uintptr_t int_address = reinterpret_cast<uintptr_t>(addr);
818 AddChunkUsingAddress(int_address, int_address);
819 AddChunkUsingAddress(int_address, int_address + size - 1);
820 }
821
822
823 void MemoryAllocator::AddChunkUsingAddress(uintptr_t chunk_start,
824 uintptr_t chunk_index_base) {
825 uintptr_t* fine_grained = AllocatedChunksFinder(
826 chunk_table_,
827 chunk_index_base,
828 kChunkSizeLog2 + (kChunkTableLevels - 1) * kChunkTableBitsPerLevel,
829 kCreateTablesAsNeeded);
830 int index = FineGrainedIndexForAddress(chunk_index_base);
831 if (fine_grained[index] != kUnusedChunkTableEntry) index++;
832 ASSERT(fine_grained[index] == kUnusedChunkTableEntry);
833 fine_grained[index] = chunk_start;
834 }
835
836
837 void MemoryAllocator::RemoveFromAllocatedChunks(Address addr, intptr_t size) {
838 ASSERT(size == kChunkSize);
839 uintptr_t int_address = reinterpret_cast<uintptr_t>(addr);
840 RemoveChunkFoundUsingAddress(int_address, int_address);
841 RemoveChunkFoundUsingAddress(int_address, int_address + size - 1);
842 }
843
844
845 void MemoryAllocator::RemoveChunkFoundUsingAddress(
846 uintptr_t chunk_start,
847 uintptr_t chunk_index_base) {
848 uintptr_t* fine_grained = AllocatedChunksFinder(
849 chunk_table_,
850 chunk_index_base,
851 kChunkSizeLog2 + (kChunkTableLevels - 1) * kChunkTableBitsPerLevel,
852 kDontCreateTables);
853 // Can't remove an entry that's not there.
854 ASSERT(fine_grained != kUnusedChunkTableEntry);
855 int index = FineGrainedIndexForAddress(chunk_index_base);
856 ASSERT(fine_grained[index] != kUnusedChunkTableEntry);
857 if (fine_grained[index] != chunk_start) {
858 index++;
859 ASSERT(fine_grained[index] == chunk_start);
860 fine_grained[index] = kUnusedChunkTableEntry;
861 } else {
862 // If only one of the entries is used it must be the first, since
863 // InAllocatedChunks relies on that. Move things around so that this is
864 // the case.
865 fine_grained[index] = fine_grained[index + 1];
866 fine_grained[index + 1] = kUnusedChunkTableEntry;
867 }
868 }
869
870
871 bool MemoryAllocator::InAllocatedChunks(Address addr) {
872 uintptr_t int_address = reinterpret_cast<uintptr_t>(addr);
873 uintptr_t* fine_grained = AllocatedChunksFinder(
874 chunk_table_,
875 int_address,
876 kChunkSizeLog2 + (kChunkTableLevels - 1) * kChunkTableBitsPerLevel,
877 kDontCreateTables);
878 if (fine_grained == NULL) return false;
879 int index = FineGrainedIndexForAddress(int_address);
880 if (fine_grained[index] == kUnusedChunkTableEntry) return false;
881 uintptr_t entry = fine_grained[index];
882 if (entry <= int_address && entry + kChunkSize > int_address) return true;
883 index++;
884 if (fine_grained[index] == kUnusedChunkTableEntry) return false;
885 entry = fine_grained[index];
886 if (entry <= int_address && entry + kChunkSize > int_address) return true;
887 return false;
888 }
889
890
891 uintptr_t* MemoryAllocator::AllocatedChunksFinder(
892 uintptr_t* table,
893 uintptr_t address,
894 int bit_position,
895 CreateTables create_as_needed) {
896 if (bit_position == kChunkSizeLog2) {
897 return table;
898 }
899 ASSERT(bit_position >= kChunkSizeLog2 + kChunkTableBitsPerLevel);
900 int index =
901 ((address >> bit_position) &
902 ((V8_INTPTR_C(1) << kChunkTableBitsPerLevel) - 1));
903 uintptr_t more_fine_grained_address =
904 address & ((V8_INTPTR_C(1) << bit_position) - 1);
905 ASSERT((table == chunk_table_ && index < kChunkTableTopLevelEntries) ||
906 (table != chunk_table_ && index < 1 << kChunkTableBitsPerLevel));
907 uintptr_t* more_fine_grained_table =
908 reinterpret_cast<uintptr_t*>(table[index]);
909 if (more_fine_grained_table == kUnusedChunkTableEntry) {
910 if (create_as_needed == kDontCreateTables) return NULL;
911 int words_needed = 1 << kChunkTableBitsPerLevel;
912 if (bit_position == kChunkTableBitsPerLevel + kChunkSizeLog2) {
913 words_needed =
914 (1 << kChunkTableBitsPerLevel) * kChunkTableFineGrainedWordsPerEntry;
915 }
916 more_fine_grained_table = new uintptr_t[words_needed];
917 for (int i = 0; i < words_needed; i++) {
918 more_fine_grained_table[i] = kUnusedChunkTableEntry;
919 }
920 table[index] = reinterpret_cast<uintptr_t>(more_fine_grained_table);
921 }
922 return AllocatedChunksFinder(
923 more_fine_grained_table,
924 more_fine_grained_address,
925 bit_position - kChunkTableBitsPerLevel,
926 create_as_needed);
927 }
928
929
930 uintptr_t MemoryAllocator::chunk_table_[kChunkTableTopLevelEntries];
931
932
933 // ----------------------------------------------------------------------------- 572 // -----------------------------------------------------------------------------
934 // PagedSpace implementation 573 // PagedSpace implementation
935 574
936 PagedSpace::PagedSpace(intptr_t max_capacity, 575 PagedSpace::PagedSpace(intptr_t max_capacity,
937 AllocationSpace id, 576 AllocationSpace id,
938 Executability executable) 577 Executability executable)
939 : Space(id, executable) { 578 : Space(id, executable) {
940 max_capacity_ = (RoundDown(max_capacity, Page::kPageSize) / Page::kPageSize) 579 max_capacity_ = (RoundDown(max_capacity, Page::kPageSize) / Page::kPageSize)
941 * Page::kObjectAreaSize; 580 * Page::kObjectAreaSize;
942 accounting_stats_.Clear(); 581 accounting_stats_.Clear();
943 582
944 allocation_info_.top = NULL; 583 allocation_info_.top = NULL;
945 allocation_info_.limit = NULL; 584 allocation_info_.limit = NULL;
946
947 mc_forwarding_info_.top = NULL;
948 mc_forwarding_info_.limit = NULL;
949 } 585 }
950 586
951 587
952 bool PagedSpace::Setup(Address start, size_t size) { 588 bool PagedSpace::Setup() {
953 if (HasBeenSetup()) return false; 589 if (HasBeenSetup()) return false;
954 590
955 int num_pages = 0; 591 // Maximum space capacity can not be less than single page size.
956 // Try to use the virtual memory range passed to us. If it is too small to 592 if (max_capacity_ < Page::kPageSize) return false;
957 // contain at least one page, ignore it and allocate instead. 593
958 int pages_in_chunk = PagesInChunk(start, size); 594 first_page_ = MemoryAllocator::AllocatePage(this, executable());
959 if (pages_in_chunk > 0) { 595 if (!first_page_->is_valid()) return false;
960 first_page_ = MemoryAllocator::CommitPages(RoundUp(start, Page::kPageSize),
961 Page::kPageSize * pages_in_chunk,
962 this, &num_pages);
963 } else {
964 int requested_pages =
965 Min(MemoryAllocator::kPagesPerChunk,
966 static_cast<int>(max_capacity_ / Page::kObjectAreaSize));
967 first_page_ =
968 MemoryAllocator::AllocatePages(requested_pages, &num_pages, this);
969 if (!first_page_->is_valid()) return false;
970 }
971 596
972 // We are sure that the first page is valid and that we have at least one 597 // We are sure that the first page is valid and that we have at least one
973 // page. 598 // page.
974 ASSERT(first_page_->is_valid()); 599 accounting_stats_.ExpandSpace(Page::kObjectAreaSize);
975 ASSERT(num_pages > 0);
976 accounting_stats_.ExpandSpace(num_pages * Page::kObjectAreaSize);
977 ASSERT(Capacity() <= max_capacity_); 600 ASSERT(Capacity() <= max_capacity_);
978 601
979 // Sequentially clear region marks in the newly allocated 602 last_page_ = first_page_;
980 // pages and cache the current last page in the space. 603 ASSERT(!last_page_->next_page()->is_valid());
981 for (Page* p = first_page_; p->is_valid(); p = p->next_page()) {
982 p->SetRegionMarks(Page::kAllRegionsCleanMarks);
983 last_page_ = p;
984 }
985 604
986 // Use first_page_ for allocation. 605 // Use first_page_ for allocation.
987 SetAllocationInfo(&allocation_info_, first_page_); 606 SetAllocationInfo(&allocation_info_, first_page_);
988 607
989 page_list_is_chunk_ordered_ = true;
990
991 return true; 608 return true;
992 } 609 }
993 610
994 611
995 bool PagedSpace::HasBeenSetup() { 612 bool PagedSpace::HasBeenSetup() {
996 return (Capacity() > 0); 613 return (Capacity() > 0);
997 } 614 }
998 615
999 616
1000 void PagedSpace::TearDown() { 617 void PagedSpace::TearDown() {
1001 MemoryAllocator::FreeAllPages(this); 618 Page* next = NULL;
619 for (Page* p = first_page_; p->is_valid(); p = next) {
620 next = p->next_page();
621 MemoryAllocator::Free(p);
622 }
1002 first_page_ = NULL; 623 first_page_ = NULL;
624 last_page_ = NULL;
1003 accounting_stats_.Clear(); 625 accounting_stats_.Clear();
1004 } 626 }
1005 627
1006 628
1007 #ifdef ENABLE_HEAP_PROTECTION 629 #ifdef ENABLE_HEAP_PROTECTION
1008 630
1009 void PagedSpace::Protect() { 631 void PagedSpace::Protect() {
1010 Page* page = first_page_; 632 Page* page = first_page_;
1011 while (page->is_valid()) { 633 while (page->is_valid()) {
1012 MemoryAllocator::ProtectChunkFromPage(page); 634 MemoryAllocator::ProtectChunkFromPage(page);
1013 page = MemoryAllocator::FindLastPageInSameChunk(page)->next_page(); 635 page = MemoryAllocator::FindLastPageInSameChunk(page)->next_page();
1014 } 636 }
1015 } 637 }
1016 638
1017 639
1018 void PagedSpace::Unprotect() { 640 void PagedSpace::Unprotect() {
1019 Page* page = first_page_; 641 Page* page = first_page_;
1020 while (page->is_valid()) { 642 while (page->is_valid()) {
1021 MemoryAllocator::UnprotectChunkFromPage(page); 643 MemoryAllocator::UnprotectChunkFromPage(page);
1022 page = MemoryAllocator::FindLastPageInSameChunk(page)->next_page(); 644 page = MemoryAllocator::FindLastPageInSameChunk(page)->next_page();
1023 } 645 }
1024 } 646 }
1025 647
1026 #endif 648 #endif
1027 649
1028 650
1029 void PagedSpace::MarkAllPagesClean() {
1030 PageIterator it(this, PageIterator::ALL_PAGES);
1031 while (it.has_next()) {
1032 it.next()->SetRegionMarks(Page::kAllRegionsCleanMarks);
1033 }
1034 }
1035
1036
1037 MaybeObject* PagedSpace::FindObject(Address addr) { 651 MaybeObject* PagedSpace::FindObject(Address addr) {
1038 // Note: this function can only be called before or after mark-compact GC 652 // Note: this function can only be called before or after mark-compact GC
1039 // because it accesses map pointers. 653 // because it accesses map pointers.
1040 ASSERT(!MarkCompactCollector::in_use()); 654 ASSERT(!MarkCompactCollector::in_use());
1041 655
1042 if (!Contains(addr)) return Failure::Exception(); 656 if (!Contains(addr)) return Failure::Exception();
1043 657
1044 Page* p = Page::FromAddress(addr); 658 Page* p = Page::FromAddress(addr);
1045 ASSERT(IsUsed(p)); 659 ASSERT(IsUsed(p));
1046 Address cur = p->ObjectAreaStart(); 660 Address cur = p->ObjectAreaStart();
(...skipping 19 matching lines...) Expand all
1066 } 680 }
1067 681
1068 682
1069 void PagedSpace::SetAllocationInfo(AllocationInfo* alloc_info, Page* p) { 683 void PagedSpace::SetAllocationInfo(AllocationInfo* alloc_info, Page* p) {
1070 alloc_info->top = p->ObjectAreaStart(); 684 alloc_info->top = p->ObjectAreaStart();
1071 alloc_info->limit = p->ObjectAreaEnd(); 685 alloc_info->limit = p->ObjectAreaEnd();
1072 ASSERT(alloc_info->VerifyPagedAllocation()); 686 ASSERT(alloc_info->VerifyPagedAllocation());
1073 } 687 }
1074 688
1075 689
1076 void PagedSpace::MCResetRelocationInfo() { 690 bool PagedSpace::Expand() {
1077 // Set page indexes.
1078 int i = 0;
1079 PageIterator it(this, PageIterator::ALL_PAGES);
1080 while (it.has_next()) {
1081 Page* p = it.next();
1082 p->mc_page_index = i++;
1083 }
1084
1085 // Set mc_forwarding_info_ to the first page in the space.
1086 SetAllocationInfo(&mc_forwarding_info_, first_page_);
1087 // All the bytes in the space are 'available'. We will rediscover
1088 // allocated and wasted bytes during GC.
1089 accounting_stats_.Reset();
1090 }
1091
1092
1093 int PagedSpace::MCSpaceOffsetForAddress(Address addr) {
1094 #ifdef DEBUG
1095 // The Contains function considers the address at the beginning of a
1096 // page in the page, MCSpaceOffsetForAddress considers it is in the
1097 // previous page.
1098 if (Page::IsAlignedToPageSize(addr)) {
1099 ASSERT(Contains(addr - kPointerSize));
1100 } else {
1101 ASSERT(Contains(addr));
1102 }
1103 #endif
1104
1105 // If addr is at the end of a page, it belongs to previous page
1106 Page* p = Page::IsAlignedToPageSize(addr)
1107 ? Page::FromAllocationTop(addr)
1108 : Page::FromAddress(addr);
1109 int index = p->mc_page_index;
1110 return (index * Page::kPageSize) + p->Offset(addr);
1111 }
1112
1113
1114 // Slow case for reallocating and promoting objects during a compacting
1115 // collection. This function is not space-specific.
1116 HeapObject* PagedSpace::SlowMCAllocateRaw(int size_in_bytes) {
1117 Page* current_page = TopPageOf(mc_forwarding_info_);
1118 if (!current_page->next_page()->is_valid()) {
1119 if (!Expand(current_page)) {
1120 return NULL;
1121 }
1122 }
1123
1124 // There are surely more pages in the space now.
1125 ASSERT(current_page->next_page()->is_valid());
1126 // We do not add the top of page block for current page to the space's
1127 // free list---the block may contain live objects so we cannot write
1128 // bookkeeping information to it. Instead, we will recover top of page
1129 // blocks when we move objects to their new locations.
1130 //
1131 // We do however write the allocation pointer to the page. The encoding
1132 // of forwarding addresses is as an offset in terms of live bytes, so we
1133 // need quick access to the allocation top of each page to decode
1134 // forwarding addresses.
1135 current_page->SetAllocationWatermark(mc_forwarding_info_.top);
1136 current_page->next_page()->InvalidateWatermark(true);
1137 SetAllocationInfo(&mc_forwarding_info_, current_page->next_page());
1138 return AllocateLinearly(&mc_forwarding_info_, size_in_bytes);
1139 }
1140
1141
1142 bool PagedSpace::Expand(Page* last_page) {
1143 ASSERT(max_capacity_ % Page::kObjectAreaSize == 0); 691 ASSERT(max_capacity_ % Page::kObjectAreaSize == 0);
1144 ASSERT(Capacity() % Page::kObjectAreaSize == 0); 692 ASSERT(Capacity() % Page::kObjectAreaSize == 0);
1145 693
1146 if (Capacity() == max_capacity_) return false; 694 if (Capacity() == max_capacity_) return false;
1147 695
1148 ASSERT(Capacity() < max_capacity_); 696 ASSERT(Capacity() < max_capacity_);
1149 // Last page must be valid and its next page is invalid. 697 // Last page must be valid and its next page is invalid.
1150 ASSERT(last_page->is_valid() && !last_page->next_page()->is_valid()); 698 ASSERT(last_page_->is_valid() && !last_page_->next_page()->is_valid());
1151 699
1152 int available_pages = 700 // We are going to exceed capacity for this space.
1153 static_cast<int>((max_capacity_ - Capacity()) / Page::kObjectAreaSize); 701 if ((Capacity() + Page::kPageSize) > max_capacity_) return false;
1154 // We don't want to have to handle small chunks near the end so if there are
1155 // not kPagesPerChunk pages available without exceeding the max capacity then
1156 // act as if memory has run out.
1157 if (available_pages < MemoryAllocator::kPagesPerChunk) return false;
1158 702
1159 int desired_pages = Min(available_pages, MemoryAllocator::kPagesPerChunk); 703 Page* p = MemoryAllocator::AllocatePage(this, executable());
1160 Page* p = MemoryAllocator::AllocatePages(desired_pages, &desired_pages, this);
1161 if (!p->is_valid()) return false; 704 if (!p->is_valid()) return false;
1162 705
1163 accounting_stats_.ExpandSpace(desired_pages * Page::kObjectAreaSize); 706 accounting_stats_.ExpandSpace(Page::kObjectAreaSize);
1164 ASSERT(Capacity() <= max_capacity_); 707 ASSERT(Capacity() <= max_capacity_);
1165 708
1166 MemoryAllocator::SetNextPage(last_page, p); 709 last_page_->set_next_page(p);
710 last_page_ = p;
1167 711
1168 // Sequentially clear region marks of new pages and and cache the 712 ASSERT(!last_page_->next_page()->is_valid());
1169 // new last page in the space.
1170 while (p->is_valid()) {
1171 p->SetRegionMarks(Page::kAllRegionsCleanMarks);
1172 last_page_ = p;
1173 p = p->next_page();
1174 }
1175 713
1176 return true; 714 return true;
1177 } 715 }
1178 716
1179 717
1180 #ifdef DEBUG 718 #ifdef DEBUG
1181 int PagedSpace::CountTotalPages() { 719 int PagedSpace::CountTotalPages() {
1182 int count = 0; 720 int count = 0;
1183 for (Page* p = first_page_; p->is_valid(); p = p->next_page()) { 721 for (Page* p = first_page_; p->is_valid(); p = p->next_page()) {
1184 count++; 722 count++;
1185 } 723 }
1186 return count; 724 return count;
1187 } 725 }
1188 #endif 726 #endif
1189 727
1190 728
1191 void PagedSpace::Shrink() { 729 void PagedSpace::Shrink() {
1192 if (!page_list_is_chunk_ordered_) {
1193 // We can't shrink space if pages is not chunk-ordered
1194 // (see comment for class MemoryAllocator for definition).
1195 return;
1196 }
1197
1198 // Release half of free pages.
1199 Page* top_page = AllocationTopPage(); 730 Page* top_page = AllocationTopPage();
1200 ASSERT(top_page->is_valid()); 731 ASSERT(top_page->is_valid());
1201 732
1202 // Count the number of pages we would like to free. 733 // TODO(gc) release half of pages?
1203 int pages_to_free = 0; 734 if (top_page->next_page()->is_valid()) {
1204 for (Page* p = top_page->next_page(); p->is_valid(); p = p->next_page()) { 735 int pages_freed = 0;
1205 pages_to_free++; 736 Page* page = top_page->next_page();
737 Page* next_page;
738 while (page->is_valid()) {
739 next_page = page->next_page();
740 MemoryAllocator::Free(page);
741 pages_freed++;
742 page = next_page;
743 }
744 top_page->set_next_page(Page::FromAddress(NULL));
745 last_page_ = top_page;
746
747 accounting_stats_.ShrinkSpace(pages_freed * Page::kObjectAreaSize);
748 ASSERT(Capacity() == CountTotalPages() * Page::kObjectAreaSize);
1206 } 749 }
1207
1208 // Free pages after top_page.
1209 Page* p = MemoryAllocator::FreePages(top_page->next_page());
1210 MemoryAllocator::SetNextPage(top_page, p);
1211
1212 // Find out how many pages we failed to free and update last_page_.
1213 // Please note pages can only be freed in whole chunks.
1214 last_page_ = top_page;
1215 for (Page* p = top_page->next_page(); p->is_valid(); p = p->next_page()) {
1216 pages_to_free--;
1217 last_page_ = p;
1218 }
1219
1220 accounting_stats_.ShrinkSpace(pages_to_free * Page::kObjectAreaSize);
1221 ASSERT(Capacity() == CountTotalPages() * Page::kObjectAreaSize);
1222 } 750 }
1223 751
1224 752
1225 bool PagedSpace::EnsureCapacity(int capacity) { 753 bool PagedSpace::EnsureCapacity(int capacity) {
1226 if (Capacity() >= capacity) return true; 754 while (Capacity() < capacity) {
1227 755 // Expand the space until it has the required capacity or expansion fails.
1228 // Start from the allocation top and loop to the last page in the space. 756 if (!Expand()) return false;
1229 Page* last_page = AllocationTopPage();
1230 Page* next_page = last_page->next_page();
1231 while (next_page->is_valid()) {
1232 last_page = MemoryAllocator::FindLastPageInSameChunk(next_page);
1233 next_page = last_page->next_page();
1234 } 757 }
1235
1236 // Expand the space until it has the required capacity or expansion fails.
1237 do {
1238 if (!Expand(last_page)) return false;
1239 ASSERT(last_page->next_page()->is_valid());
1240 last_page =
1241 MemoryAllocator::FindLastPageInSameChunk(last_page->next_page());
1242 } while (Capacity() < capacity);
1243
1244 return true; 758 return true;
1245 } 759 }
1246 760
1247 761
1248 #ifdef DEBUG 762 #ifdef DEBUG
1249 void PagedSpace::Print() { } 763 void PagedSpace::Print() { }
1250 #endif 764 #endif
1251 765
1252 766
1253 #ifdef DEBUG 767 #ifdef DEBUG
1254 // We do not assume that the PageIterator works, because it depends on the 768 // We do not assume that the PageIterator works, because it depends on the
1255 // invariants we are checking during verification. 769 // invariants we are checking during verification.
1256 void PagedSpace::Verify(ObjectVisitor* visitor) { 770 void PagedSpace::Verify(ObjectVisitor* visitor) {
1257 // The allocation pointer should be valid, and it should be in a page in the 771 // The allocation pointer should be valid, and it should be in a page in the
1258 // space. 772 // space.
1259 ASSERT(allocation_info_.VerifyPagedAllocation()); 773 ASSERT(allocation_info_.VerifyPagedAllocation());
1260 Page* top_page = Page::FromAllocationTop(allocation_info_.top); 774 Page* top_page = Page::FromAllocationTop(allocation_info_.top);
1261 ASSERT(MemoryAllocator::IsPageInSpace(top_page, this));
1262 775
1263 // Loop over all the pages. 776 // Loop over all the pages.
1264 bool above_allocation_top = false; 777 bool above_allocation_top = false;
1265 Page* current_page = first_page_; 778 Page* current_page = first_page_;
1266 while (current_page->is_valid()) { 779 while (current_page->is_valid()) {
1267 if (above_allocation_top) { 780 if (above_allocation_top) {
1268 // We don't care what's above the allocation top. 781 // We don't care what's above the allocation top.
1269 } else { 782 } else {
1270 Address top = current_page->AllocationTop(); 783 Address top = current_page->AllocationTop();
1271 if (current_page == top_page) { 784 if (current_page == top_page) {
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
1307 current_page = current_page->next_page(); 820 current_page = current_page->next_page();
1308 } 821 }
1309 } 822 }
1310 #endif 823 #endif
1311 824
1312 825
1313 // ----------------------------------------------------------------------------- 826 // -----------------------------------------------------------------------------
1314 // NewSpace implementation 827 // NewSpace implementation
1315 828
1316 829
1317 bool NewSpace::Setup(Address start, int size) { 830 bool NewSpace::Setup(int maximum_semispace_capacity) {
1318 // Setup new space based on the preallocated memory block defined by 831 // Setup new space based on the preallocated memory block defined by
1319 // start and size. The provided space is divided into two semi-spaces. 832 // start and size. The provided space is divided into two semi-spaces.
1320 // To support fast containment testing in the new space, the size of 833 // To support fast containment testing in the new space, the size of
1321 // this chunk must be a power of two and it must be aligned to its size. 834 // this chunk must be a power of two and it must be aligned to its size.
1322 int initial_semispace_capacity = Heap::InitialSemiSpaceSize(); 835 int initial_semispace_capacity = Heap::InitialSemiSpaceSize();
1323 int maximum_semispace_capacity = Heap::MaxSemiSpaceSize(); 836
837 size_t size = 0;
838 Address base =
839 MemoryAllocator::ReserveAlignedMemory(2 * maximum_semispace_capacity,
840 2 * maximum_semispace_capacity,
841 &size);
842
843 if (base == NULL) return false;
844
845 chunk_base_ = base;
846 chunk_size_ = static_cast<uintptr_t>(size);
847 LOG(NewEvent("InitialChunk", chunk_base_, chunk_size_));
1324 848
1325 ASSERT(initial_semispace_capacity <= maximum_semispace_capacity); 849 ASSERT(initial_semispace_capacity <= maximum_semispace_capacity);
1326 ASSERT(IsPowerOf2(maximum_semispace_capacity)); 850 ASSERT(IsPowerOf2(maximum_semispace_capacity));
1327 851
1328 // Allocate and setup the histogram arrays if necessary. 852 // Allocate and setup the histogram arrays if necessary.
1329 #if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING) 853 #if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
1330 allocated_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1); 854 allocated_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1);
1331 promoted_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1); 855 promoted_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1);
1332 856
1333 #define SET_NAME(name) allocated_histogram_[name].set_name(#name); \ 857 #define SET_NAME(name) allocated_histogram_[name].set_name(#name); \
1334 promoted_histogram_[name].set_name(#name); 858 promoted_histogram_[name].set_name(#name);
1335 INSTANCE_TYPE_LIST(SET_NAME) 859 INSTANCE_TYPE_LIST(SET_NAME)
1336 #undef SET_NAME 860 #undef SET_NAME
1337 #endif 861 #endif
1338 862
1339 ASSERT(size == 2 * Heap::ReservedSemiSpaceSize()); 863 ASSERT(maximum_semispace_capacity == Heap::ReservedSemiSpaceSize());
1340 ASSERT(IsAddressAligned(start, size, 0)); 864 ASSERT(static_cast<intptr_t>(chunk_size_) >=
865 2 * Heap::ReservedSemiSpaceSize());
866 ASSERT(IsAddressAligned(chunk_base_, 2 * maximum_semispace_capacity, 0));
1341 867
1342 if (!to_space_.Setup(start, 868 if (!to_space_.Setup(chunk_base_,
1343 initial_semispace_capacity, 869 initial_semispace_capacity,
1344 maximum_semispace_capacity)) { 870 maximum_semispace_capacity)) {
1345 return false; 871 return false;
1346 } 872 }
1347 if (!from_space_.Setup(start + maximum_semispace_capacity, 873 if (!from_space_.Setup(chunk_base_ + maximum_semispace_capacity,
1348 initial_semispace_capacity, 874 initial_semispace_capacity,
1349 maximum_semispace_capacity)) { 875 maximum_semispace_capacity)) {
1350 return false; 876 return false;
1351 } 877 }
1352 878
1353 start_ = start; 879 start_ = chunk_base_;
1354 address_mask_ = ~(size - 1); 880 address_mask_ = ~(2 * maximum_semispace_capacity - 1);
1355 object_mask_ = address_mask_ | kHeapObjectTagMask; 881 object_mask_ = address_mask_ | kHeapObjectTagMask;
1356 object_expected_ = reinterpret_cast<uintptr_t>(start) | kHeapObjectTag; 882 object_expected_ = reinterpret_cast<uintptr_t>(start_) | kHeapObjectTag;
1357 883
1358 allocation_info_.top = to_space_.low(); 884 allocation_info_.top = to_space_.low();
1359 allocation_info_.limit = to_space_.high(); 885 allocation_info_.limit = to_space_.high();
1360 mc_forwarding_info_.top = NULL;
1361 mc_forwarding_info_.limit = NULL;
1362 886
1363 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); 887 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1364 return true; 888 return true;
1365 } 889 }
1366 890
1367 891
1368 void NewSpace::TearDown() { 892 void NewSpace::TearDown() {
1369 #if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING) 893 #if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING)
1370 if (allocated_histogram_) { 894 if (allocated_histogram_) {
1371 DeleteArray(allocated_histogram_); 895 DeleteArray(allocated_histogram_);
1372 allocated_histogram_ = NULL; 896 allocated_histogram_ = NULL;
1373 } 897 }
1374 if (promoted_histogram_) { 898 if (promoted_histogram_) {
1375 DeleteArray(promoted_histogram_); 899 DeleteArray(promoted_histogram_);
1376 promoted_histogram_ = NULL; 900 promoted_histogram_ = NULL;
1377 } 901 }
1378 #endif 902 #endif
1379 903
1380 start_ = NULL; 904 start_ = NULL;
1381 allocation_info_.top = NULL; 905 allocation_info_.top = NULL;
1382 allocation_info_.limit = NULL; 906 allocation_info_.limit = NULL;
1383 mc_forwarding_info_.top = NULL;
1384 mc_forwarding_info_.limit = NULL;
1385 907
1386 to_space_.TearDown(); 908 to_space_.TearDown();
1387 from_space_.TearDown(); 909 from_space_.TearDown();
910
911 LOG(DeleteEvent("InitialChunk", chunk_base_));
912 MemoryAllocator::FreeMemory(chunk_base_,
913 static_cast<size_t>(chunk_size_),
914 NOT_EXECUTABLE);
915 chunk_base_ = NULL;
916 chunk_size_ = 0;
1388 } 917 }
1389 918
1390 919
1391 #ifdef ENABLE_HEAP_PROTECTION 920 #ifdef ENABLE_HEAP_PROTECTION
1392 921
1393 void NewSpace::Protect() { 922 void NewSpace::Protect() {
1394 MemoryAllocator::Protect(ToSpaceLow(), Capacity()); 923 MemoryAllocator::Protect(ToSpaceLow(), Capacity());
1395 MemoryAllocator::Protect(FromSpaceLow(), Capacity()); 924 MemoryAllocator::Protect(FromSpaceLow(), Capacity());
1396 } 925 }
1397 926
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
1454 } 983 }
1455 984
1456 985
1457 void NewSpace::ResetAllocationInfo() { 986 void NewSpace::ResetAllocationInfo() {
1458 allocation_info_.top = to_space_.low(); 987 allocation_info_.top = to_space_.low();
1459 allocation_info_.limit = to_space_.high(); 988 allocation_info_.limit = to_space_.high();
1460 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); 989 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1461 } 990 }
1462 991
1463 992
1464 void NewSpace::MCResetRelocationInfo() {
1465 mc_forwarding_info_.top = from_space_.low();
1466 mc_forwarding_info_.limit = from_space_.high();
1467 ASSERT_SEMISPACE_ALLOCATION_INFO(mc_forwarding_info_, from_space_);
1468 }
1469
1470
1471 void NewSpace::MCCommitRelocationInfo() {
1472 // Assumes that the spaces have been flipped so that mc_forwarding_info_ is
1473 // valid allocation info for the to space.
1474 allocation_info_.top = mc_forwarding_info_.top;
1475 allocation_info_.limit = to_space_.high();
1476 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1477 }
1478
1479
1480 #ifdef DEBUG 993 #ifdef DEBUG
1481 // We do not use the SemispaceIterator because verification doesn't assume 994 // We do not use the SemispaceIterator because verification doesn't assume
1482 // that it works (it depends on the invariants we are checking). 995 // that it works (it depends on the invariants we are checking).
1483 void NewSpace::Verify() { 996 void NewSpace::Verify() {
1484 // The allocation pointer should be in the space or at the very end. 997 // The allocation pointer should be in the space or at the very end.
1485 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); 998 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_);
1486 999
1487 // There should be objects packed in from the low address up to the 1000 // There should be objects packed in from the low address up to the
1488 // allocation pointer. 1001 // allocation pointer.
1489 Address current = to_space_.low(); 1002 Address current = to_space_.low();
(...skipping 633 matching lines...) Expand 10 before | Expand all | Expand 10 after
2123 cur_addr = cur_node->next(); 1636 cur_addr = cur_node->next();
2124 cur_node->SetMark(); 1637 cur_node->SetMark();
2125 } 1638 }
2126 } 1639 }
2127 1640
2128 1641
2129 // ----------------------------------------------------------------------------- 1642 // -----------------------------------------------------------------------------
2130 // OldSpace implementation 1643 // OldSpace implementation
2131 1644
2132 void OldSpace::PrepareForMarkCompact(bool will_compact) { 1645 void OldSpace::PrepareForMarkCompact(bool will_compact) {
1646 ASSERT(!will_compact);
2133 // Call prepare of the super class. 1647 // Call prepare of the super class.
2134 PagedSpace::PrepareForMarkCompact(will_compact); 1648 PagedSpace::PrepareForMarkCompact(will_compact);
2135 1649
2136 if (will_compact) { 1650 // During a non-compacting collection, everything below the linear
2137 // Reset relocation info. During a compacting collection, everything in 1651 // allocation pointer is considered allocated (everything above is
2138 // the space is considered 'available' and we will rediscover live data 1652 // available) and we will rediscover available and wasted bytes during
2139 // and waste during the collection. 1653 // the collection.
2140 MCResetRelocationInfo(); 1654 accounting_stats_.AllocateBytes(free_list_.available());
2141 ASSERT(Available() == Capacity()); 1655 accounting_stats_.FillWastedBytes(Waste());
2142 } else {
2143 // During a non-compacting collection, everything below the linear
2144 // allocation pointer is considered allocated (everything above is
2145 // available) and we will rediscover available and wasted bytes during
2146 // the collection.
2147 accounting_stats_.AllocateBytes(free_list_.available());
2148 accounting_stats_.FillWastedBytes(Waste());
2149 }
2150 1656
2151 // Clear the free list before a full GC---it will be rebuilt afterward. 1657 // Clear the free list before a full GC---it will be rebuilt afterward.
2152 free_list_.Reset(); 1658 free_list_.Reset();
2153 } 1659 }
2154 1660
2155 1661
2156 void OldSpace::MCCommitRelocationInfo() {
2157 // Update fast allocation info.
2158 allocation_info_.top = mc_forwarding_info_.top;
2159 allocation_info_.limit = mc_forwarding_info_.limit;
2160 ASSERT(allocation_info_.VerifyPagedAllocation());
2161
2162 // The space is compacted and we haven't yet built free lists or
2163 // wasted any space.
2164 ASSERT(Waste() == 0);
2165 ASSERT(AvailableFree() == 0);
2166
2167 // Build the free list for the space.
2168 int computed_size = 0;
2169 PageIterator it(this, PageIterator::PAGES_USED_BY_MC);
2170 while (it.has_next()) {
2171 Page* p = it.next();
2172 // Space below the relocation pointer is allocated.
2173 computed_size +=
2174 static_cast<int>(p->AllocationWatermark() - p->ObjectAreaStart());
2175 if (it.has_next()) {
2176 // Free the space at the top of the page.
2177 int extra_size =
2178 static_cast<int>(p->ObjectAreaEnd() - p->AllocationWatermark());
2179 if (extra_size > 0) {
2180 int wasted_bytes = free_list_.Free(p->AllocationWatermark(),
2181 extra_size);
2182 // The bytes we have just "freed" to add to the free list were
2183 // already accounted as available.
2184 accounting_stats_.WasteBytes(wasted_bytes);
2185 }
2186 }
2187 }
2188
2189 // Make sure the computed size - based on the used portion of the pages in
2190 // use - matches the size obtained while computing forwarding addresses.
2191 ASSERT(computed_size == Size());
2192 }
2193
2194
2195 bool NewSpace::ReserveSpace(int bytes) { 1662 bool NewSpace::ReserveSpace(int bytes) {
2196 // We can't reliably unpack a partial snapshot that needs more new space 1663 // We can't reliably unpack a partial snapshot that needs more new space
2197 // space than the minimum NewSpace size. 1664 // space than the minimum NewSpace size.
2198 ASSERT(bytes <= InitialCapacity()); 1665 ASSERT(bytes <= InitialCapacity());
2199 Address limit = allocation_info_.limit; 1666 Address limit = allocation_info_.limit;
2200 Address top = allocation_info_.top; 1667 Address top = allocation_info_.top;
2201 return limit - top >= bytes; 1668 return limit - top >= bytes;
2202 } 1669 }
2203 1670
2204 1671
2205 void PagedSpace::FreePages(Page* prev, Page* last) { 1672 void PagedSpace::FreePages(Page* prev, Page* last) {
2206 if (last == AllocationTopPage()) { 1673 if (last == AllocationTopPage()) {
2207 // Pages are already at the end of used pages. 1674 // Pages are already at the end of used pages.
2208 return; 1675 return;
2209 } 1676 }
2210 1677
2211 Page* first = NULL; 1678 Page* first = NULL;
2212 1679
2213 // Remove pages from the list. 1680 // Remove pages from the list.
2214 if (prev == NULL) { 1681 if (prev == NULL) {
2215 first = first_page_; 1682 first = first_page_;
2216 first_page_ = last->next_page(); 1683 first_page_ = last->next_page();
2217 } else { 1684 } else {
2218 first = prev->next_page(); 1685 first = prev->next_page();
2219 MemoryAllocator::SetNextPage(prev, last->next_page()); 1686 prev->set_next_page(last->next_page());
2220 } 1687 }
2221 1688
2222 // Attach it after the last page. 1689 // Attach it after the last page.
2223 MemoryAllocator::SetNextPage(last_page_, first); 1690 last_page_->set_next_page(first);
2224 last_page_ = last; 1691 last_page_ = last;
2225 MemoryAllocator::SetNextPage(last, NULL); 1692 last->set_next_page(NULL);
2226 1693
2227 // Clean them up. 1694 // Clean them up.
2228 do { 1695 do {
2229 first->InvalidateWatermark(true); 1696 first->InvalidateWatermark(true);
2230 first->SetAllocationWatermark(first->ObjectAreaStart()); 1697 first->SetAllocationWatermark(first->ObjectAreaStart());
2231 first->SetCachedAllocationWatermark(first->ObjectAreaStart()); 1698 first->SetCachedAllocationWatermark(first->ObjectAreaStart());
2232 first->SetRegionMarks(Page::kAllRegionsCleanMarks); 1699 first->SetRegionMarks(Page::kAllRegionsCleanMarks);
2233 first = first->next_page(); 1700 first = first->next_page();
2234 } while (first != NULL); 1701 } while (first != NULL);
2235
2236 // Order of pages in this space might no longer be consistent with
2237 // order of pages in chunks.
2238 page_list_is_chunk_ordered_ = false;
2239 }
2240
2241
2242 void PagedSpace::RelinkPageListInChunkOrder(bool deallocate_blocks) {
2243 const bool add_to_freelist = true;
2244
2245 // Mark used and unused pages to properly fill unused pages
2246 // after reordering.
2247 PageIterator all_pages_iterator(this, PageIterator::ALL_PAGES);
2248 Page* last_in_use = AllocationTopPage();
2249 bool in_use = true;
2250
2251 while (all_pages_iterator.has_next()) {
2252 Page* p = all_pages_iterator.next();
2253 p->SetWasInUseBeforeMC(in_use);
2254 if (p == last_in_use) {
2255 // We passed a page containing allocation top. All consequent
2256 // pages are not used.
2257 in_use = false;
2258 }
2259 }
2260
2261 if (page_list_is_chunk_ordered_) return;
2262
2263 Page* new_last_in_use = Page::FromAddress(NULL);
2264 MemoryAllocator::RelinkPageListInChunkOrder(this,
2265 &first_page_,
2266 &last_page_,
2267 &new_last_in_use);
2268 ASSERT(new_last_in_use->is_valid());
2269
2270 if (new_last_in_use != last_in_use) {
2271 // Current allocation top points to a page which is now in the middle
2272 // of page list. We should move allocation top forward to the new last
2273 // used page so various object iterators will continue to work properly.
2274 int size_in_bytes = static_cast<int>(PageAllocationLimit(last_in_use) -
2275 last_in_use->AllocationTop());
2276
2277 last_in_use->SetAllocationWatermark(last_in_use->AllocationTop());
2278 if (size_in_bytes > 0) {
2279 Address start = last_in_use->AllocationTop();
2280 if (deallocate_blocks) {
2281 accounting_stats_.AllocateBytes(size_in_bytes);
2282 DeallocateBlock(start, size_in_bytes, add_to_freelist);
2283 } else {
2284 Heap::CreateFillerObjectAt(start, size_in_bytes);
2285 }
2286 }
2287
2288 // New last in use page was in the middle of the list before
2289 // sorting so it full.
2290 SetTop(new_last_in_use->AllocationTop());
2291
2292 ASSERT(AllocationTopPage() == new_last_in_use);
2293 ASSERT(AllocationTopPage()->WasInUseBeforeMC());
2294 }
2295
2296 PageIterator pages_in_use_iterator(this, PageIterator::PAGES_IN_USE);
2297 while (pages_in_use_iterator.has_next()) {
2298 Page* p = pages_in_use_iterator.next();
2299 if (!p->WasInUseBeforeMC()) {
2300 // Empty page is in the middle of a sequence of used pages.
2301 // Allocate it as a whole and deallocate immediately.
2302 int size_in_bytes = static_cast<int>(PageAllocationLimit(p) -
2303 p->ObjectAreaStart());
2304
2305 p->SetAllocationWatermark(p->ObjectAreaStart());
2306 Address start = p->ObjectAreaStart();
2307 if (deallocate_blocks) {
2308 accounting_stats_.AllocateBytes(size_in_bytes);
2309 DeallocateBlock(start, size_in_bytes, add_to_freelist);
2310 } else {
2311 Heap::CreateFillerObjectAt(start, size_in_bytes);
2312 }
2313 }
2314 }
2315
2316 page_list_is_chunk_ordered_ = true;
2317 } 1702 }
2318 1703
2319 1704
2320 void PagedSpace::PrepareForMarkCompact(bool will_compact) { 1705 void PagedSpace::PrepareForMarkCompact(bool will_compact) {
2321 if (will_compact) { 1706 ASSERT(!will_compact);
2322 RelinkPageListInChunkOrder(false);
2323 }
2324 } 1707 }
2325 1708
2326 1709
2327 bool PagedSpace::ReserveSpace(int bytes) { 1710 bool PagedSpace::ReserveSpace(int bytes) {
2328 Address limit = allocation_info_.limit; 1711 Address limit = allocation_info_.limit;
2329 Address top = allocation_info_.top; 1712 Address top = allocation_info_.top;
2330 if (limit - top >= bytes) return true; 1713 if (limit - top >= bytes) return true;
2331 1714
2332 // There wasn't enough space in the current page. Lets put the rest 1715 // There wasn't enough space in the current page. Lets put the rest
2333 // of the page on the free list and start a fresh page. 1716 // of the page on the free list and start a fresh page.
2334 PutRestOfCurrentPageOnFreeList(TopPageOf(allocation_info_)); 1717 PutRestOfCurrentPageOnFreeList(TopPageOf(allocation_info_));
2335 1718
2336 Page* reserved_page = TopPageOf(allocation_info_); 1719 Page* reserved_page = TopPageOf(allocation_info_);
2337 int bytes_left_to_reserve = bytes; 1720 int bytes_left_to_reserve = bytes;
2338 while (bytes_left_to_reserve > 0) { 1721 while (bytes_left_to_reserve > 0) {
2339 if (!reserved_page->next_page()->is_valid()) { 1722 if (!reserved_page->next_page()->is_valid()) {
2340 if (Heap::OldGenerationAllocationLimitReached()) return false; 1723 if (Heap::OldGenerationAllocationLimitReached()) return false;
2341 Expand(reserved_page); 1724 Expand();
2342 } 1725 }
2343 bytes_left_to_reserve -= Page::kPageSize; 1726 bytes_left_to_reserve -= Page::kPageSize;
2344 reserved_page = reserved_page->next_page(); 1727 reserved_page = reserved_page->next_page();
2345 if (!reserved_page->is_valid()) return false; 1728 if (!reserved_page->is_valid()) return false;
2346 } 1729 }
2347 ASSERT(TopPageOf(allocation_info_)->next_page()->is_valid()); 1730 ASSERT(TopPageOf(allocation_info_)->next_page()->is_valid());
2348 TopPageOf(allocation_info_)->next_page()->InvalidateWatermark(true); 1731 TopPageOf(allocation_info_)->next_page()->InvalidateWatermark(true);
2349 SetAllocationInfo(&allocation_info_, 1732 SetAllocationInfo(&allocation_info_,
2350 TopPageOf(allocation_info_)->next_page()); 1733 TopPageOf(allocation_info_)->next_page());
2351 return true; 1734 return true;
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
2400 1783
2401 // Free list allocation failed and there is no next page. Fail if we have 1784 // Free list allocation failed and there is no next page. Fail if we have
2402 // hit the old generation size limit that should cause a garbage 1785 // hit the old generation size limit that should cause a garbage
2403 // collection. 1786 // collection.
2404 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) { 1787 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) {
2405 return NULL; 1788 return NULL;
2406 } 1789 }
2407 1790
2408 // Try to expand the space and allocate in the new next page. 1791 // Try to expand the space and allocate in the new next page.
2409 ASSERT(!current_page->next_page()->is_valid()); 1792 ASSERT(!current_page->next_page()->is_valid());
2410 if (Expand(current_page)) { 1793 if (Expand()) {
2411 return AllocateInNextPage(current_page, size_in_bytes); 1794 return AllocateInNextPage(current_page, size_in_bytes);
2412 } 1795 }
2413 1796
2414 // Finally, fail. 1797 // Finally, fail.
2415 return NULL; 1798 return NULL;
2416 } 1799 }
2417 1800
2418 1801
2419 void OldSpace::PutRestOfCurrentPageOnFreeList(Page* current_page) { 1802 void OldSpace::PutRestOfCurrentPageOnFreeList(Page* current_page) {
2420 current_page->SetAllocationWatermark(allocation_info_.top); 1803 current_page->SetAllocationWatermark(allocation_info_.top);
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after
2611 } 1994 }
2612 #endif 1995 #endif
2613 1996
2614 // ----------------------------------------------------------------------------- 1997 // -----------------------------------------------------------------------------
2615 // FixedSpace implementation 1998 // FixedSpace implementation
2616 1999
2617 void FixedSpace::PrepareForMarkCompact(bool will_compact) { 2000 void FixedSpace::PrepareForMarkCompact(bool will_compact) {
2618 // Call prepare of the super class. 2001 // Call prepare of the super class.
2619 PagedSpace::PrepareForMarkCompact(will_compact); 2002 PagedSpace::PrepareForMarkCompact(will_compact);
2620 2003
2621 if (will_compact) { 2004 ASSERT(!will_compact);
2622 // Reset relocation info.
2623 MCResetRelocationInfo();
2624 2005
2625 // During a compacting collection, everything in the space is considered 2006 // During a non-compacting collection, everything below the linear
2626 // 'available' (set by the call to MCResetRelocationInfo) and we will 2007 // allocation pointer except wasted top-of-page blocks is considered
2627 // rediscover live and wasted bytes during the collection. 2008 // allocated and we will rediscover available bytes during the
2628 ASSERT(Available() == Capacity()); 2009 // collection.
2629 } else { 2010 accounting_stats_.AllocateBytes(free_list_.available());
2630 // During a non-compacting collection, everything below the linear
2631 // allocation pointer except wasted top-of-page blocks is considered
2632 // allocated and we will rediscover available bytes during the
2633 // collection.
2634 accounting_stats_.AllocateBytes(free_list_.available());
2635 }
2636 2011
2637 // Clear the free list before a full GC---it will be rebuilt afterward. 2012 // Clear the free list before a full GC---it will be rebuilt afterward.
2638 free_list_.Reset(); 2013 free_list_.Reset();
2639 } 2014 }
2640 2015
2641 2016
2642 void FixedSpace::MCCommitRelocationInfo() {
2643 // Update fast allocation info.
2644 allocation_info_.top = mc_forwarding_info_.top;
2645 allocation_info_.limit = mc_forwarding_info_.limit;
2646 ASSERT(allocation_info_.VerifyPagedAllocation());
2647
2648 // The space is compacted and we haven't yet wasted any space.
2649 ASSERT(Waste() == 0);
2650
2651 // Update allocation_top of each page in use and compute waste.
2652 int computed_size = 0;
2653 PageIterator it(this, PageIterator::PAGES_USED_BY_MC);
2654 while (it.has_next()) {
2655 Page* page = it.next();
2656 Address page_top = page->AllocationTop();
2657 computed_size += static_cast<int>(page_top - page->ObjectAreaStart());
2658 if (it.has_next()) {
2659 accounting_stats_.WasteBytes(
2660 static_cast<int>(page->ObjectAreaEnd() - page_top));
2661 page->SetAllocationWatermark(page_top);
2662 }
2663 }
2664
2665 // Make sure the computed size - based on the used portion of the
2666 // pages in use - matches the size we adjust during allocation.
2667 ASSERT(computed_size == Size());
2668 }
2669
2670
2671 // Slow case for normal allocation. Try in order: (1) allocate in the next 2017 // Slow case for normal allocation. Try in order: (1) allocate in the next
2672 // page in the space, (2) allocate off the space's free list, (3) expand the 2018 // page in the space, (2) allocate off the space's free list, (3) expand the
2673 // space, (4) fail. 2019 // space, (4) fail.
2674 HeapObject* FixedSpace::SlowAllocateRaw(int size_in_bytes) { 2020 HeapObject* FixedSpace::SlowAllocateRaw(int size_in_bytes) {
2675 ASSERT_EQ(object_size_in_bytes_, size_in_bytes); 2021 ASSERT_EQ(object_size_in_bytes_, size_in_bytes);
2676 // Linear allocation in this space has failed. If there is another page 2022 // Linear allocation in this space has failed. If there is another page
2677 // in the space, move to that page and allocate there. This allocation 2023 // in the space, move to that page and allocate there. This allocation
2678 // should succeed. 2024 // should succeed.
2679 Page* current_page = TopPageOf(allocation_info_); 2025 Page* current_page = TopPageOf(allocation_info_);
2680 if (current_page->next_page()->is_valid()) { 2026 if (current_page->next_page()->is_valid()) {
(...skipping 26 matching lines...) Expand all
2707 2053
2708 // Free list allocation failed and there is no next page. Fail if we have 2054 // Free list allocation failed and there is no next page. Fail if we have
2709 // hit the old generation size limit that should cause a garbage 2055 // hit the old generation size limit that should cause a garbage
2710 // collection. 2056 // collection.
2711 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) { 2057 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) {
2712 return NULL; 2058 return NULL;
2713 } 2059 }
2714 2060
2715 // Try to expand the space and allocate in the new next page. 2061 // Try to expand the space and allocate in the new next page.
2716 ASSERT(!current_page->next_page()->is_valid()); 2062 ASSERT(!current_page->next_page()->is_valid());
2717 if (Expand(current_page)) { 2063 if (Expand()) {
2718 return AllocateInNextPage(current_page, size_in_bytes); 2064 return AllocateInNextPage(current_page, size_in_bytes);
2719 } 2065 }
2720 2066
2721 // Finally, fail. 2067 // Finally, fail.
2722 return NULL; 2068 return NULL;
2723 } 2069 }
2724 2070
2725 2071
2726 // Move to the next page (there is assumed to be one) and allocate there. 2072 // Move to the next page (there is assumed to be one) and allocate there.
2727 // The top of page block is always wasted, because it is too small to hold a 2073 // The top of page block is always wasted, because it is too small to hold a
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
2798 ASSERT(object->IsJSGlobalPropertyCell() || 2144 ASSERT(object->IsJSGlobalPropertyCell() ||
2799 object->map() == Heap::two_pointer_filler_map()); 2145 object->map() == Heap::two_pointer_filler_map());
2800 } 2146 }
2801 #endif 2147 #endif
2802 2148
2803 2149
2804 // ----------------------------------------------------------------------------- 2150 // -----------------------------------------------------------------------------
2805 // LargeObjectIterator 2151 // LargeObjectIterator
2806 2152
2807 LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space) { 2153 LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space) {
2808 current_ = space->first_chunk_; 2154 current_ = space->first_page_;
2809 size_func_ = NULL; 2155 size_func_ = NULL;
2810 } 2156 }
2811 2157
2812 2158
2813 LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space, 2159 LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space,
2814 HeapObjectCallback size_func) { 2160 HeapObjectCallback size_func) {
2815 current_ = space->first_chunk_; 2161 current_ = space->first_page_;
2816 size_func_ = size_func; 2162 size_func_ = size_func;
2817 } 2163 }
2818 2164
2819 2165
2820 HeapObject* LargeObjectIterator::next() { 2166 HeapObject* LargeObjectIterator::next() {
2821 if (current_ == NULL) return NULL; 2167 if (current_ == NULL) return NULL;
2822 2168
2823 HeapObject* object = current_->GetObject(); 2169 HeapObject* object = current_->GetObject();
2824 current_ = current_->next(); 2170 current_ = current_->next_page();
2825 return object; 2171 return object;
2826 } 2172 }
2827 2173
2828 2174
2829 // ----------------------------------------------------------------------------- 2175 // -----------------------------------------------------------------------------
2830 // LargeObjectChunk
2831
2832 LargeObjectChunk* LargeObjectChunk::New(int size_in_bytes,
2833 Executability executable) {
2834 size_t requested = ChunkSizeFor(size_in_bytes);
2835 size_t size;
2836 void* mem = MemoryAllocator::AllocateRawMemory(requested, &size, executable);
2837 if (mem == NULL) return NULL;
2838
2839 // The start of the chunk may be overlayed with a page so we have to
2840 // make sure that the page flags fit in the size field.
2841 ASSERT((size & Page::kPageFlagMask) == 0);
2842
2843 LOG(NewEvent("LargeObjectChunk", mem, size));
2844 if (size < requested) {
2845 MemoryAllocator::FreeRawMemory(mem, size, executable);
2846 LOG(DeleteEvent("LargeObjectChunk", mem));
2847 return NULL;
2848 }
2849
2850 ObjectSpace space = (executable == EXECUTABLE)
2851 ? kObjectSpaceCodeSpace
2852 : kObjectSpaceLoSpace;
2853 MemoryAllocator::PerformAllocationCallback(
2854 space, kAllocationActionAllocate, size);
2855
2856 LargeObjectChunk* chunk = reinterpret_cast<LargeObjectChunk*>(mem);
2857 chunk->size_ = size;
2858 return chunk;
2859 }
2860
2861
2862 int LargeObjectChunk::ChunkSizeFor(int size_in_bytes) {
2863 int os_alignment = static_cast<int>(OS::AllocateAlignment());
2864 if (os_alignment < Page::kPageSize) {
2865 size_in_bytes += (Page::kPageSize - os_alignment);
2866 }
2867 return size_in_bytes + Page::kObjectStartOffset;
2868 }
2869
2870 // -----------------------------------------------------------------------------
2871 // LargeObjectSpace 2176 // LargeObjectSpace
2872 2177
2873 LargeObjectSpace::LargeObjectSpace(AllocationSpace id) 2178 LargeObjectSpace::LargeObjectSpace(AllocationSpace id)
2874 : Space(id, NOT_EXECUTABLE), // Managed on a per-allocation basis 2179 : Space(id, NOT_EXECUTABLE), // Managed on a per-allocation basis
2875 first_chunk_(NULL), 2180 first_page_(NULL),
2876 size_(0), 2181 size_(0),
2877 page_count_(0), 2182 page_count_(0),
2878 objects_size_(0) {} 2183 objects_size_(0) {}
2879 2184
2880 2185
2881 bool LargeObjectSpace::Setup() { 2186 bool LargeObjectSpace::Setup() {
2882 first_chunk_ = NULL; 2187 first_page_ = NULL;
2883 size_ = 0; 2188 size_ = 0;
2884 page_count_ = 0; 2189 page_count_ = 0;
2885 objects_size_ = 0; 2190 objects_size_ = 0;
2886 return true; 2191 return true;
2887 } 2192 }
2888 2193
2889 2194
2890 void LargeObjectSpace::TearDown() { 2195 void LargeObjectSpace::TearDown() {
2891 while (first_chunk_ != NULL) { 2196 while (first_page_ != NULL) {
2892 LargeObjectChunk* chunk = first_chunk_; 2197 LargePage* page = first_page_;
2893 first_chunk_ = first_chunk_->next(); 2198 first_page_ = first_page_->next_page();
2894 LOG(DeleteEvent("LargeObjectChunk", chunk->address())); 2199
2895 Page* page = Page::FromAddress(RoundUp(chunk->address(), Page::kPageSize)); 2200 MemoryAllocator::Free(page);
2896 Executability executable =
2897 page->IsPageExecutable() ? EXECUTABLE : NOT_EXECUTABLE;
2898 ObjectSpace space = kObjectSpaceLoSpace;
2899 if (executable == EXECUTABLE) space = kObjectSpaceCodeSpace;
2900 size_t size = chunk->size();
2901 MemoryAllocator::FreeRawMemory(chunk->address(), size, executable);
2902 MemoryAllocator::PerformAllocationCallback(
2903 space, kAllocationActionFree, size);
2904 } 2201 }
2905 2202
2906 size_ = 0; 2203 size_ = 0;
2907 page_count_ = 0; 2204 page_count_ = 0;
2908 objects_size_ = 0; 2205 objects_size_ = 0;
2909 } 2206 }
2910 2207
2911 2208
2912 #ifdef ENABLE_HEAP_PROTECTION 2209 #ifdef ENABLE_HEAP_PROTECTION
2913 2210
(...skipping 11 matching lines...) Expand all
2925 while (chunk != NULL) { 2222 while (chunk != NULL) {
2926 bool is_code = chunk->GetObject()->IsCode(); 2223 bool is_code = chunk->GetObject()->IsCode();
2927 MemoryAllocator::Unprotect(chunk->address(), chunk->size(), 2224 MemoryAllocator::Unprotect(chunk->address(), chunk->size(),
2928 is_code ? EXECUTABLE : NOT_EXECUTABLE); 2225 is_code ? EXECUTABLE : NOT_EXECUTABLE);
2929 chunk = chunk->next(); 2226 chunk = chunk->next();
2930 } 2227 }
2931 } 2228 }
2932 2229
2933 #endif 2230 #endif
2934 2231
2935 2232 MaybeObject* LargeObjectSpace::AllocateRawInternal(int object_size,
2936 MaybeObject* LargeObjectSpace::AllocateRawInternal(int requested_size,
2937 int object_size,
2938 Executability executable) { 2233 Executability executable) {
2939 ASSERT(0 < object_size && object_size <= requested_size);
2940
2941 // Check if we want to force a GC before growing the old space further. 2234 // Check if we want to force a GC before growing the old space further.
2942 // If so, fail the allocation. 2235 // If so, fail the allocation.
2943 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) { 2236 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) {
2944 return Failure::RetryAfterGC(identity()); 2237 return Failure::RetryAfterGC(identity());
2945 } 2238 }
2946 2239
2947 LargeObjectChunk* chunk = LargeObjectChunk::New(requested_size, executable); 2240 LargePage* page = MemoryAllocator::AllocateLargePage(object_size,
2948 if (chunk == NULL) { 2241 executable,
2949 return Failure::RetryAfterGC(identity()); 2242 this);
2950 } 2243 if (page == NULL) return Failure::RetryAfterGC(identity());
2244 ASSERT(page->body_size() >= object_size);
2951 2245
2952 size_ += static_cast<int>(chunk->size()); 2246 size_ += static_cast<int>(page->size());
2953 objects_size_ += requested_size; 2247 objects_size_ += object_size;
2954 page_count_++; 2248 page_count_++;
2955 chunk->set_next(first_chunk_); 2249 page->set_next_page(first_page_);
2956 first_chunk_ = chunk; 2250 first_page_ = page;
2957 2251
2958 // Initialize page header. 2252 return page->GetObject();
2959 Page* page = Page::FromAddress(RoundUp(chunk->address(), Page::kPageSize));
2960 Address object_address = page->ObjectAreaStart();
2961
2962 // Clear the low order bit of the second word in the page to flag it as a
2963 // large object page. If the chunk_size happened to be written there, its
2964 // low order bit should already be clear.
2965 page->SetIsLargeObjectPage(true);
2966 page->SetIsPageExecutable(executable);
2967 page->SetRegionMarks(Page::kAllRegionsCleanMarks);
2968 return HeapObject::FromAddress(object_address);
2969 } 2253 }
2970 2254
2971 2255
2972 MaybeObject* LargeObjectSpace::AllocateRawCode(int size_in_bytes) { 2256 MaybeObject* LargeObjectSpace::AllocateRawCode(int size_in_bytes) {
2973 ASSERT(0 < size_in_bytes); 2257 ASSERT(0 < size_in_bytes);
2974 return AllocateRawInternal(size_in_bytes, 2258 return AllocateRawInternal(size_in_bytes, EXECUTABLE);
2975 size_in_bytes,
2976 EXECUTABLE);
2977 } 2259 }
2978 2260
2979 2261
2980 MaybeObject* LargeObjectSpace::AllocateRawFixedArray(int size_in_bytes) { 2262 MaybeObject* LargeObjectSpace::AllocateRawFixedArray(int size_in_bytes) {
2981 ASSERT(0 < size_in_bytes); 2263 ASSERT(0 < size_in_bytes);
2982 return AllocateRawInternal(size_in_bytes, 2264 return AllocateRawInternal(size_in_bytes, NOT_EXECUTABLE);
2983 size_in_bytes,
2984 NOT_EXECUTABLE);
2985 } 2265 }
2986 2266
2987 2267
2988 MaybeObject* LargeObjectSpace::AllocateRaw(int size_in_bytes) { 2268 MaybeObject* LargeObjectSpace::AllocateRaw(int size_in_bytes) {
2989 ASSERT(0 < size_in_bytes); 2269 ASSERT(0 < size_in_bytes);
2990 return AllocateRawInternal(size_in_bytes, 2270 return AllocateRawInternal(size_in_bytes, NOT_EXECUTABLE);
2991 size_in_bytes,
2992 NOT_EXECUTABLE);
2993 } 2271 }
2994 2272
2995 2273
2996 // GC support 2274 // GC support
2997 MaybeObject* LargeObjectSpace::FindObject(Address a) { 2275 MaybeObject* LargeObjectSpace::FindObject(Address a) {
2998 for (LargeObjectChunk* chunk = first_chunk_; 2276 for (LargePage* page = first_page_;
2999 chunk != NULL; 2277 page != NULL;
3000 chunk = chunk->next()) { 2278 page = page->next_page()) {
3001 Address chunk_address = chunk->address(); 2279 Address page_address = page->address();
3002 if (chunk_address <= a && a < chunk_address + chunk->size()) { 2280 if (page_address <= a && a < page_address + page->size()) {
3003 return chunk->GetObject(); 2281 return page->GetObject();
3004 } 2282 }
3005 } 2283 }
3006 return Failure::Exception(); 2284 return Failure::Exception();
3007 } 2285 }
3008 2286
3009 2287
3010 LargeObjectChunk* LargeObjectSpace::FindChunkContainingPc(Address pc) { 2288 LargePage* LargeObjectSpace::FindPageContainingPc(Address pc) {
3011 // TODO(853): Change this implementation to only find executable 2289 // TODO(853): Change this implementation to only find executable
3012 // chunks and use some kind of hash-based approach to speed it up. 2290 // chunks and use some kind of hash-based approach to speed it up.
3013 for (LargeObjectChunk* chunk = first_chunk_; 2291 for (LargePage* chunk = first_page_;
3014 chunk != NULL; 2292 chunk != NULL;
3015 chunk = chunk->next()) { 2293 chunk = chunk->next_page()) {
3016 Address chunk_address = chunk->address(); 2294 Address chunk_address = chunk->address();
3017 if (chunk_address <= pc && pc < chunk_address + chunk->size()) { 2295 if (chunk_address <= pc && pc < chunk_address + chunk->size()) {
3018 return chunk; 2296 return chunk;
3019 } 2297 }
3020 } 2298 }
3021 return NULL; 2299 return NULL;
3022 } 2300 }
3023 2301
3024 2302
3025 void LargeObjectSpace::IterateDirtyRegions(ObjectSlotCallback copy_object) { 2303 void LargeObjectSpace::IterateDirtyRegions(ObjectSlotCallback copy_object) {
3026 LargeObjectIterator it(this); 2304 LargeObjectIterator it(this);
3027 for (HeapObject* object = it.next(); object != NULL; object = it.next()) { 2305 for (HeapObject* object = it.next(); object != NULL; object = it.next()) {
3028 // We only have code, sequential strings, or fixed arrays in large 2306 // We only have code, sequential strings, or fixed arrays in large
3029 // object space, and only fixed arrays can possibly contain pointers to 2307 // object space, and only fixed arrays can possibly contain pointers to
3030 // the young generation. 2308 // the young generation.
3031 if (object->IsFixedArray()) { 2309 if (object->IsFixedArray()) {
3032 Page* page = Page::FromAddress(object->address()); 2310 Page* page = Page::FromAddress(object->address());
3033 uint32_t marks = page->GetRegionMarks(); 2311 uint32_t marks = page->GetRegionMarks();
3034 uint32_t newmarks = Page::kAllRegionsCleanMarks;
3035 2312
3036 if (marks != Page::kAllRegionsCleanMarks) { 2313 // TODO(gc): we can no longer assume that LargePage is bigger than normal
3037 // For a large page a single dirty mark corresponds to several 2314 // page.
3038 // regions (modulo 32). So we treat a large page as a sequence of 2315 ASSERT(marks == Page::kAllRegionsDirtyMarks);
3039 // normal pages of size Page::kPageSize having same dirty marks 2316 USE(marks);
3040 // and subsequently iterate dirty regions on each of these pages.
3041 Address start = object->address();
3042 Address end = page->ObjectAreaEnd();
3043 Address object_end = start + object->Size();
3044 2317
3045 // Iterate regions of the first normal page covering object. 2318 Address start = object->address();
3046 uint32_t first_region_number = page->GetRegionNumberForAddress(start); 2319 Address object_end = start + object->Size();
3047 newmarks |= 2320 Heap::IteratePointersInDirtyRegion(start, object_end, copy_object);
3048 Heap::IterateDirtyRegions(marks >> first_region_number,
3049 start,
3050 end,
3051 &Heap::IteratePointersInDirtyRegion,
3052 copy_object) << first_region_number;
3053
3054 start = end;
3055 end = start + Page::kPageSize;
3056 while (end <= object_end) {
3057 // Iterate next 32 regions.
3058 newmarks |=
3059 Heap::IterateDirtyRegions(marks,
3060 start,
3061 end,
3062 &Heap::IteratePointersInDirtyRegion,
3063 copy_object);
3064 start = end;
3065 end = start + Page::kPageSize;
3066 }
3067
3068 if (start != object_end) {
3069 // Iterate the last piece of an object which is less than
3070 // Page::kPageSize.
3071 newmarks |=
3072 Heap::IterateDirtyRegions(marks,
3073 start,
3074 object_end,
3075 &Heap::IteratePointersInDirtyRegion,
3076 copy_object);
3077 }
3078
3079 page->SetRegionMarks(newmarks);
3080 }
3081 } 2321 }
3082 } 2322 }
3083 } 2323 }
3084 2324
3085 2325
3086 void LargeObjectSpace::FreeUnmarkedObjects() { 2326 void LargeObjectSpace::FreeUnmarkedObjects() {
3087 LargeObjectChunk* previous = NULL; 2327 LargePage* previous = NULL;
3088 LargeObjectChunk* current = first_chunk_; 2328 LargePage* current = first_page_;
3089 while (current != NULL) { 2329 while (current != NULL) {
3090 HeapObject* object = current->GetObject(); 2330 HeapObject* object = current->GetObject();
3091 if (object->IsMarked()) { 2331 if (object->IsMarked()) {
3092 object->ClearMark(); 2332 object->ClearMark();
3093 MarkCompactCollector::tracer()->decrement_marked_count(); 2333 MarkCompactCollector::tracer()->decrement_marked_count();
3094 previous = current; 2334 previous = current;
3095 current = current->next(); 2335 current = current->next_page();
3096 } else { 2336 } else {
3097 Page* page = Page::FromAddress(RoundUp(current->address(), 2337 LargePage* page = current;
3098 Page::kPageSize));
3099 Executability executable =
3100 page->IsPageExecutable() ? EXECUTABLE : NOT_EXECUTABLE;
3101 Address chunk_address = current->address();
3102 size_t chunk_size = current->size();
3103
3104 // Cut the chunk out from the chunk list. 2338 // Cut the chunk out from the chunk list.
3105 current = current->next(); 2339 current = current->next_page();
3106 if (previous == NULL) { 2340 if (previous == NULL) {
3107 first_chunk_ = current; 2341 first_page_ = current;
3108 } else { 2342 } else {
3109 previous->set_next(current); 2343 previous->set_next_page(current);
3110 } 2344 }
3111 2345
3112 // Free the chunk. 2346 // Free the chunk.
3113 MarkCompactCollector::ReportDeleteIfNeeded(object); 2347 MarkCompactCollector::ReportDeleteIfNeeded(object);
3114 size_ -= static_cast<int>(chunk_size); 2348 size_ -= static_cast<int>(page->size());
3115 objects_size_ -= object->Size(); 2349 objects_size_ -= object->Size();
3116 page_count_--; 2350 page_count_--;
3117 ObjectSpace space = kObjectSpaceLoSpace; 2351
3118 if (executable == EXECUTABLE) space = kObjectSpaceCodeSpace; 2352 MemoryAllocator::Free(page);
3119 MemoryAllocator::FreeRawMemory(chunk_address, chunk_size, executable);
3120 MemoryAllocator::PerformAllocationCallback(space, kAllocationActionFree,
3121 size_);
3122 LOG(DeleteEvent("LargeObjectChunk", chunk_address));
3123 } 2353 }
3124 } 2354 }
3125 } 2355 }
3126 2356
3127 2357
3128 bool LargeObjectSpace::Contains(HeapObject* object) { 2358 bool LargeObjectSpace::Contains(HeapObject* object) {
3129 Address address = object->address(); 2359 Address address = object->address();
3130 if (Heap::new_space()->Contains(address)) { 2360 if (Heap::new_space()->Contains(address)) {
3131 return false; 2361 return false;
3132 } 2362 }
3133 Page* page = Page::FromAddress(address); 2363 MemoryChunk* chunk = MemoryChunk::FromAddress(address);
3134 2364
3135 SLOW_ASSERT(!page->IsLargeObjectPage() 2365 bool owned = chunk->owner() == this;
2366
2367 SLOW_ASSERT(!owned
3136 || !FindObject(address)->IsFailure()); 2368 || !FindObject(address)->IsFailure());
3137 2369
3138 return page->IsLargeObjectPage(); 2370 return owned;
3139 } 2371 }
3140 2372
3141 2373
3142 #ifdef DEBUG 2374 #ifdef DEBUG
3143 // We do not assume that the large object iterator works, because it depends 2375 // We do not assume that the large object iterator works, because it depends
3144 // on the invariants we are checking during verification. 2376 // on the invariants we are checking during verification.
3145 void LargeObjectSpace::Verify() { 2377 void LargeObjectSpace::Verify() {
3146 for (LargeObjectChunk* chunk = first_chunk_; 2378 for (LargePage* chunk = first_page_;
3147 chunk != NULL; 2379 chunk != NULL;
3148 chunk = chunk->next()) { 2380 chunk = chunk->next_page()) {
3149 // Each chunk contains an object that starts at the large object page's 2381 // Each chunk contains an object that starts at the large object page's
3150 // object area start. 2382 // object area start.
3151 HeapObject* object = chunk->GetObject(); 2383 HeapObject* object = chunk->GetObject();
3152 Page* page = Page::FromAddress(object->address()); 2384 Page* page = Page::FromAddress(object->address());
3153 ASSERT(object->address() == page->ObjectAreaStart()); 2385 ASSERT(object->address() == page->ObjectAreaStart());
3154 2386
3155 // The first word should be a map, and we expect all map pointers to be 2387 // The first word should be a map, and we expect all map pointers to be
3156 // in map space. 2388 // in map space.
3157 Map* map = object->map(); 2389 Map* map = object->map();
3158 ASSERT(map->IsMap()); 2390 ASSERT(map->IsMap());
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
3228 for (HeapObject* obj = obj_it.next(); obj != NULL; obj = obj_it.next()) { 2460 for (HeapObject* obj = obj_it.next(); obj != NULL; obj = obj_it.next()) {
3229 if (obj->IsCode()) { 2461 if (obj->IsCode()) {
3230 Code* code = Code::cast(obj); 2462 Code* code = Code::cast(obj);
3231 code_kind_statistics[code->kind()] += code->Size(); 2463 code_kind_statistics[code->kind()] += code->Size();
3232 } 2464 }
3233 } 2465 }
3234 } 2466 }
3235 #endif // DEBUG 2467 #endif // DEBUG
3236 2468
3237 } } // namespace v8::internal 2469 } } // namespace v8::internal
OLDNEW
« no previous file with comments | « src/spaces.h ('k') | src/spaces-inl.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698