Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 226 | 223 |
| 227 | 224 |
| 228 void* CodeRange::AllocateRawMemory(const size_t requested, size_t* allocated) { | 225 void* CodeRange::AllocateRawMemory(const size_t requested, size_t* allocated) { |
| 229 ASSERT(current_allocation_block_index_ < allocation_list_.length()); | 226 ASSERT(current_allocation_block_index_ < allocation_list_.length()); |
| 230 if (requested > allocation_list_[current_allocation_block_index_].size) { | 227 if (requested > allocation_list_[current_allocation_block_index_].size) { |
| 231 // Find an allocation block large enough. This function call may | 228 // Find an allocation block large enough. This function call may |
| 232 // call V8::FatalProcessOutOfMemory if it cannot find a large enough block. | 229 // call V8::FatalProcessOutOfMemory if it cannot find a large enough block. |
| 233 GetNextAllocationBlock(requested); | 230 GetNextAllocationBlock(requested); |
| 234 } | 231 } |
| 235 // Commit the requested memory at the start of the current allocation block. | 232 // Commit the requested memory at the start of the current allocation block. |
| 236 *allocated = RoundUp(requested, Page::kPageSize); | 233 *allocated = RoundUp(requested, MemoryChunk::kAlignment); |
| 237 FreeBlock current = allocation_list_[current_allocation_block_index_]; | 234 FreeBlock current = allocation_list_[current_allocation_block_index_]; |
| 238 if (*allocated >= current.size - Page::kPageSize) { | 235 if (*allocated >= current.size - Page::kPageSize) { |
| 239 // Don't leave a small free block, useless for a large object or chunk. | 236 // Don't leave a small free block, useless for a large object or chunk. |
| 240 *allocated = current.size; | 237 *allocated = current.size; |
| 241 } | 238 } |
| 242 ASSERT(*allocated <= current.size); | 239 ASSERT(*allocated <= current.size); |
| 240 ASSERT(IsAligned(static_cast<intptr_t>(*allocated), MemoryChunk::kAlignment)); | |
| 241 ASSERT(IsAligned(OffsetFrom(current.start), MemoryChunk::kAlignment)); | |
| 243 if (!code_range_->Commit(current.start, *allocated, true)) { | 242 if (!code_range_->Commit(current.start, *allocated, true)) { |
| 244 *allocated = 0; | 243 *allocated = 0; |
| 245 return NULL; | 244 return NULL; |
| 246 } | 245 } |
| 247 allocation_list_[current_allocation_block_index_].start += *allocated; | 246 allocation_list_[current_allocation_block_index_].start += *allocated; |
| 248 allocation_list_[current_allocation_block_index_].size -= *allocated; | 247 allocation_list_[current_allocation_block_index_].size -= *allocated; |
| 249 if (*allocated == current.size) { | 248 if (*allocated == current.size) { |
| 250 GetNextAllocationBlock(0); // This block is used up, get the next one. | 249 GetNextAllocationBlock(0); // This block is used up, get the next one. |
| 251 } | 250 } |
| 252 return current.start; | 251 return current.start; |
| 253 } | 252 } |
| 254 | 253 |
| 255 | 254 |
| 256 void CodeRange::FreeRawMemory(void* address, size_t length) { | 255 void CodeRange::FreeRawMemory(void* address, size_t length) { |
| 256 ASSERT(IsAligned(reinterpret_cast<intptr_t>(address), | |
| 257 MemoryChunk::kAlignment)); | |
| 258 ASSERT(IsAligned(static_cast<intptr_t>(length), MemoryChunk::kAlignment)); | |
| 257 free_list_.Add(FreeBlock(address, length)); | 259 free_list_.Add(FreeBlock(address, length)); |
| 258 code_range_->Uncommit(address, length); | 260 code_range_->Uncommit(address, length); |
| 259 } | 261 } |
| 260 | 262 |
| 261 | 263 |
| 262 void CodeRange::TearDown() { | 264 void CodeRange::TearDown() { |
| 263 delete code_range_; // Frees all memory in the virtual memory range. | 265 delete code_range_; // Frees all memory in the virtual memory range. |
| 264 code_range_ = NULL; | 266 code_range_ = NULL; |
| 265 free_list_.Free(); | 267 free_list_.Free(); |
| 266 allocation_list_.Free(); | 268 allocation_list_.Free(); |
| 267 } | 269 } |
| 268 | 270 |
| 269 | 271 |
| 270 // ----------------------------------------------------------------------------- | 272 // ----------------------------------------------------------------------------- |
| 271 // MemoryAllocator | 273 // MemoryAllocator |
| 272 // | 274 // |
| 273 intptr_t MemoryAllocator::capacity_ = 0; | 275 size_t MemoryAllocator::capacity_ = 0; |
| 274 intptr_t MemoryAllocator::capacity_executable_ = 0; | 276 size_t MemoryAllocator::capacity_executable_ = 0; |
| 275 intptr_t MemoryAllocator::size_ = 0; | 277 size_t MemoryAllocator::size_ = 0; |
| 276 intptr_t MemoryAllocator::size_executable_ = 0; | 278 size_t MemoryAllocator::size_executable_ = 0; |
| 277 | 279 |
| 278 List<MemoryAllocator::MemoryAllocationCallbackRegistration> | 280 List<MemoryAllocator::MemoryAllocationCallbackRegistration> |
| 279 MemoryAllocator::memory_allocation_callbacks_; | 281 MemoryAllocator::memory_allocation_callbacks_; |
| 280 | 282 |
| 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) { | 283 bool MemoryAllocator::Setup(intptr_t capacity, intptr_t capacity_executable) { |
| 307 capacity_ = RoundUp(capacity, Page::kPageSize); | 284 capacity_ = RoundUp(capacity, Page::kPageSize); |
| 308 capacity_executable_ = RoundUp(capacity_executable, Page::kPageSize); | 285 capacity_executable_ = RoundUp(capacity_executable, Page::kPageSize); |
| 309 ASSERT_GE(capacity_, capacity_executable_); | 286 ASSERT_GE(capacity_, capacity_executable_); |
| 310 | 287 |
| 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; | 288 size_ = 0; |
| 325 size_executable_ = 0; | 289 size_executable_ = 0; |
| 326 ChunkInfo info; // uninitialized element. | 290 |
| 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; | 291 return true; |
| 333 } | 292 } |
| 334 | 293 |
| 335 | 294 |
| 336 bool MemoryAllocator::SafeIsInAPageChunk(Address addr) { | |
| 337 return InInitialChunk(addr) || InAllocatedChunks(addr); | |
| 338 } | |
| 339 | |
| 340 | |
| 341 void MemoryAllocator::TearDown() { | 295 void MemoryAllocator::TearDown() { |
| 342 for (int i = 0; i < max_nof_chunks_; i++) { | |
| 343 if (chunks_[i].address() != NULL) DeleteChunk(i); | |
| 344 } | |
| 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; | 296 capacity_ = 0; |
| 361 capacity_executable_ = 0; | 297 capacity_executable_ = 0; |
| 362 size_ = 0; | 298 size_ = 0; |
| 363 max_nof_chunks_ = 0; | 299 size_executable_ = 0; |
| 364 } | 300 } |
| 365 | 301 |
| 366 | 302 |
| 367 void MemoryAllocator::FreeChunkTables(uintptr_t* array, int len, int level) { | 303 void MemoryAllocator::FreeMemory(void* base, |
| 368 for (int i = 0; i < len; i++) { | 304 size_t size, |
| 369 if (array[i] != kUnusedChunkTableEntry) { | 305 Executability executable) { |
| 370 uintptr_t* subarray = reinterpret_cast<uintptr_t*>(array[i]); | 306 if (CodeRange::contains(static_cast<Address>(base))) { |
| 371 if (level > 1) { | 307 ASSERT(executable == EXECUTABLE); |
| 372 array[i] = kUnusedChunkTableEntry; | 308 CodeRange::FreeRawMemory(base, size); |
| 373 FreeChunkTables(subarray, 1 << kChunkTableBitsPerLevel, level - 1); | 309 } else { |
| 374 } else { | 310 ASSERT(executable == NOT_EXECUTABLE || !CodeRange::exists()); |
| 375 array[i] = kUnusedChunkTableEntry; | 311 VirtualMemory::ReleaseRegion(base, size); |
| 376 } | 312 } |
| 377 delete[] subarray; | 313 |
| 378 } | 314 Counters::memory_allocated.Decrement(static_cast<int>(size)); |
| 379 } | 315 |
| 380 } | 316 ASSERT(size_ >= size); |
| 381 | 317 size_ -= size; |
| 382 | 318 |
| 383 void* MemoryAllocator::AllocateRawMemory(const size_t requested, | 319 if (executable == EXECUTABLE) { |
| 384 size_t* allocated, | 320 ASSERT(size_executable_ >= size); |
| 385 Executability executable) { | 321 size_executable_ -= size; |
| 386 if (size_ + static_cast<size_t>(requested) > static_cast<size_t>(capacity_)) { | 322 } |
| 323 } | |
| 324 | |
| 325 | |
| 326 void* MemoryAllocator::ReserveAlignedMemory(const size_t requested, | |
|
Erik Corry
2010/12/22 13:48:27
Shouldn't this return an Address?
Vyacheslav Egorov (Chromium)
2010/12/22 19:55:07
Done. I eradicated void* usages.
| |
| 327 size_t alignment, | |
| 328 size_t* allocated_size) { | |
| 329 ASSERT(IsAligned(alignment, OS::AllocateAlignment())); | |
| 330 if (size_ + requested > capacity_) return false; | |
| 331 | |
| 332 size_t allocated = RoundUp(requested + alignment, OS::AllocateAlignment()); | |
| 333 | |
| 334 uintptr_t base = reinterpret_cast<intptr_t>( | |
| 335 VirtualMemory::ReserveRegion(allocated)); | |
| 336 | |
| 337 uintptr_t end = base + allocated; | |
| 338 | |
| 339 if (base == 0) return NULL; | |
| 340 | |
| 341 uintptr_t aligned_base = RoundUp(base, alignment); | |
| 342 | |
| 343 ASSERT(aligned_base + requested <= base + allocated); | |
| 344 | |
| 345 // The difference between re-aligned base address and base address is | |
| 346 // multiple of OS::AllocateAlignment(). | |
| 347 if (aligned_base != base) { | |
| 348 ASSERT(aligned_base > base); | |
| 349 // TODO(gc) check result of operation? | |
| 350 VirtualMemory::ReleaseRegion(reinterpret_cast<void*>(base), | |
| 351 aligned_base - base); | |
| 352 allocated -= (aligned_base - base); | |
| 353 base = aligned_base; | |
| 354 } | |
| 355 | |
| 356 ASSERT(base + allocated == end); | |
| 357 | |
| 358 uintptr_t requested_end = base + requested; | |
| 359 uintptr_t aligned_requested_end = | |
| 360 RoundUp(requested_end, OS::AllocateAlignment()); | |
| 361 | |
| 362 if (aligned_requested_end < end) { | |
| 363 // TODO(gc) check result of operation? | |
| 364 VirtualMemory::ReleaseRegion(reinterpret_cast<void*>(aligned_requested_end), | |
| 365 end - aligned_requested_end); | |
| 366 allocated = aligned_requested_end - base; | |
| 367 } | |
| 368 | |
| 369 size_ += allocated; | |
| 370 *allocated_size = allocated; | |
| 371 return reinterpret_cast<void*>(base); | |
| 372 } | |
| 373 | |
| 374 | |
| 375 void* MemoryAllocator::AllocateAlignedMemory(const size_t requested, | |
| 376 size_t alignment, | |
| 377 Executability executable, | |
| 378 size_t* allocated_size) { | |
| 379 void* base = | |
| 380 ReserveAlignedMemory(requested, Page::kPageSize, allocated_size); | |
| 381 | |
| 382 if (base == NULL) return NULL; | |
| 383 | |
| 384 if (!VirtualMemory::CommitRegion(base, | |
| 385 *allocated_size, | |
| 386 executable == EXECUTABLE)) { | |
| 387 VirtualMemory::ReleaseRegion(base, *allocated_size); | |
| 388 size_ -= *allocated_size; | |
| 387 return NULL; | 389 return NULL; |
| 388 } | 390 } |
| 389 | 391 |
| 390 void* mem; | 392 return base; |
| 393 } | |
| 394 | |
| 395 | |
| 396 MemoryChunk* MemoryAllocator::AllocateChunk(intptr_t body_size, | |
| 397 Executability executable, | |
| 398 Space* owner) { | |
| 399 size_t chunk_size = MemoryChunk::kBodyOffset + body_size; | |
| 400 void* base = NULL; | |
| 391 if (executable == EXECUTABLE) { | 401 if (executable == EXECUTABLE) { |
| 392 // Check executable memory limit. | 402 // Check executable memory limit. |
| 393 if (size_executable_ + requested > | 403 if (size_executable_ + chunk_size > capacity_executable_) { |
| 394 static_cast<size_t>(capacity_executable_)) { | |
| 395 LOG(StringEvent("MemoryAllocator::AllocateRawMemory", | 404 LOG(StringEvent("MemoryAllocator::AllocateRawMemory", |
| 396 "V8 Executable Allocation capacity exceeded")); | 405 "V8 Executable Allocation capacity exceeded")); |
| 397 return NULL; | 406 return NULL; |
| 398 } | 407 } |
| 408 | |
| 399 // Allocate executable memory either from code range or from the | 409 // Allocate executable memory either from code range or from the |
| 400 // OS. | 410 // OS. |
| 401 if (CodeRange::exists()) { | 411 if (CodeRange::exists()) { |
| 402 mem = CodeRange::AllocateRawMemory(requested, allocated); | 412 base = CodeRange::AllocateRawMemory(chunk_size, &chunk_size); |
| 413 ASSERT(IsAligned(reinterpret_cast<intptr_t>(base), | |
| 414 MemoryChunk::kAlignment)); | |
| 415 size_ += chunk_size; | |
| 403 } else { | 416 } else { |
| 404 mem = OS::Allocate(requested, allocated, true); | 417 base = AllocateAlignedMemory(chunk_size, |
| 418 MemoryChunk::kAlignment, | |
| 419 executable, | |
| 420 &chunk_size); | |
| 405 } | 421 } |
| 422 | |
| 423 if (base == NULL) return NULL; | |
| 424 | |
| 406 // Update executable memory size. | 425 // Update executable memory size. |
| 407 size_executable_ += static_cast<int>(*allocated); | 426 size_executable_ += chunk_size; |
| 408 } else { | 427 } else { |
| 409 mem = OS::Allocate(requested, allocated, false); | 428 base = AllocateAlignedMemory(chunk_size, |
| 410 } | 429 MemoryChunk::kAlignment, |
| 411 int alloced = static_cast<int>(*allocated); | 430 executable, |
| 412 size_ += alloced; | 431 &chunk_size); |
| 432 | |
| 433 if (base == NULL) return NULL; | |
| 434 } | |
| 413 | 435 |
| 414 #ifdef DEBUG | 436 #ifdef DEBUG |
| 415 ZapBlock(reinterpret_cast<Address>(mem), alloced); | 437 ZapBlock(reinterpret_cast<Address>(base), chunk_size); |
| 416 #endif | 438 #endif |
| 417 Counters::memory_allocated.Increment(alloced); | 439 Counters::memory_allocated.Increment(chunk_size); |
| 418 return mem; | 440 |
| 419 } | 441 LOG(NewEvent("MemoryChunk", base, chunk_size)); |
| 420 | 442 if (owner != NULL) { |
| 421 | 443 ObjectSpace space = static_cast<ObjectSpace>(1 << owner->identity()); |
| 422 void MemoryAllocator::FreeRawMemory(void* mem, | 444 PerformAllocationCallback(space, kAllocationActionAllocate, chunk_size); |
| 423 size_t length, | 445 } |
| 446 | |
| 447 return MemoryChunk::Initialize(reinterpret_cast<Address>(base), | |
| 448 chunk_size, | |
| 449 executable, | |
| 450 owner); | |
| 451 } | |
| 452 | |
| 453 | |
| 454 Page* MemoryAllocator::AllocatePage(PagedSpace* owner, | |
| 424 Executability executable) { | 455 Executability executable) { |
| 425 #ifdef DEBUG | 456 MemoryChunk* chunk = AllocateChunk(Page::kObjectAreaSize, executable, owner); |
| 426 ZapBlock(reinterpret_cast<Address>(mem), length); | 457 |
| 427 #endif | 458 if (chunk == NULL) return NULL; |
| 428 if (CodeRange::contains(static_cast<Address>(mem))) { | 459 |
| 429 CodeRange::FreeRawMemory(mem, length); | 460 return Page::Initialize(chunk); |
| 430 } else { | 461 } |
| 431 OS::Free(mem, length); | 462 |
| 432 } | 463 |
| 433 Counters::memory_allocated.Decrement(static_cast<int>(length)); | 464 LargePage* MemoryAllocator::AllocateLargePage(intptr_t object_size, |
| 434 size_ -= static_cast<int>(length); | 465 Executability executable, |
| 435 if (executable == EXECUTABLE) size_executable_ -= static_cast<int>(length); | 466 Space* owner) { |
| 436 | 467 MemoryChunk* chunk = AllocateChunk(object_size, executable, owner); |
| 437 ASSERT(size_ >= 0); | 468 if (chunk == NULL) return NULL; |
| 438 ASSERT(size_executable_ >= 0); | 469 return LargePage::Initialize(chunk); |
| 439 } | 470 } |
| 440 | 471 |
| 441 | 472 |
| 442 void MemoryAllocator::PerformAllocationCallback(ObjectSpace space, | 473 void MemoryAllocator::Free(MemoryChunk* chunk) { |
| 443 AllocationAction action, | 474 LOG(DeleteEvent("MemoryChunk", chunk)); |
| 444 size_t size) { | 475 if (chunk->owner() != NULL) { |
| 445 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { | 476 ObjectSpace space = |
| 446 MemoryAllocationCallbackRegistration registration = | 477 static_cast<ObjectSpace>(1 << chunk->owner()->identity()); |
| 447 memory_allocation_callbacks_[i]; | 478 PerformAllocationCallback(space, kAllocationActionFree, chunk->size()); |
| 448 if ((registration.space & space) == space && | 479 } |
| 449 (registration.action & action) == action) | 480 |
| 450 registration.callback(space, action, static_cast<int>(size)); | 481 FreeMemory(reinterpret_cast<void*>(chunk->address()), |
| 451 } | 482 chunk->size(), |
| 452 } | 483 chunk->executable()); |
| 453 | 484 } |
| 454 | 485 |
| 455 bool MemoryAllocator::MemoryAllocationCallbackRegistered( | 486 |
| 456 MemoryAllocationCallback callback) { | 487 bool MemoryAllocator::CommitBlock(Address start, |
| 457 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { | 488 size_t size, |
| 458 if (memory_allocation_callbacks_[i].callback == callback) return true; | 489 Executability executable) { |
| 459 } | 490 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 | 491 #ifdef DEBUG |
| 560 ZapBlock(start, size); | 492 ZapBlock(start, size); |
| 561 #endif | 493 #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)); | 494 Counters::memory_allocated.Increment(static_cast<int>(size)); |
| 587 return true; | 495 return true; |
| 588 } | 496 } |
| 589 | 497 |
| 590 | 498 |
| 591 bool MemoryAllocator::UncommitBlock(Address start, size_t size) { | 499 bool MemoryAllocator::UncommitBlock(Address start, size_t size) { |
| 592 ASSERT(start != NULL); | 500 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)); | 501 Counters::memory_allocated.Decrement(static_cast<int>(size)); |
| 600 return true; | 502 return true; |
| 601 } | 503 } |
| 602 | 504 |
| 603 | 505 |
| 604 void MemoryAllocator::ZapBlock(Address start, size_t size) { | 506 void MemoryAllocator::ZapBlock(Address start, size_t size) { |
| 605 for (size_t s = 0; s + kPointerSize <= size; s += kPointerSize) { | 507 for (size_t s = 0; s + kPointerSize <= size; s += kPointerSize) { |
| 606 Memory::Address_at(start + s) = kZapValue; | 508 Memory::Address_at(start + s) = kZapValue; |
| 607 } | 509 } |
| 608 } | 510 } |
| 609 | 511 |
| 610 | 512 |
| 611 Page* MemoryAllocator::InitializePagesInChunk(int chunk_id, int pages_in_chunk, | 513 void MemoryAllocator::PerformAllocationCallback(ObjectSpace space, |
| 612 PagedSpace* owner) { | 514 AllocationAction action, |
| 613 ASSERT(IsValidChunk(chunk_id)); | 515 size_t size) { |
| 614 ASSERT(pages_in_chunk > 0); | 516 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { |
| 615 | 517 MemoryAllocationCallbackRegistration registration = |
| 616 Address chunk_start = chunks_[chunk_id].address(); | 518 memory_allocation_callbacks_[i]; |
| 617 | 519 if ((registration.space & space) == space && |
| 618 Address low = RoundUp(chunk_start, Page::kPageSize); | 520 (registration.action & action) == action) |
| 619 | 521 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 } | 522 } |
| 684 } | 523 } |
| 685 | 524 |
| 686 | 525 |
| 687 void MemoryAllocator::DeleteChunk(int chunk_id) { | 526 bool MemoryAllocator::MemoryAllocationCallbackRegistered( |
| 688 ASSERT(IsValidChunk(chunk_id)); | 527 MemoryAllocationCallback callback) { |
| 689 | 528 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { |
| 690 ChunkInfo& c = chunks_[chunk_id]; | 529 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 } | 530 } |
| 708 c.init(NULL, 0, NULL); | 531 return false; |
| 709 Push(chunk_id); | |
| 710 } | 532 } |
| 711 | 533 |
| 712 | 534 |
| 713 Page* MemoryAllocator::FindFirstPageInSameChunk(Page* p) { | 535 void MemoryAllocator::AddMemoryAllocationCallback( |
| 714 int chunk_id = GetChunkId(p); | 536 MemoryAllocationCallback callback, |
| 715 ASSERT(IsValidChunk(chunk_id)); | 537 ObjectSpace space, |
| 716 | 538 AllocationAction action) { |
| 717 Address low = RoundUp(chunks_[chunk_id].address(), Page::kPageSize); | 539 ASSERT(callback != NULL); |
| 718 return Page::FromAddress(low); | 540 MemoryAllocationCallbackRegistration registration(callback, space, action); |
| 541 ASSERT(!MemoryAllocator::MemoryAllocationCallbackRegistered(callback)); | |
| 542 return memory_allocation_callbacks_.Add(registration); | |
| 719 } | 543 } |
| 720 | 544 |
| 721 | 545 |
| 722 Page* MemoryAllocator::FindLastPageInSameChunk(Page* p) { | 546 void MemoryAllocator::RemoveMemoryAllocationCallback( |
| 723 int chunk_id = GetChunkId(p); | 547 MemoryAllocationCallback callback) { |
| 724 ASSERT(IsValidChunk(chunk_id)); | 548 ASSERT(callback != NULL); |
| 725 | 549 for (int i = 0; i < memory_allocation_callbacks_.length(); ++i) { |
| 726 Address chunk_start = chunks_[chunk_id].address(); | 550 if (memory_allocation_callbacks_[i].callback == callback) { |
| 727 size_t chunk_size = chunks_[chunk_id].size(); | 551 memory_allocation_callbacks_.Remove(i); |
| 728 | 552 return; |
| 729 Address high = RoundDown(chunk_start + chunk_size, Page::kPageSize); | 553 } |
| 730 ASSERT(chunk_start <= p->address() && p->address() < high); | 554 } |
| 731 | 555 UNREACHABLE(); |
| 732 return Page::FromAddress(high - Page::kPageSize); | |
| 733 } | 556 } |
| 734 | 557 |
| 735 | 558 |
| 736 #ifdef DEBUG | 559 #ifdef DEBUG |
| 737 void MemoryAllocator::ReportStatistics() { | 560 void MemoryAllocator::ReportStatistics() { |
| 738 float pct = static_cast<float>(capacity_ - size_) / capacity_; | 561 float pct = static_cast<float>(capacity_ - size_) / capacity_; |
| 739 PrintF(" capacity: %" V8_PTR_PREFIX "d" | 562 PrintF(" capacity: %" V8_PTR_PREFIX "d" |
| 740 ", used: %" V8_PTR_PREFIX "d" | 563 ", used: %" V8_PTR_PREFIX "d" |
| 741 ", available: %%%d\n\n", | 564 ", available: %%%d\n\n", |
| 742 capacity_, size_, static_cast<int>(pct*100)); | 565 capacity_, size_, static_cast<int>(pct*100)); |
| 743 } | 566 } |
| 744 #endif | 567 #endif |
| 745 | 568 |
| 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 // ----------------------------------------------------------------------------- | 569 // ----------------------------------------------------------------------------- |
| 934 // PagedSpace implementation | 570 // PagedSpace implementation |
| 935 | 571 |
| 936 PagedSpace::PagedSpace(intptr_t max_capacity, | 572 PagedSpace::PagedSpace(intptr_t max_capacity, |
| 937 AllocationSpace id, | 573 AllocationSpace id, |
| 938 Executability executable) | 574 Executability executable) |
| 939 : Space(id, executable) { | 575 : Space(id, executable) { |
| 940 max_capacity_ = (RoundDown(max_capacity, Page::kPageSize) / Page::kPageSize) | 576 max_capacity_ = (RoundDown(max_capacity, Page::kPageSize) / Page::kPageSize) |
| 941 * Page::kObjectAreaSize; | 577 * Page::kObjectAreaSize; |
| 942 accounting_stats_.Clear(); | 578 accounting_stats_.Clear(); |
| 943 | 579 |
| 944 allocation_info_.top = NULL; | 580 allocation_info_.top = NULL; |
| 945 allocation_info_.limit = NULL; | 581 allocation_info_.limit = NULL; |
| 946 | |
| 947 mc_forwarding_info_.top = NULL; | |
| 948 mc_forwarding_info_.limit = NULL; | |
| 949 } | 582 } |
| 950 | 583 |
| 951 | 584 |
| 952 bool PagedSpace::Setup(Address start, size_t size) { | 585 bool PagedSpace::Setup() { |
| 953 if (HasBeenSetup()) return false; | 586 if (HasBeenSetup()) return false; |
| 954 | 587 |
| 955 int num_pages = 0; | 588 // 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 | 589 if (max_capacity_ < Page::kPageSize) return false; |
| 957 // contain at least one page, ignore it and allocate instead. | 590 |
| 958 int pages_in_chunk = PagesInChunk(start, size); | 591 first_page_ = MemoryAllocator::AllocatePage(this, executable()); |
| 959 if (pages_in_chunk > 0) { | 592 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 | 593 |
| 972 // We are sure that the first page is valid and that we have at least one | 594 // We are sure that the first page is valid and that we have at least one |
| 973 // page. | 595 // page. |
| 974 ASSERT(first_page_->is_valid()); | 596 accounting_stats_.ExpandSpace(Page::kObjectAreaSize); |
| 975 ASSERT(num_pages > 0); | |
| 976 accounting_stats_.ExpandSpace(num_pages * Page::kObjectAreaSize); | |
| 977 ASSERT(Capacity() <= max_capacity_); | 597 ASSERT(Capacity() <= max_capacity_); |
| 978 | 598 |
| 979 // Sequentially clear region marks in the newly allocated | 599 last_page_ = first_page_; |
| 980 // pages and cache the current last page in the space. | 600 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 | 601 |
| 986 // Use first_page_ for allocation. | 602 // Use first_page_ for allocation. |
| 987 SetAllocationInfo(&allocation_info_, first_page_); | 603 SetAllocationInfo(&allocation_info_, first_page_); |
| 988 | 604 |
| 989 page_list_is_chunk_ordered_ = true; | |
| 990 | |
| 991 return true; | 605 return true; |
| 992 } | 606 } |
| 993 | 607 |
| 994 | 608 |
| 995 bool PagedSpace::HasBeenSetup() { | 609 bool PagedSpace::HasBeenSetup() { |
| 996 return (Capacity() > 0); | 610 return (Capacity() > 0); |
| 997 } | 611 } |
| 998 | 612 |
| 999 | 613 |
| 1000 void PagedSpace::TearDown() { | 614 void PagedSpace::TearDown() { |
| 1001 MemoryAllocator::FreeAllPages(this); | 615 Page* next = NULL; |
| 616 for (Page* p = first_page_; p->is_valid(); p = next) { | |
| 617 next = p->next_page(); | |
| 618 MemoryAllocator::Free(p); | |
| 619 } | |
| 1002 first_page_ = NULL; | 620 first_page_ = NULL; |
| 621 last_page_ = NULL; | |
| 1003 accounting_stats_.Clear(); | 622 accounting_stats_.Clear(); |
| 1004 } | 623 } |
| 1005 | 624 |
| 1006 | 625 |
| 1007 #ifdef ENABLE_HEAP_PROTECTION | 626 #ifdef ENABLE_HEAP_PROTECTION |
| 1008 | 627 |
| 1009 void PagedSpace::Protect() { | 628 void PagedSpace::Protect() { |
| 1010 Page* page = first_page_; | 629 Page* page = first_page_; |
| 1011 while (page->is_valid()) { | 630 while (page->is_valid()) { |
| 1012 MemoryAllocator::ProtectChunkFromPage(page); | 631 MemoryAllocator::ProtectChunkFromPage(page); |
| 1013 page = MemoryAllocator::FindLastPageInSameChunk(page)->next_page(); | 632 page = MemoryAllocator::FindLastPageInSameChunk(page)->next_page(); |
| 1014 } | 633 } |
| 1015 } | 634 } |
| 1016 | 635 |
| 1017 | 636 |
| 1018 void PagedSpace::Unprotect() { | 637 void PagedSpace::Unprotect() { |
| 1019 Page* page = first_page_; | 638 Page* page = first_page_; |
| 1020 while (page->is_valid()) { | 639 while (page->is_valid()) { |
| 1021 MemoryAllocator::UnprotectChunkFromPage(page); | 640 MemoryAllocator::UnprotectChunkFromPage(page); |
| 1022 page = MemoryAllocator::FindLastPageInSameChunk(page)->next_page(); | 641 page = MemoryAllocator::FindLastPageInSameChunk(page)->next_page(); |
| 1023 } | 642 } |
| 1024 } | 643 } |
| 1025 | 644 |
| 1026 #endif | 645 #endif |
| 1027 | 646 |
| 1028 | 647 |
| 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) { | 648 MaybeObject* PagedSpace::FindObject(Address addr) { |
| 1038 // Note: this function can only be called before or after mark-compact GC | 649 // Note: this function can only be called before or after mark-compact GC |
| 1039 // because it accesses map pointers. | 650 // because it accesses map pointers. |
| 1040 ASSERT(!MarkCompactCollector::in_use()); | 651 ASSERT(!MarkCompactCollector::in_use()); |
| 1041 | 652 |
| 1042 if (!Contains(addr)) return Failure::Exception(); | 653 if (!Contains(addr)) return Failure::Exception(); |
| 1043 | 654 |
| 1044 Page* p = Page::FromAddress(addr); | 655 Page* p = Page::FromAddress(addr); |
| 1045 ASSERT(IsUsed(p)); | 656 ASSERT(IsUsed(p)); |
| 1046 Address cur = p->ObjectAreaStart(); | 657 Address cur = p->ObjectAreaStart(); |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 1066 } | 677 } |
| 1067 | 678 |
| 1068 | 679 |
| 1069 void PagedSpace::SetAllocationInfo(AllocationInfo* alloc_info, Page* p) { | 680 void PagedSpace::SetAllocationInfo(AllocationInfo* alloc_info, Page* p) { |
| 1070 alloc_info->top = p->ObjectAreaStart(); | 681 alloc_info->top = p->ObjectAreaStart(); |
| 1071 alloc_info->limit = p->ObjectAreaEnd(); | 682 alloc_info->limit = p->ObjectAreaEnd(); |
| 1072 ASSERT(alloc_info->VerifyPagedAllocation()); | 683 ASSERT(alloc_info->VerifyPagedAllocation()); |
| 1073 } | 684 } |
| 1074 | 685 |
| 1075 | 686 |
| 1076 void PagedSpace::MCResetRelocationInfo() { | 687 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); | 688 ASSERT(max_capacity_ % Page::kObjectAreaSize == 0); |
| 1144 ASSERT(Capacity() % Page::kObjectAreaSize == 0); | 689 ASSERT(Capacity() % Page::kObjectAreaSize == 0); |
| 1145 | 690 |
| 1146 if (Capacity() == max_capacity_) return false; | 691 if (Capacity() == max_capacity_) return false; |
| 1147 | 692 |
| 1148 ASSERT(Capacity() < max_capacity_); | 693 ASSERT(Capacity() < max_capacity_); |
| 1149 // Last page must be valid and its next page is invalid. | 694 // Last page must be valid and its next page is invalid. |
| 1150 ASSERT(last_page->is_valid() && !last_page->next_page()->is_valid()); | 695 ASSERT(last_page_->is_valid() && !last_page_->next_page()->is_valid()); |
| 1151 | 696 |
| 1152 int available_pages = | 697 // We are going to exceed capacity for this space. |
| 1153 static_cast<int>((max_capacity_ - Capacity()) / Page::kObjectAreaSize); | 698 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 | 699 |
| 1159 int desired_pages = Min(available_pages, MemoryAllocator::kPagesPerChunk); | 700 Page* p = MemoryAllocator::AllocatePage(this, executable()); |
| 1160 Page* p = MemoryAllocator::AllocatePages(desired_pages, &desired_pages, this); | |
| 1161 if (!p->is_valid()) return false; | 701 if (!p->is_valid()) return false; |
| 1162 | 702 |
| 1163 accounting_stats_.ExpandSpace(desired_pages * Page::kObjectAreaSize); | 703 accounting_stats_.ExpandSpace(Page::kObjectAreaSize); |
| 1164 ASSERT(Capacity() <= max_capacity_); | 704 ASSERT(Capacity() <= max_capacity_); |
| 1165 | 705 |
| 1166 MemoryAllocator::SetNextPage(last_page, p); | 706 last_page_->set_next_page(p); |
| 707 last_page_ = p; | |
| 1167 | 708 |
| 1168 // Sequentially clear region marks of new pages and and cache the | 709 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 | 710 |
| 1176 return true; | 711 return true; |
| 1177 } | 712 } |
| 1178 | 713 |
| 1179 | 714 |
| 1180 #ifdef DEBUG | 715 #ifdef DEBUG |
| 1181 int PagedSpace::CountTotalPages() { | 716 int PagedSpace::CountTotalPages() { |
| 1182 int count = 0; | 717 int count = 0; |
| 1183 for (Page* p = first_page_; p->is_valid(); p = p->next_page()) { | 718 for (Page* p = first_page_; p->is_valid(); p = p->next_page()) { |
| 1184 count++; | 719 count++; |
| 1185 } | 720 } |
| 1186 return count; | 721 return count; |
| 1187 } | 722 } |
| 1188 #endif | 723 #endif |
| 1189 | 724 |
| 1190 | 725 |
| 1191 void PagedSpace::Shrink() { | 726 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(); | 727 Page* top_page = AllocationTopPage(); |
| 1200 ASSERT(top_page->is_valid()); | 728 ASSERT(top_page->is_valid()); |
| 1201 | 729 |
| 1202 // Count the number of pages we would like to free. | 730 // TODO(gc) release half of pages? |
| 1203 int pages_to_free = 0; | 731 if (top_page->next_page()->is_valid()) { |
| 1204 for (Page* p = top_page->next_page(); p->is_valid(); p = p->next_page()) { | 732 int pages_freed = 0; |
| 1205 pages_to_free++; | 733 Page* page = top_page->next_page(); |
| 734 Page* next_page; | |
| 735 while (page->is_valid()) { | |
| 736 next_page = page->next_page(); | |
| 737 MemoryAllocator::Free(page); | |
| 738 pages_freed++; | |
| 739 page = next_page; | |
| 740 } | |
| 741 top_page->set_next_page(Page::FromAddress(NULL)); | |
| 742 last_page_ = top_page; | |
| 743 | |
| 744 accounting_stats_.ShrinkSpace(pages_freed * Page::kObjectAreaSize); | |
| 745 ASSERT(Capacity() == CountTotalPages() * Page::kObjectAreaSize); | |
| 1206 } | 746 } |
| 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 } | 747 } |
| 1223 | 748 |
| 1224 | 749 |
| 1225 bool PagedSpace::EnsureCapacity(int capacity) { | 750 bool PagedSpace::EnsureCapacity(int capacity) { |
| 1226 if (Capacity() >= capacity) return true; | 751 while (Capacity() < capacity) { |
| 1227 | 752 // 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. | 753 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 } | 754 } |
| 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; | 755 return true; |
| 1245 } | 756 } |
| 1246 | 757 |
| 1247 | 758 |
| 1248 #ifdef DEBUG | 759 #ifdef DEBUG |
| 1249 void PagedSpace::Print() { } | 760 void PagedSpace::Print() { } |
| 1250 #endif | 761 #endif |
| 1251 | 762 |
| 1252 | 763 |
| 1253 #ifdef DEBUG | 764 #ifdef DEBUG |
| 1254 // We do not assume that the PageIterator works, because it depends on the | 765 // We do not assume that the PageIterator works, because it depends on the |
| 1255 // invariants we are checking during verification. | 766 // invariants we are checking during verification. |
| 1256 void PagedSpace::Verify(ObjectVisitor* visitor) { | 767 void PagedSpace::Verify(ObjectVisitor* visitor) { |
| 1257 // The allocation pointer should be valid, and it should be in a page in the | 768 // The allocation pointer should be valid, and it should be in a page in the |
| 1258 // space. | 769 // space. |
| 1259 ASSERT(allocation_info_.VerifyPagedAllocation()); | 770 ASSERT(allocation_info_.VerifyPagedAllocation()); |
| 1260 Page* top_page = Page::FromAllocationTop(allocation_info_.top); | 771 Page* top_page = Page::FromAllocationTop(allocation_info_.top); |
| 1261 ASSERT(MemoryAllocator::IsPageInSpace(top_page, this)); | |
| 1262 | 772 |
| 1263 // Loop over all the pages. | 773 // Loop over all the pages. |
| 1264 bool above_allocation_top = false; | 774 bool above_allocation_top = false; |
| 1265 Page* current_page = first_page_; | 775 Page* current_page = first_page_; |
| 1266 while (current_page->is_valid()) { | 776 while (current_page->is_valid()) { |
| 1267 if (above_allocation_top) { | 777 if (above_allocation_top) { |
| 1268 // We don't care what's above the allocation top. | 778 // We don't care what's above the allocation top. |
| 1269 } else { | 779 } else { |
| 1270 Address top = current_page->AllocationTop(); | 780 Address top = current_page->AllocationTop(); |
| 1271 if (current_page == top_page) { | 781 if (current_page == top_page) { |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1307 current_page = current_page->next_page(); | 817 current_page = current_page->next_page(); |
| 1308 } | 818 } |
| 1309 } | 819 } |
| 1310 #endif | 820 #endif |
| 1311 | 821 |
| 1312 | 822 |
| 1313 // ----------------------------------------------------------------------------- | 823 // ----------------------------------------------------------------------------- |
| 1314 // NewSpace implementation | 824 // NewSpace implementation |
| 1315 | 825 |
| 1316 | 826 |
| 1317 bool NewSpace::Setup(Address start, int size) { | 827 bool NewSpace::Setup(int maximum_semispace_capacity) { |
| 1318 // Setup new space based on the preallocated memory block defined by | 828 // Setup new space based on the preallocated memory block defined by |
| 1319 // start and size. The provided space is divided into two semi-spaces. | 829 // 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 | 830 // 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. | 831 // this chunk must be a power of two and it must be aligned to its size. |
| 1322 int initial_semispace_capacity = Heap::InitialSemiSpaceSize(); | 832 int initial_semispace_capacity = Heap::InitialSemiSpaceSize(); |
| 1323 int maximum_semispace_capacity = Heap::MaxSemiSpaceSize(); | 833 |
| 834 size_t size = 0; | |
| 835 void* base = | |
| 836 MemoryAllocator::ReserveAlignedMemory(2*maximum_semispace_capacity, | |
| 837 2*maximum_semispace_capacity, | |
| 838 &size); | |
| 839 | |
| 840 if (base == NULL) return false; | |
| 841 | |
| 842 chunk_base_ = static_cast<Address>(base); | |
| 843 chunk_size_ = static_cast<uintptr_t>(size); | |
| 844 LOG(NewEvent("InitialChunk", chunk_base_, chunk_size_)); | |
| 1324 | 845 |
| 1325 ASSERT(initial_semispace_capacity <= maximum_semispace_capacity); | 846 ASSERT(initial_semispace_capacity <= maximum_semispace_capacity); |
| 1326 ASSERT(IsPowerOf2(maximum_semispace_capacity)); | 847 ASSERT(IsPowerOf2(maximum_semispace_capacity)); |
| 1327 | 848 |
| 1328 // Allocate and setup the histogram arrays if necessary. | 849 // Allocate and setup the histogram arrays if necessary. |
| 1329 #if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING) | 850 #if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING) |
| 1330 allocated_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1); | 851 allocated_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1); |
| 1331 promoted_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1); | 852 promoted_histogram_ = NewArray<HistogramInfo>(LAST_TYPE + 1); |
| 1332 | 853 |
| 1333 #define SET_NAME(name) allocated_histogram_[name].set_name(#name); \ | 854 #define SET_NAME(name) allocated_histogram_[name].set_name(#name); \ |
| 1334 promoted_histogram_[name].set_name(#name); | 855 promoted_histogram_[name].set_name(#name); |
| 1335 INSTANCE_TYPE_LIST(SET_NAME) | 856 INSTANCE_TYPE_LIST(SET_NAME) |
| 1336 #undef SET_NAME | 857 #undef SET_NAME |
| 1337 #endif | 858 #endif |
| 1338 | 859 |
| 1339 ASSERT(size == 2 * Heap::ReservedSemiSpaceSize()); | 860 ASSERT(maximum_semispace_capacity == Heap::ReservedSemiSpaceSize()); |
| 1340 ASSERT(IsAddressAligned(start, size, 0)); | 861 ASSERT(static_cast<intptr_t>(chunk_size_) >= 2 * Heap::ReservedSemiSpaceSize() ); |
| 862 ASSERT(IsAddressAligned(chunk_base_, 2*maximum_semispace_capacity, 0)); | |
|
Erik Corry
2010/12/22 13:48:27
spaces around '*'
Vyacheslav Egorov (Chromium)
2010/12/22 19:55:07
Done.
| |
| 1341 | 863 |
| 1342 if (!to_space_.Setup(start, | 864 if (!to_space_.Setup(chunk_base_, |
| 1343 initial_semispace_capacity, | 865 initial_semispace_capacity, |
| 1344 maximum_semispace_capacity)) { | 866 maximum_semispace_capacity)) { |
| 1345 return false; | 867 return false; |
| 1346 } | 868 } |
| 1347 if (!from_space_.Setup(start + maximum_semispace_capacity, | 869 if (!from_space_.Setup(chunk_base_ + maximum_semispace_capacity, |
| 1348 initial_semispace_capacity, | 870 initial_semispace_capacity, |
| 1349 maximum_semispace_capacity)) { | 871 maximum_semispace_capacity)) { |
| 1350 return false; | 872 return false; |
| 1351 } | 873 } |
| 1352 | 874 |
| 1353 start_ = start; | 875 start_ = chunk_base_; |
| 1354 address_mask_ = ~(size - 1); | 876 address_mask_ = ~(2*maximum_semispace_capacity - 1); |
|
Erik Corry
2010/12/22 13:48:27
and here and a few other places
| |
| 1355 object_mask_ = address_mask_ | kHeapObjectTagMask; | 877 object_mask_ = address_mask_ | kHeapObjectTagMask; |
| 1356 object_expected_ = reinterpret_cast<uintptr_t>(start) | kHeapObjectTag; | 878 object_expected_ = reinterpret_cast<uintptr_t>(start_) | kHeapObjectTag; |
| 1357 | 879 |
| 1358 allocation_info_.top = to_space_.low(); | 880 allocation_info_.top = to_space_.low(); |
| 1359 allocation_info_.limit = to_space_.high(); | 881 allocation_info_.limit = to_space_.high(); |
| 1360 mc_forwarding_info_.top = NULL; | |
| 1361 mc_forwarding_info_.limit = NULL; | |
| 1362 | 882 |
| 1363 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); | 883 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); |
| 1364 return true; | 884 return true; |
| 1365 } | 885 } |
| 1366 | 886 |
| 1367 | 887 |
| 1368 void NewSpace::TearDown() { | 888 void NewSpace::TearDown() { |
| 1369 #if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING) | 889 #if defined(DEBUG) || defined(ENABLE_LOGGING_AND_PROFILING) |
| 1370 if (allocated_histogram_) { | 890 if (allocated_histogram_) { |
| 1371 DeleteArray(allocated_histogram_); | 891 DeleteArray(allocated_histogram_); |
| 1372 allocated_histogram_ = NULL; | 892 allocated_histogram_ = NULL; |
| 1373 } | 893 } |
| 1374 if (promoted_histogram_) { | 894 if (promoted_histogram_) { |
| 1375 DeleteArray(promoted_histogram_); | 895 DeleteArray(promoted_histogram_); |
| 1376 promoted_histogram_ = NULL; | 896 promoted_histogram_ = NULL; |
| 1377 } | 897 } |
| 1378 #endif | 898 #endif |
| 1379 | 899 |
| 1380 start_ = NULL; | 900 start_ = NULL; |
| 1381 allocation_info_.top = NULL; | 901 allocation_info_.top = NULL; |
| 1382 allocation_info_.limit = NULL; | 902 allocation_info_.limit = NULL; |
| 1383 mc_forwarding_info_.top = NULL; | |
| 1384 mc_forwarding_info_.limit = NULL; | |
| 1385 | 903 |
| 1386 to_space_.TearDown(); | 904 to_space_.TearDown(); |
| 1387 from_space_.TearDown(); | 905 from_space_.TearDown(); |
| 906 | |
| 907 LOG(DeleteEvent("InitialChunk", chunk_base_)); | |
| 908 MemoryAllocator::FreeMemory(chunk_base_, | |
| 909 static_cast<size_t>(chunk_size_), | |
| 910 NOT_EXECUTABLE); | |
| 911 chunk_base_ = NULL; | |
| 912 chunk_size_ = 0; | |
| 1388 } | 913 } |
| 1389 | 914 |
| 1390 | 915 |
| 1391 #ifdef ENABLE_HEAP_PROTECTION | 916 #ifdef ENABLE_HEAP_PROTECTION |
| 1392 | 917 |
| 1393 void NewSpace::Protect() { | 918 void NewSpace::Protect() { |
| 1394 MemoryAllocator::Protect(ToSpaceLow(), Capacity()); | 919 MemoryAllocator::Protect(ToSpaceLow(), Capacity()); |
| 1395 MemoryAllocator::Protect(FromSpaceLow(), Capacity()); | 920 MemoryAllocator::Protect(FromSpaceLow(), Capacity()); |
| 1396 } | 921 } |
| 1397 | 922 |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1454 } | 979 } |
| 1455 | 980 |
| 1456 | 981 |
| 1457 void NewSpace::ResetAllocationInfo() { | 982 void NewSpace::ResetAllocationInfo() { |
| 1458 allocation_info_.top = to_space_.low(); | 983 allocation_info_.top = to_space_.low(); |
| 1459 allocation_info_.limit = to_space_.high(); | 984 allocation_info_.limit = to_space_.high(); |
| 1460 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); | 985 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); |
| 1461 } | 986 } |
| 1462 | 987 |
| 1463 | 988 |
| 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 | 989 #ifdef DEBUG |
| 1481 // We do not use the SemispaceIterator because verification doesn't assume | 990 // We do not use the SemispaceIterator because verification doesn't assume |
| 1482 // that it works (it depends on the invariants we are checking). | 991 // that it works (it depends on the invariants we are checking). |
| 1483 void NewSpace::Verify() { | 992 void NewSpace::Verify() { |
| 1484 // The allocation pointer should be in the space or at the very end. | 993 // The allocation pointer should be in the space or at the very end. |
| 1485 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); | 994 ASSERT_SEMISPACE_ALLOCATION_INFO(allocation_info_, to_space_); |
| 1486 | 995 |
| 1487 // There should be objects packed in from the low address up to the | 996 // There should be objects packed in from the low address up to the |
| 1488 // allocation pointer. | 997 // allocation pointer. |
| 1489 Address current = to_space_.low(); | 998 Address current = to_space_.low(); |
| (...skipping 633 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2123 cur_addr = cur_node->next(); | 1632 cur_addr = cur_node->next(); |
| 2124 cur_node->SetMark(); | 1633 cur_node->SetMark(); |
| 2125 } | 1634 } |
| 2126 } | 1635 } |
| 2127 | 1636 |
| 2128 | 1637 |
| 2129 // ----------------------------------------------------------------------------- | 1638 // ----------------------------------------------------------------------------- |
| 2130 // OldSpace implementation | 1639 // OldSpace implementation |
| 2131 | 1640 |
| 2132 void OldSpace::PrepareForMarkCompact(bool will_compact) { | 1641 void OldSpace::PrepareForMarkCompact(bool will_compact) { |
| 1642 ASSERT(!will_compact); | |
| 2133 // Call prepare of the super class. | 1643 // Call prepare of the super class. |
| 2134 PagedSpace::PrepareForMarkCompact(will_compact); | 1644 PagedSpace::PrepareForMarkCompact(will_compact); |
| 2135 | 1645 |
| 2136 if (will_compact) { | 1646 // During a non-compacting collection, everything below the linear |
| 2137 // Reset relocation info. During a compacting collection, everything in | 1647 // allocation pointer is considered allocated (everything above is |
| 2138 // the space is considered 'available' and we will rediscover live data | 1648 // available) and we will rediscover available and wasted bytes during |
| 2139 // and waste during the collection. | 1649 // the collection. |
| 2140 MCResetRelocationInfo(); | 1650 accounting_stats_.AllocateBytes(free_list_.available()); |
| 2141 ASSERT(Available() == Capacity()); | 1651 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 | 1652 |
| 2151 // Clear the free list before a full GC---it will be rebuilt afterward. | 1653 // Clear the free list before a full GC---it will be rebuilt afterward. |
| 2152 free_list_.Reset(); | 1654 free_list_.Reset(); |
| 2153 } | 1655 } |
| 2154 | 1656 |
| 2155 | 1657 |
| 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) { | 1658 bool NewSpace::ReserveSpace(int bytes) { |
| 2196 // We can't reliably unpack a partial snapshot that needs more new space | 1659 // We can't reliably unpack a partial snapshot that needs more new space |
| 2197 // space than the minimum NewSpace size. | 1660 // space than the minimum NewSpace size. |
| 2198 ASSERT(bytes <= InitialCapacity()); | 1661 ASSERT(bytes <= InitialCapacity()); |
| 2199 Address limit = allocation_info_.limit; | 1662 Address limit = allocation_info_.limit; |
| 2200 Address top = allocation_info_.top; | 1663 Address top = allocation_info_.top; |
| 2201 return limit - top >= bytes; | 1664 return limit - top >= bytes; |
| 2202 } | 1665 } |
| 2203 | 1666 |
| 2204 | 1667 |
| 2205 void PagedSpace::FreePages(Page* prev, Page* last) { | 1668 void PagedSpace::FreePages(Page* prev, Page* last) { |
| 2206 if (last == AllocationTopPage()) { | 1669 if (last == AllocationTopPage()) { |
| 2207 // Pages are already at the end of used pages. | 1670 // Pages are already at the end of used pages. |
| 2208 return; | 1671 return; |
| 2209 } | 1672 } |
| 2210 | 1673 |
| 2211 Page* first = NULL; | 1674 Page* first = NULL; |
| 2212 | 1675 |
| 2213 // Remove pages from the list. | 1676 // Remove pages from the list. |
| 2214 if (prev == NULL) { | 1677 if (prev == NULL) { |
| 2215 first = first_page_; | 1678 first = first_page_; |
| 2216 first_page_ = last->next_page(); | 1679 first_page_ = last->next_page(); |
| 2217 } else { | 1680 } else { |
| 2218 first = prev->next_page(); | 1681 first = prev->next_page(); |
| 2219 MemoryAllocator::SetNextPage(prev, last->next_page()); | 1682 prev->set_next_page(last->next_page()); |
| 2220 } | 1683 } |
| 2221 | 1684 |
| 2222 // Attach it after the last page. | 1685 // Attach it after the last page. |
| 2223 MemoryAllocator::SetNextPage(last_page_, first); | 1686 last_page_->set_next_page(first); |
| 2224 last_page_ = last; | 1687 last_page_ = last; |
| 2225 MemoryAllocator::SetNextPage(last, NULL); | 1688 last->set_next_page(NULL); |
| 2226 | 1689 |
| 2227 // Clean them up. | 1690 // Clean them up. |
| 2228 do { | 1691 do { |
| 2229 first->InvalidateWatermark(true); | 1692 first->InvalidateWatermark(true); |
| 2230 first->SetAllocationWatermark(first->ObjectAreaStart()); | 1693 first->SetAllocationWatermark(first->ObjectAreaStart()); |
| 2231 first->SetCachedAllocationWatermark(first->ObjectAreaStart()); | 1694 first->SetCachedAllocationWatermark(first->ObjectAreaStart()); |
| 2232 first->SetRegionMarks(Page::kAllRegionsCleanMarks); | 1695 first->SetRegionMarks(Page::kAllRegionsCleanMarks); |
| 2233 first = first->next_page(); | 1696 first = first->next_page(); |
| 2234 } while (first != NULL); | 1697 } 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 } | 1698 } |
| 2318 | 1699 |
| 2319 | 1700 |
| 2320 void PagedSpace::PrepareForMarkCompact(bool will_compact) { | 1701 void PagedSpace::PrepareForMarkCompact(bool will_compact) { |
| 2321 if (will_compact) { | 1702 ASSERT(!will_compact); |
| 2322 RelinkPageListInChunkOrder(false); | |
| 2323 } | |
| 2324 } | 1703 } |
| 2325 | 1704 |
| 2326 | 1705 |
| 2327 bool PagedSpace::ReserveSpace(int bytes) { | 1706 bool PagedSpace::ReserveSpace(int bytes) { |
| 2328 Address limit = allocation_info_.limit; | 1707 Address limit = allocation_info_.limit; |
| 2329 Address top = allocation_info_.top; | 1708 Address top = allocation_info_.top; |
| 2330 if (limit - top >= bytes) return true; | 1709 if (limit - top >= bytes) return true; |
| 2331 | 1710 |
| 2332 // There wasn't enough space in the current page. Lets put the rest | 1711 // 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. | 1712 // of the page on the free list and start a fresh page. |
| 2334 PutRestOfCurrentPageOnFreeList(TopPageOf(allocation_info_)); | 1713 PutRestOfCurrentPageOnFreeList(TopPageOf(allocation_info_)); |
| 2335 | 1714 |
| 2336 Page* reserved_page = TopPageOf(allocation_info_); | 1715 Page* reserved_page = TopPageOf(allocation_info_); |
| 2337 int bytes_left_to_reserve = bytes; | 1716 int bytes_left_to_reserve = bytes; |
| 2338 while (bytes_left_to_reserve > 0) { | 1717 while (bytes_left_to_reserve > 0) { |
| 2339 if (!reserved_page->next_page()->is_valid()) { | 1718 if (!reserved_page->next_page()->is_valid()) { |
| 2340 if (Heap::OldGenerationAllocationLimitReached()) return false; | 1719 if (Heap::OldGenerationAllocationLimitReached()) return false; |
| 2341 Expand(reserved_page); | 1720 Expand(); |
| 2342 } | 1721 } |
| 2343 bytes_left_to_reserve -= Page::kPageSize; | 1722 bytes_left_to_reserve -= Page::kPageSize; |
| 2344 reserved_page = reserved_page->next_page(); | 1723 reserved_page = reserved_page->next_page(); |
| 2345 if (!reserved_page->is_valid()) return false; | 1724 if (!reserved_page->is_valid()) return false; |
| 2346 } | 1725 } |
| 2347 ASSERT(TopPageOf(allocation_info_)->next_page()->is_valid()); | 1726 ASSERT(TopPageOf(allocation_info_)->next_page()->is_valid()); |
| 2348 TopPageOf(allocation_info_)->next_page()->InvalidateWatermark(true); | 1727 TopPageOf(allocation_info_)->next_page()->InvalidateWatermark(true); |
| 2349 SetAllocationInfo(&allocation_info_, | 1728 SetAllocationInfo(&allocation_info_, |
| 2350 TopPageOf(allocation_info_)->next_page()); | 1729 TopPageOf(allocation_info_)->next_page()); |
| 2351 return true; | 1730 return true; |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2400 | 1779 |
| 2401 // Free list allocation failed and there is no next page. Fail if we have | 1780 // 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 | 1781 // hit the old generation size limit that should cause a garbage |
| 2403 // collection. | 1782 // collection. |
| 2404 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) { | 1783 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) { |
| 2405 return NULL; | 1784 return NULL; |
| 2406 } | 1785 } |
| 2407 | 1786 |
| 2408 // Try to expand the space and allocate in the new next page. | 1787 // Try to expand the space and allocate in the new next page. |
| 2409 ASSERT(!current_page->next_page()->is_valid()); | 1788 ASSERT(!current_page->next_page()->is_valid()); |
| 2410 if (Expand(current_page)) { | 1789 if (Expand()) { |
| 2411 return AllocateInNextPage(current_page, size_in_bytes); | 1790 return AllocateInNextPage(current_page, size_in_bytes); |
| 2412 } | 1791 } |
| 2413 | 1792 |
| 2414 // Finally, fail. | 1793 // Finally, fail. |
| 2415 return NULL; | 1794 return NULL; |
| 2416 } | 1795 } |
| 2417 | 1796 |
| 2418 | 1797 |
| 2419 void OldSpace::PutRestOfCurrentPageOnFreeList(Page* current_page) { | 1798 void OldSpace::PutRestOfCurrentPageOnFreeList(Page* current_page) { |
| 2420 current_page->SetAllocationWatermark(allocation_info_.top); | 1799 current_page->SetAllocationWatermark(allocation_info_.top); |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 2611 } | 1990 } |
| 2612 #endif | 1991 #endif |
| 2613 | 1992 |
| 2614 // ----------------------------------------------------------------------------- | 1993 // ----------------------------------------------------------------------------- |
| 2615 // FixedSpace implementation | 1994 // FixedSpace implementation |
| 2616 | 1995 |
| 2617 void FixedSpace::PrepareForMarkCompact(bool will_compact) { | 1996 void FixedSpace::PrepareForMarkCompact(bool will_compact) { |
| 2618 // Call prepare of the super class. | 1997 // Call prepare of the super class. |
| 2619 PagedSpace::PrepareForMarkCompact(will_compact); | 1998 PagedSpace::PrepareForMarkCompact(will_compact); |
| 2620 | 1999 |
| 2621 if (will_compact) { | 2000 ASSERT(!will_compact); |
| 2622 // Reset relocation info. | |
| 2623 MCResetRelocationInfo(); | |
| 2624 | 2001 |
| 2625 // During a compacting collection, everything in the space is considered | 2002 // During a non-compacting collection, everything below the linear |
| 2626 // 'available' (set by the call to MCResetRelocationInfo) and we will | 2003 // allocation pointer except wasted top-of-page blocks is considered |
| 2627 // rediscover live and wasted bytes during the collection. | 2004 // allocated and we will rediscover available bytes during the |
| 2628 ASSERT(Available() == Capacity()); | 2005 // collection. |
| 2629 } else { | 2006 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 | 2007 |
| 2637 // Clear the free list before a full GC---it will be rebuilt afterward. | 2008 // Clear the free list before a full GC---it will be rebuilt afterward. |
| 2638 free_list_.Reset(); | 2009 free_list_.Reset(); |
| 2639 } | 2010 } |
| 2640 | 2011 |
| 2641 | 2012 |
| 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 | 2013 // 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 | 2014 // page in the space, (2) allocate off the space's free list, (3) expand the |
| 2673 // space, (4) fail. | 2015 // space, (4) fail. |
| 2674 HeapObject* FixedSpace::SlowAllocateRaw(int size_in_bytes) { | 2016 HeapObject* FixedSpace::SlowAllocateRaw(int size_in_bytes) { |
| 2675 ASSERT_EQ(object_size_in_bytes_, size_in_bytes); | 2017 ASSERT_EQ(object_size_in_bytes_, size_in_bytes); |
| 2676 // Linear allocation in this space has failed. If there is another page | 2018 // 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 | 2019 // in the space, move to that page and allocate there. This allocation |
| 2678 // should succeed. | 2020 // should succeed. |
| 2679 Page* current_page = TopPageOf(allocation_info_); | 2021 Page* current_page = TopPageOf(allocation_info_); |
| 2680 if (current_page->next_page()->is_valid()) { | 2022 if (current_page->next_page()->is_valid()) { |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 2707 | 2049 |
| 2708 // Free list allocation failed and there is no next page. Fail if we have | 2050 // 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 | 2051 // hit the old generation size limit that should cause a garbage |
| 2710 // collection. | 2052 // collection. |
| 2711 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) { | 2053 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) { |
| 2712 return NULL; | 2054 return NULL; |
| 2713 } | 2055 } |
| 2714 | 2056 |
| 2715 // Try to expand the space and allocate in the new next page. | 2057 // Try to expand the space and allocate in the new next page. |
| 2716 ASSERT(!current_page->next_page()->is_valid()); | 2058 ASSERT(!current_page->next_page()->is_valid()); |
| 2717 if (Expand(current_page)) { | 2059 if (Expand()) { |
| 2718 return AllocateInNextPage(current_page, size_in_bytes); | 2060 return AllocateInNextPage(current_page, size_in_bytes); |
| 2719 } | 2061 } |
| 2720 | 2062 |
| 2721 // Finally, fail. | 2063 // Finally, fail. |
| 2722 return NULL; | 2064 return NULL; |
| 2723 } | 2065 } |
| 2724 | 2066 |
| 2725 | 2067 |
| 2726 // Move to the next page (there is assumed to be one) and allocate there. | 2068 // 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 | 2069 // 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 Loading... | |
| 2798 ASSERT(object->IsJSGlobalPropertyCell() || | 2140 ASSERT(object->IsJSGlobalPropertyCell() || |
| 2799 object->map() == Heap::two_pointer_filler_map()); | 2141 object->map() == Heap::two_pointer_filler_map()); |
| 2800 } | 2142 } |
| 2801 #endif | 2143 #endif |
| 2802 | 2144 |
| 2803 | 2145 |
| 2804 // ----------------------------------------------------------------------------- | 2146 // ----------------------------------------------------------------------------- |
| 2805 // LargeObjectIterator | 2147 // LargeObjectIterator |
| 2806 | 2148 |
| 2807 LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space) { | 2149 LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space) { |
| 2808 current_ = space->first_chunk_; | 2150 current_ = space->first_page_; |
| 2809 size_func_ = NULL; | 2151 size_func_ = NULL; |
| 2810 } | 2152 } |
| 2811 | 2153 |
| 2812 | 2154 |
| 2813 LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space, | 2155 LargeObjectIterator::LargeObjectIterator(LargeObjectSpace* space, |
| 2814 HeapObjectCallback size_func) { | 2156 HeapObjectCallback size_func) { |
| 2815 current_ = space->first_chunk_; | 2157 current_ = space->first_page_; |
| 2816 size_func_ = size_func; | 2158 size_func_ = size_func; |
| 2817 } | 2159 } |
| 2818 | 2160 |
| 2819 | 2161 |
| 2820 HeapObject* LargeObjectIterator::next() { | 2162 HeapObject* LargeObjectIterator::next() { |
| 2821 if (current_ == NULL) return NULL; | 2163 if (current_ == NULL) return NULL; |
| 2822 | 2164 |
| 2823 HeapObject* object = current_->GetObject(); | 2165 HeapObject* object = current_->GetObject(); |
| 2824 current_ = current_->next(); | 2166 current_ = current_->next_page(); |
| 2825 return object; | 2167 return object; |
| 2826 } | 2168 } |
| 2827 | 2169 |
| 2828 | 2170 |
| 2829 // ----------------------------------------------------------------------------- | 2171 // ----------------------------------------------------------------------------- |
| 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 | 2172 // LargeObjectSpace |
| 2872 | 2173 |
| 2873 LargeObjectSpace::LargeObjectSpace(AllocationSpace id) | 2174 LargeObjectSpace::LargeObjectSpace(AllocationSpace id) |
| 2874 : Space(id, NOT_EXECUTABLE), // Managed on a per-allocation basis | 2175 : Space(id, NOT_EXECUTABLE), // Managed on a per-allocation basis |
| 2875 first_chunk_(NULL), | 2176 first_page_(NULL), |
| 2876 size_(0), | 2177 size_(0), |
| 2877 page_count_(0), | 2178 page_count_(0), |
| 2878 objects_size_(0) {} | 2179 objects_size_(0) {} |
| 2879 | 2180 |
| 2880 | 2181 |
| 2881 bool LargeObjectSpace::Setup() { | 2182 bool LargeObjectSpace::Setup() { |
| 2882 first_chunk_ = NULL; | 2183 first_page_ = NULL; |
| 2883 size_ = 0; | 2184 size_ = 0; |
| 2884 page_count_ = 0; | 2185 page_count_ = 0; |
| 2885 objects_size_ = 0; | 2186 objects_size_ = 0; |
| 2886 return true; | 2187 return true; |
| 2887 } | 2188 } |
| 2888 | 2189 |
| 2889 | 2190 |
| 2890 void LargeObjectSpace::TearDown() { | 2191 void LargeObjectSpace::TearDown() { |
| 2891 while (first_chunk_ != NULL) { | 2192 while (first_page_ != NULL) { |
| 2892 LargeObjectChunk* chunk = first_chunk_; | 2193 LargePage* page = first_page_; |
| 2893 first_chunk_ = first_chunk_->next(); | 2194 first_page_ = first_page_->next_page(); |
| 2894 LOG(DeleteEvent("LargeObjectChunk", chunk->address())); | 2195 |
| 2895 Page* page = Page::FromAddress(RoundUp(chunk->address(), Page::kPageSize)); | 2196 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 } | 2197 } |
| 2905 | 2198 |
| 2906 size_ = 0; | 2199 size_ = 0; |
| 2907 page_count_ = 0; | 2200 page_count_ = 0; |
| 2908 objects_size_ = 0; | 2201 objects_size_ = 0; |
| 2909 } | 2202 } |
| 2910 | 2203 |
| 2911 | 2204 |
| 2912 #ifdef ENABLE_HEAP_PROTECTION | 2205 #ifdef ENABLE_HEAP_PROTECTION |
| 2913 | 2206 |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 2925 while (chunk != NULL) { | 2218 while (chunk != NULL) { |
| 2926 bool is_code = chunk->GetObject()->IsCode(); | 2219 bool is_code = chunk->GetObject()->IsCode(); |
| 2927 MemoryAllocator::Unprotect(chunk->address(), chunk->size(), | 2220 MemoryAllocator::Unprotect(chunk->address(), chunk->size(), |
| 2928 is_code ? EXECUTABLE : NOT_EXECUTABLE); | 2221 is_code ? EXECUTABLE : NOT_EXECUTABLE); |
| 2929 chunk = chunk->next(); | 2222 chunk = chunk->next(); |
| 2930 } | 2223 } |
| 2931 } | 2224 } |
| 2932 | 2225 |
| 2933 #endif | 2226 #endif |
| 2934 | 2227 |
| 2935 | 2228 MaybeObject* LargeObjectSpace::AllocateRawInternal(int object_size, |
| 2936 MaybeObject* LargeObjectSpace::AllocateRawInternal(int requested_size, | |
| 2937 int object_size, | |
| 2938 Executability executable) { | 2229 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. | 2230 // Check if we want to force a GC before growing the old space further. |
| 2942 // If so, fail the allocation. | 2231 // If so, fail the allocation. |
| 2943 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) { | 2232 if (!Heap::always_allocate() && Heap::OldGenerationAllocationLimitReached()) { |
| 2944 return Failure::RetryAfterGC(identity()); | 2233 return Failure::RetryAfterGC(identity()); |
| 2945 } | 2234 } |
| 2946 | 2235 |
| 2947 LargeObjectChunk* chunk = LargeObjectChunk::New(requested_size, executable); | 2236 LargePage* page = MemoryAllocator::AllocateLargePage(object_size, |
| 2948 if (chunk == NULL) { | 2237 executable, |
| 2949 return Failure::RetryAfterGC(identity()); | 2238 this); |
| 2950 } | 2239 if (page == NULL) return Failure::RetryAfterGC(identity()); |
| 2240 ASSERT(page->body_size() >= object_size); | |
| 2951 | 2241 |
| 2952 size_ += static_cast<int>(chunk->size()); | 2242 size_ += static_cast<int>(page->size()); |
| 2953 objects_size_ += requested_size; | 2243 objects_size_ += object_size; |
| 2954 page_count_++; | 2244 page_count_++; |
| 2955 chunk->set_next(first_chunk_); | 2245 page->set_next_page(first_page_); |
| 2956 first_chunk_ = chunk; | 2246 first_page_ = page; |
| 2957 | 2247 |
| 2958 // Initialize page header. | 2248 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 } | 2249 } |
| 2970 | 2250 |
| 2971 | 2251 |
| 2972 MaybeObject* LargeObjectSpace::AllocateRawCode(int size_in_bytes) { | 2252 MaybeObject* LargeObjectSpace::AllocateRawCode(int size_in_bytes) { |
| 2973 ASSERT(0 < size_in_bytes); | 2253 ASSERT(0 < size_in_bytes); |
| 2974 return AllocateRawInternal(size_in_bytes, | 2254 return AllocateRawInternal(size_in_bytes, EXECUTABLE); |
| 2975 size_in_bytes, | |
| 2976 EXECUTABLE); | |
| 2977 } | 2255 } |
| 2978 | 2256 |
| 2979 | 2257 |
| 2980 MaybeObject* LargeObjectSpace::AllocateRawFixedArray(int size_in_bytes) { | 2258 MaybeObject* LargeObjectSpace::AllocateRawFixedArray(int size_in_bytes) { |
| 2981 ASSERT(0 < size_in_bytes); | 2259 ASSERT(0 < size_in_bytes); |
| 2982 return AllocateRawInternal(size_in_bytes, | 2260 return AllocateRawInternal(size_in_bytes, NOT_EXECUTABLE); |
| 2983 size_in_bytes, | |
| 2984 NOT_EXECUTABLE); | |
| 2985 } | 2261 } |
| 2986 | 2262 |
| 2987 | 2263 |
| 2988 MaybeObject* LargeObjectSpace::AllocateRaw(int size_in_bytes) { | 2264 MaybeObject* LargeObjectSpace::AllocateRaw(int size_in_bytes) { |
| 2989 ASSERT(0 < size_in_bytes); | 2265 ASSERT(0 < size_in_bytes); |
| 2990 return AllocateRawInternal(size_in_bytes, | 2266 return AllocateRawInternal(size_in_bytes, NOT_EXECUTABLE); |
| 2991 size_in_bytes, | |
| 2992 NOT_EXECUTABLE); | |
| 2993 } | 2267 } |
| 2994 | 2268 |
| 2995 | 2269 |
| 2996 // GC support | 2270 // GC support |
| 2997 MaybeObject* LargeObjectSpace::FindObject(Address a) { | 2271 MaybeObject* LargeObjectSpace::FindObject(Address a) { |
| 2998 for (LargeObjectChunk* chunk = first_chunk_; | 2272 for (LargePage* page = first_page_; |
| 2999 chunk != NULL; | 2273 page != NULL; |
| 3000 chunk = chunk->next()) { | 2274 page = page->next_page()) { |
| 3001 Address chunk_address = chunk->address(); | 2275 Address page_address = page->address(); |
| 3002 if (chunk_address <= a && a < chunk_address + chunk->size()) { | 2276 if (page_address <= a && a < page_address + page->size()) { |
| 3003 return chunk->GetObject(); | 2277 return page->GetObject(); |
| 3004 } | 2278 } |
| 3005 } | 2279 } |
| 3006 return Failure::Exception(); | 2280 return Failure::Exception(); |
| 3007 } | 2281 } |
| 3008 | 2282 |
| 3009 | 2283 |
| 3010 LargeObjectChunk* LargeObjectSpace::FindChunkContainingPc(Address pc) { | 2284 LargePage* LargeObjectSpace::FindPageContainingPc(Address pc) { |
| 3011 // TODO(853): Change this implementation to only find executable | 2285 // TODO(853): Change this implementation to only find executable |
| 3012 // chunks and use some kind of hash-based approach to speed it up. | 2286 // chunks and use some kind of hash-based approach to speed it up. |
| 3013 for (LargeObjectChunk* chunk = first_chunk_; | 2287 for (LargePage* chunk = first_page_; |
| 3014 chunk != NULL; | 2288 chunk != NULL; |
| 3015 chunk = chunk->next()) { | 2289 chunk = chunk->next_page()) { |
| 3016 Address chunk_address = chunk->address(); | 2290 Address chunk_address = chunk->address(); |
| 3017 if (chunk_address <= pc && pc < chunk_address + chunk->size()) { | 2291 if (chunk_address <= pc && pc < chunk_address + chunk->size()) { |
| 3018 return chunk; | 2292 return chunk; |
| 3019 } | 2293 } |
| 3020 } | 2294 } |
| 3021 return NULL; | 2295 return NULL; |
| 3022 } | 2296 } |
| 3023 | 2297 |
| 3024 | 2298 |
| 3025 void LargeObjectSpace::IterateDirtyRegions(ObjectSlotCallback copy_object) { | 2299 void LargeObjectSpace::IterateDirtyRegions(ObjectSlotCallback copy_object) { |
| 3026 LargeObjectIterator it(this); | 2300 LargeObjectIterator it(this); |
| 3027 for (HeapObject* object = it.next(); object != NULL; object = it.next()) { | 2301 for (HeapObject* object = it.next(); object != NULL; object = it.next()) { |
| 3028 // We only have code, sequential strings, or fixed arrays in large | 2302 // We only have code, sequential strings, or fixed arrays in large |
| 3029 // object space, and only fixed arrays can possibly contain pointers to | 2303 // object space, and only fixed arrays can possibly contain pointers to |
| 3030 // the young generation. | 2304 // the young generation. |
| 3031 if (object->IsFixedArray()) { | 2305 if (object->IsFixedArray()) { |
| 3032 Page* page = Page::FromAddress(object->address()); | 2306 Page* page = Page::FromAddress(object->address()); |
| 3033 uint32_t marks = page->GetRegionMarks(); | 2307 uint32_t marks = page->GetRegionMarks(); |
| 3034 uint32_t newmarks = Page::kAllRegionsCleanMarks; | |
| 3035 | 2308 |
| 3036 if (marks != Page::kAllRegionsCleanMarks) { | 2309 // 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 | 2310 // page. |
| 3038 // regions (modulo 32). So we treat a large page as a sequence of | 2311 ASSERT(marks == Page::kAllRegionsDirtyMarks); |
| 3039 // normal pages of size Page::kPageSize having same dirty marks | 2312 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 | 2313 |
| 3045 // Iterate regions of the first normal page covering object. | 2314 Address start = object->address(); |
| 3046 uint32_t first_region_number = page->GetRegionNumberForAddress(start); | 2315 Address object_end = start + object->Size(); |
| 3047 newmarks |= | 2316 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 } | 2317 } |
| 3082 } | 2318 } |
| 3083 } | 2319 } |
| 3084 | 2320 |
| 3085 | 2321 |
| 3086 void LargeObjectSpace::FreeUnmarkedObjects() { | 2322 void LargeObjectSpace::FreeUnmarkedObjects() { |
| 3087 LargeObjectChunk* previous = NULL; | 2323 LargePage* previous = NULL; |
| 3088 LargeObjectChunk* current = first_chunk_; | 2324 LargePage* current = first_page_; |
| 3089 while (current != NULL) { | 2325 while (current != NULL) { |
| 3090 HeapObject* object = current->GetObject(); | 2326 HeapObject* object = current->GetObject(); |
| 3091 if (object->IsMarked()) { | 2327 if (object->IsMarked()) { |
| 3092 object->ClearMark(); | 2328 object->ClearMark(); |
| 3093 MarkCompactCollector::tracer()->decrement_marked_count(); | 2329 MarkCompactCollector::tracer()->decrement_marked_count(); |
| 3094 previous = current; | 2330 previous = current; |
| 3095 current = current->next(); | 2331 current = current->next_page(); |
| 3096 } else { | 2332 } else { |
| 3097 Page* page = Page::FromAddress(RoundUp(current->address(), | 2333 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. | 2334 // Cut the chunk out from the chunk list. |
| 3105 current = current->next(); | 2335 current = current->next_page(); |
| 3106 if (previous == NULL) { | 2336 if (previous == NULL) { |
| 3107 first_chunk_ = current; | 2337 first_page_ = current; |
| 3108 } else { | 2338 } else { |
| 3109 previous->set_next(current); | 2339 previous->set_next_page(current); |
| 3110 } | 2340 } |
| 3111 | 2341 |
| 3112 // Free the chunk. | 2342 // Free the chunk. |
| 3113 MarkCompactCollector::ReportDeleteIfNeeded(object); | 2343 MarkCompactCollector::ReportDeleteIfNeeded(object); |
| 3114 size_ -= static_cast<int>(chunk_size); | 2344 size_ -= static_cast<int>(page->size()); |
| 3115 objects_size_ -= object->Size(); | 2345 objects_size_ -= object->Size(); |
| 3116 page_count_--; | 2346 page_count_--; |
| 3117 ObjectSpace space = kObjectSpaceLoSpace; | 2347 |
| 3118 if (executable == EXECUTABLE) space = kObjectSpaceCodeSpace; | 2348 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 } | 2349 } |
| 3124 } | 2350 } |
| 3125 } | 2351 } |
| 3126 | 2352 |
| 3127 | 2353 |
| 3128 bool LargeObjectSpace::Contains(HeapObject* object) { | 2354 bool LargeObjectSpace::Contains(HeapObject* object) { |
| 3129 Address address = object->address(); | 2355 Address address = object->address(); |
| 3130 if (Heap::new_space()->Contains(address)) { | 2356 if (Heap::new_space()->Contains(address)) { |
| 3131 return false; | 2357 return false; |
| 3132 } | 2358 } |
| 3133 Page* page = Page::FromAddress(address); | 2359 MemoryChunk* chunk = MemoryChunk::FromAddress(address); |
| 3134 | 2360 |
| 3135 SLOW_ASSERT(!page->IsLargeObjectPage() | 2361 bool owned = chunk->owner() == this; |
| 2362 | |
| 2363 SLOW_ASSERT(!owned | |
| 3136 || !FindObject(address)->IsFailure()); | 2364 || !FindObject(address)->IsFailure()); |
| 3137 | 2365 |
| 3138 return page->IsLargeObjectPage(); | 2366 return owned; |
| 3139 } | 2367 } |
| 3140 | 2368 |
| 3141 | 2369 |
| 3142 #ifdef DEBUG | 2370 #ifdef DEBUG |
| 3143 // We do not assume that the large object iterator works, because it depends | 2371 // We do not assume that the large object iterator works, because it depends |
| 3144 // on the invariants we are checking during verification. | 2372 // on the invariants we are checking during verification. |
| 3145 void LargeObjectSpace::Verify() { | 2373 void LargeObjectSpace::Verify() { |
| 3146 for (LargeObjectChunk* chunk = first_chunk_; | 2374 for (LargePage* chunk = first_page_; |
| 3147 chunk != NULL; | 2375 chunk != NULL; |
| 3148 chunk = chunk->next()) { | 2376 chunk = chunk->next_page()) { |
| 3149 // Each chunk contains an object that starts at the large object page's | 2377 // Each chunk contains an object that starts at the large object page's |
| 3150 // object area start. | 2378 // object area start. |
| 3151 HeapObject* object = chunk->GetObject(); | 2379 HeapObject* object = chunk->GetObject(); |
| 3152 Page* page = Page::FromAddress(object->address()); | 2380 Page* page = Page::FromAddress(object->address()); |
| 3153 ASSERT(object->address() == page->ObjectAreaStart()); | 2381 ASSERT(object->address() == page->ObjectAreaStart()); |
| 3154 | 2382 |
| 3155 // The first word should be a map, and we expect all map pointers to be | 2383 // The first word should be a map, and we expect all map pointers to be |
| 3156 // in map space. | 2384 // in map space. |
| 3157 Map* map = object->map(); | 2385 Map* map = object->map(); |
| 3158 ASSERT(map->IsMap()); | 2386 ASSERT(map->IsMap()); |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 3228 for (HeapObject* obj = obj_it.next(); obj != NULL; obj = obj_it.next()) { | 2456 for (HeapObject* obj = obj_it.next(); obj != NULL; obj = obj_it.next()) { |
| 3229 if (obj->IsCode()) { | 2457 if (obj->IsCode()) { |
| 3230 Code* code = Code::cast(obj); | 2458 Code* code = Code::cast(obj); |
| 3231 code_kind_statistics[code->kind()] += code->Size(); | 2459 code_kind_statistics[code->kind()] += code->Size(); |
| 3232 } | 2460 } |
| 3233 } | 2461 } |
| 3234 } | 2462 } |
| 3235 #endif // DEBUG | 2463 #endif // DEBUG |
| 3236 | 2464 |
| 3237 } } // namespace v8::internal | 2465 } } // namespace v8::internal |
| OLD | NEW |