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