| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2013 Google Inc. All rights reserved. | |
| 3 * | |
| 4 * Redistribution and use in source and binary forms, with or without | |
| 5 * modification, are permitted provided that the following conditions are | |
| 6 * met: | |
| 7 * | |
| 8 * * Redistributions of source code must retain the above copyright | |
| 9 * notice, this list of conditions and the following disclaimer. | |
| 10 * * Redistributions in binary form must reproduce the above | |
| 11 * copyright notice, this list of conditions and the following disclaimer | |
| 12 * in the documentation and/or other materials provided with the | |
| 13 * distribution. | |
| 14 * * Neither the name of Google Inc. nor the names of its | |
| 15 * contributors may be used to endorse or promote products derived from | |
| 16 * this software without specific prior written permission. | |
| 17 * | |
| 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 29 */ | |
| 30 | |
| 31 #include "config.h" | |
| 32 #include "platform/heap/Heap.h" | |
| 33 | |
| 34 #include "platform/ScriptForbiddenScope.h" | |
| 35 #include "platform/TraceEvent.h" | |
| 36 #include "platform/heap/ThreadState.h" | |
| 37 #include "public/platform/Platform.h" | |
| 38 #include "wtf/AddressSpaceRandomization.h" | |
| 39 #include "wtf/Assertions.h" | |
| 40 #include "wtf/LeakAnnotations.h" | |
| 41 #include "wtf/PassOwnPtr.h" | |
| 42 #if ENABLE(GC_PROFILE_MARKING) | |
| 43 #include "wtf/HashMap.h" | |
| 44 #include "wtf/HashSet.h" | |
| 45 #include "wtf/text/StringBuilder.h" | |
| 46 #include "wtf/text/StringHash.h" | |
| 47 #include <stdio.h> | |
| 48 #include <utility> | |
| 49 #endif | |
| 50 #if ENABLE(GC_PROFILE_HEAP) | |
| 51 #include "platform/TracedValue.h" | |
| 52 #endif | |
| 53 | |
| 54 #include <sys/mman.h> | |
| 55 #include <unistd.h> | |
| 56 | |
| 57 namespace blink { | |
| 58 | |
| 59 #if ENABLE(GC_PROFILE_MARKING) | |
| 60 static String classOf(const void* object) | |
| 61 { | |
| 62 const GCInfo* gcInfo = Heap::findGCInfo(reinterpret_cast<Address>(const_cast
<void*>(object))); | |
| 63 if (gcInfo) | |
| 64 return gcInfo->m_className; | |
| 65 | |
| 66 return "unknown"; | |
| 67 } | |
| 68 #endif | |
| 69 | |
| 70 static bool vTableInitialized(void* objectPointer) | |
| 71 { | |
| 72 return !!(*reinterpret_cast<Address*>(objectPointer)); | |
| 73 } | |
| 74 | |
| 75 static Address roundToBlinkPageBoundary(void* base) | |
| 76 { | |
| 77 return reinterpret_cast<Address>((reinterpret_cast<uintptr_t>(base) + blinkP
ageOffsetMask) & blinkPageBaseMask); | |
| 78 } | |
| 79 | |
| 80 static size_t roundToOsPageSize(size_t size) | |
| 81 { | |
| 82 return (size + osPageSize() - 1) & ~(osPageSize() - 1); | |
| 83 } | |
| 84 | |
| 85 size_t osPageSize() | |
| 86 { | |
| 87 #if OS(POSIX) | |
| 88 static const size_t pageSize = getpagesize(); | |
| 89 #else | |
| 90 static size_t pageSize = 0; | |
| 91 if (!pageSize) { | |
| 92 SYSTEM_INFO info; | |
| 93 GetSystemInfo(&info); | |
| 94 pageSize = info.dwPageSize; | |
| 95 ASSERT(IsPowerOf2(pageSize)); | |
| 96 } | |
| 97 #endif | |
| 98 return pageSize; | |
| 99 } | |
| 100 | |
| 101 class MemoryRegion { | |
| 102 public: | |
| 103 MemoryRegion(Address base, size_t size) | |
| 104 : m_base(base) | |
| 105 , m_size(size) | |
| 106 { | |
| 107 ASSERT(size > 0); | |
| 108 } | |
| 109 | |
| 110 bool contains(Address addr) const | |
| 111 { | |
| 112 return m_base <= addr && addr < (m_base + m_size); | |
| 113 } | |
| 114 | |
| 115 | |
| 116 bool contains(const MemoryRegion& other) const | |
| 117 { | |
| 118 return contains(other.m_base) && contains(other.m_base + other.m_size -
1); | |
| 119 } | |
| 120 | |
| 121 void release() | |
| 122 { | |
| 123 #if OS(POSIX) | |
| 124 int err = munmap(m_base, m_size); | |
| 125 RELEASE_ASSERT(!err); | |
| 126 #else | |
| 127 bool success = VirtualFree(m_base, 0, MEM_RELEASE); | |
| 128 RELEASE_ASSERT(success); | |
| 129 #endif | |
| 130 } | |
| 131 | |
| 132 WARN_UNUSED_RETURN bool commit() | |
| 133 { | |
| 134 ASSERT(Heap::heapDoesNotContainCacheIsEmpty()); | |
| 135 #if OS(POSIX) | |
| 136 int err = mprotect(m_base, m_size, PROT_READ | PROT_WRITE); | |
| 137 if (!err) { | |
| 138 madvise(m_base, m_size, MADV_NORMAL); | |
| 139 return true; | |
| 140 } | |
| 141 return false; | |
| 142 #else | |
| 143 void* result = VirtualAlloc(m_base, m_size, MEM_COMMIT, PAGE_READWRITE); | |
| 144 return !!result; | |
| 145 #endif | |
| 146 } | |
| 147 | |
| 148 void decommit() | |
| 149 { | |
| 150 #if OS(POSIX) | |
| 151 int err = mprotect(m_base, m_size, PROT_NONE); | |
| 152 RELEASE_ASSERT(!err); | |
| 153 // FIXME: Consider using MADV_FREE on MacOS. | |
| 154 madvise(m_base, m_size, MADV_DONTNEED); | |
| 155 #else | |
| 156 bool success = VirtualFree(m_base, m_size, MEM_DECOMMIT); | |
| 157 RELEASE_ASSERT(success); | |
| 158 #endif | |
| 159 } | |
| 160 | |
| 161 Address base() const { return m_base; } | |
| 162 size_t size() const { return m_size; } | |
| 163 | |
| 164 private: | |
| 165 Address m_base; | |
| 166 size_t m_size; | |
| 167 }; | |
| 168 | |
| 169 // A PageMemoryRegion represents a chunk of reserved virtual address | |
| 170 // space containing a number of blink heap pages. On Windows, reserved | |
| 171 // virtual address space can only be given back to the system as a | |
| 172 // whole. The PageMemoryRegion allows us to do that by keeping track | |
| 173 // of the number of pages using it in order to be able to release all | |
| 174 // of the virtual address space when there are no more pages using it. | |
| 175 class PageMemoryRegion : public MemoryRegion { | |
| 176 public: | |
| 177 ~PageMemoryRegion() | |
| 178 { | |
| 179 release(); | |
| 180 } | |
| 181 | |
| 182 void pageRemoved() | |
| 183 { | |
| 184 if (!--m_numPages) | |
| 185 delete this; | |
| 186 } | |
| 187 | |
| 188 static PageMemoryRegion* allocate(size_t size, unsigned numPages) | |
| 189 { | |
| 190 ASSERT(Heap::heapDoesNotContainCacheIsEmpty()); | |
| 191 | |
| 192 // Compute a random blink page aligned address for the page memory | |
| 193 // region and attempt to get the memory there. | |
| 194 Address randomAddress = reinterpret_cast<Address>(WTF::getRandomPageBase
()); | |
| 195 Address alignedRandomAddress = roundToBlinkPageBoundary(randomAddress); | |
| 196 | |
| 197 #if OS(POSIX) | |
| 198 Address base = static_cast<Address>(mmap(alignedRandomAddress, size, PRO
T_NONE, MAP_ANON | MAP_PRIVATE, -1, 0)); | |
| 199 RELEASE_ASSERT(base != MAP_FAILED); | |
| 200 if (base == roundToBlinkPageBoundary(base)) | |
| 201 return new PageMemoryRegion(base, size, numPages); | |
| 202 | |
| 203 // We failed to get a blink page aligned chunk of | |
| 204 // memory. Unmap the chunk that we got and fall back to | |
| 205 // overallocating and selecting an aligned sub part of what | |
| 206 // we allocate. | |
| 207 int error = munmap(base, size); | |
| 208 RELEASE_ASSERT(!error); | |
| 209 size_t allocationSize = size + blinkPageSize; | |
| 210 base = static_cast<Address>(mmap(alignedRandomAddress, allocationSize, P
ROT_NONE, MAP_ANON | MAP_PRIVATE, -1, 0)); | |
| 211 RELEASE_ASSERT(base != MAP_FAILED); | |
| 212 | |
| 213 Address end = base + allocationSize; | |
| 214 Address alignedBase = roundToBlinkPageBoundary(base); | |
| 215 Address regionEnd = alignedBase + size; | |
| 216 | |
| 217 // If the allocated memory was not blink page aligned release | |
| 218 // the memory before the aligned address. | |
| 219 if (alignedBase != base) | |
| 220 MemoryRegion(base, alignedBase - base).release(); | |
| 221 | |
| 222 // Free the additional memory at the end of the page if any. | |
| 223 if (regionEnd < end) | |
| 224 MemoryRegion(regionEnd, end - regionEnd).release(); | |
| 225 | |
| 226 return new PageMemoryRegion(alignedBase, size, numPages); | |
| 227 #else | |
| 228 Address base = static_cast<Address>(VirtualAlloc(alignedRandomAddress, s
ize, MEM_RESERVE, PAGE_NOACCESS)); | |
| 229 if (base) { | |
| 230 ASSERT(base == alignedRandomAddress); | |
| 231 return new PageMemoryRegion(base, size, numPages); | |
| 232 } | |
| 233 | |
| 234 // We failed to get the random aligned address that we asked | |
| 235 // for. Fall back to overallocating. On Windows it is | |
| 236 // impossible to partially release a region of memory | |
| 237 // allocated by VirtualAlloc. To avoid wasting virtual address | |
| 238 // space we attempt to release a large region of memory | |
| 239 // returned as a whole and then allocate an aligned region | |
| 240 // inside this larger region. | |
| 241 size_t allocationSize = size + blinkPageSize; | |
| 242 for (int attempt = 0; attempt < 3; attempt++) { | |
| 243 base = static_cast<Address>(VirtualAlloc(0, allocationSize, MEM_RESE
RVE, PAGE_NOACCESS)); | |
| 244 RELEASE_ASSERT(base); | |
| 245 VirtualFree(base, 0, MEM_RELEASE); | |
| 246 | |
| 247 Address alignedBase = roundToBlinkPageBoundary(base); | |
| 248 base = static_cast<Address>(VirtualAlloc(alignedBase, size, MEM_RESE
RVE, PAGE_NOACCESS)); | |
| 249 if (base) { | |
| 250 ASSERT(base == alignedBase); | |
| 251 return new PageMemoryRegion(alignedBase, size, numPages); | |
| 252 } | |
| 253 } | |
| 254 | |
| 255 // We failed to avoid wasting virtual address space after | |
| 256 // several attempts. | |
| 257 base = static_cast<Address>(VirtualAlloc(0, allocationSize, MEM_RESERVE,
PAGE_NOACCESS)); | |
| 258 RELEASE_ASSERT(base); | |
| 259 | |
| 260 // FIXME: If base is by accident blink page size aligned | |
| 261 // here then we can create two pages out of reserved | |
| 262 // space. Do this. | |
| 263 Address alignedBase = roundToBlinkPageBoundary(base); | |
| 264 | |
| 265 return new PageMemoryRegion(alignedBase, size, numPages); | |
| 266 #endif | |
| 267 } | |
| 268 | |
| 269 private: | |
| 270 PageMemoryRegion(Address base, size_t size, unsigned numPages) | |
| 271 : MemoryRegion(base, size) | |
| 272 , m_numPages(numPages) | |
| 273 { | |
| 274 } | |
| 275 | |
| 276 unsigned m_numPages; | |
| 277 }; | |
| 278 | |
| 279 // Representation of the memory used for a Blink heap page. | |
| 280 // | |
| 281 // The representation keeps track of two memory regions: | |
| 282 // | |
| 283 // 1. The virtual memory reserved from the system in order to be able | |
| 284 // to free all the virtual memory reserved. Multiple PageMemory | |
| 285 // instances can share the same reserved memory region and | |
| 286 // therefore notify the reserved memory region on destruction so | |
| 287 // that the system memory can be given back when all PageMemory | |
| 288 // instances for that memory are gone. | |
| 289 // | |
| 290 // 2. The writable memory (a sub-region of the reserved virtual | |
| 291 // memory region) that is used for the actual heap page payload. | |
| 292 // | |
| 293 // Guard pages are created before and after the writable memory. | |
| 294 class PageMemory { | |
| 295 public: | |
| 296 ~PageMemory() | |
| 297 { | |
| 298 __lsan_unregister_root_region(m_writable.base(), m_writable.size()); | |
| 299 m_reserved->pageRemoved(); | |
| 300 } | |
| 301 | |
| 302 bool commit() WARN_UNUSED_RETURN { return m_writable.commit(); } | |
| 303 void decommit() { m_writable.decommit(); } | |
| 304 | |
| 305 Address writableStart() { return m_writable.base(); } | |
| 306 | |
| 307 static PageMemory* setupPageMemoryInRegion(PageMemoryRegion* region, size_t
pageOffset, size_t payloadSize) | |
| 308 { | |
| 309 // Setup the payload one OS page into the page memory. The | |
| 310 // first os page is the guard page. | |
| 311 Address payloadAddress = region->base() + pageOffset + osPageSize(); | |
| 312 return new PageMemory(region, MemoryRegion(payloadAddress, payloadSize))
; | |
| 313 } | |
| 314 | |
| 315 // Allocate a virtual address space for one blink page with the | |
| 316 // following layout: | |
| 317 // | |
| 318 // [ guard os page | ... payload ... | guard os page ] | |
| 319 // ^---{ aligned to blink page size } | |
| 320 // | |
| 321 static PageMemory* allocate(size_t payloadSize) | |
| 322 { | |
| 323 ASSERT(payloadSize > 0); | |
| 324 | |
| 325 // Virtual memory allocation routines operate in OS page sizes. | |
| 326 // Round up the requested size to nearest os page size. | |
| 327 payloadSize = roundToOsPageSize(payloadSize); | |
| 328 | |
| 329 // Overallocate by 2 times OS page size to have space for a | |
| 330 // guard page at the beginning and end of blink heap page. | |
| 331 size_t allocationSize = payloadSize + 2 * osPageSize(); | |
| 332 PageMemoryRegion* pageMemoryRegion = PageMemoryRegion::allocate(allocati
onSize, 1); | |
| 333 PageMemory* storage = setupPageMemoryInRegion(pageMemoryRegion, 0, paylo
adSize); | |
| 334 RELEASE_ASSERT(storage->commit()); | |
| 335 return storage; | |
| 336 } | |
| 337 | |
| 338 private: | |
| 339 PageMemory(PageMemoryRegion* reserved, const MemoryRegion& writable) | |
| 340 : m_reserved(reserved) | |
| 341 , m_writable(writable) | |
| 342 { | |
| 343 ASSERT(reserved->contains(writable)); | |
| 344 | |
| 345 // Register the writable area of the memory as part of the LSan root set
. | |
| 346 // Only the writable area is mapped and can contain C++ objects. Those | |
| 347 // C++ objects can contain pointers to objects outside of the heap and | |
| 348 // should therefore be part of the LSan root set. | |
| 349 __lsan_register_root_region(m_writable.base(), m_writable.size()); | |
| 350 } | |
| 351 | |
| 352 | |
| 353 PageMemoryRegion* m_reserved; | |
| 354 MemoryRegion m_writable; | |
| 355 }; | |
| 356 | |
| 357 NO_SANITIZE_ADDRESS | |
| 358 bool HeapObjectHeader::isMarked() const | |
| 359 { | |
| 360 checkHeader(); | |
| 361 unsigned size = acquireLoad(&m_size); | |
| 362 return size & markBitMask; | |
| 363 } | |
| 364 | |
| 365 NO_SANITIZE_ADDRESS | |
| 366 void HeapObjectHeader::unmark() | |
| 367 { | |
| 368 checkHeader(); | |
| 369 m_size &= ~markBitMask; | |
| 370 } | |
| 371 | |
| 372 NO_SANITIZE_ADDRESS | |
| 373 bool HeapObjectHeader::hasDeadMark() const | |
| 374 { | |
| 375 checkHeader(); | |
| 376 return m_size & deadBitMask; | |
| 377 } | |
| 378 | |
| 379 NO_SANITIZE_ADDRESS | |
| 380 void HeapObjectHeader::clearDeadMark() | |
| 381 { | |
| 382 checkHeader(); | |
| 383 m_size &= ~deadBitMask; | |
| 384 } | |
| 385 | |
| 386 NO_SANITIZE_ADDRESS | |
| 387 void HeapObjectHeader::setDeadMark() | |
| 388 { | |
| 389 ASSERT(!isMarked()); | |
| 390 checkHeader(); | |
| 391 m_size |= deadBitMask; | |
| 392 } | |
| 393 | |
| 394 #if ENABLE(ASSERT) | |
| 395 NO_SANITIZE_ADDRESS | |
| 396 void HeapObjectHeader::zapMagic() | |
| 397 { | |
| 398 m_magic = zappedMagic; | |
| 399 } | |
| 400 #endif | |
| 401 | |
| 402 HeapObjectHeader* HeapObjectHeader::fromPayload(const void* payload) | |
| 403 { | |
| 404 Address addr = reinterpret_cast<Address>(const_cast<void*>(payload)); | |
| 405 HeapObjectHeader* header = | |
| 406 reinterpret_cast<HeapObjectHeader*>(addr - objectHeaderSize); | |
| 407 return header; | |
| 408 } | |
| 409 | |
| 410 void HeapObjectHeader::finalize(const GCInfo* gcInfo, Address object, size_t obj
ectSize) | |
| 411 { | |
| 412 ASSERT(gcInfo); | |
| 413 if (gcInfo->hasFinalizer()) { | |
| 414 gcInfo->m_finalize(object); | |
| 415 } | |
| 416 | |
| 417 #if ENABLE(ASSERT) || defined(LEAK_SANITIZER) || defined(ADDRESS_SANITIZER) | |
| 418 // In Debug builds, memory is zapped when it's freed, and the zapped memory
is | |
| 419 // zeroed out when the memory is reused. Memory is also zapped when using Le
ak | |
| 420 // Sanitizer because the heap is used as a root region for LSan and therefor
e | |
| 421 // pointers in unreachable memory could hide leaks. | |
| 422 for (size_t i = 0; i < objectSize; i++) | |
| 423 object[i] = finalizedZapValue; | |
| 424 | |
| 425 // Zap the primary vTable entry (secondary vTable entries are not zapped). | |
| 426 *(reinterpret_cast<uintptr_t*>(object)) = zappedVTable; | |
| 427 #endif | |
| 428 // In Release builds, the entire object is zeroed out when it is added to th
e free list. | |
| 429 // This happens right after sweeping the page and before the thread commence
s execution. | |
| 430 } | |
| 431 | |
| 432 NO_SANITIZE_ADDRESS | |
| 433 void FinalizedHeapObjectHeader::finalize() | |
| 434 { | |
| 435 HeapObjectHeader::finalize(m_gcInfo, payload(), payloadSize()); | |
| 436 } | |
| 437 | |
| 438 template<typename Header> | |
| 439 void LargeHeapObject<Header>::unmark() | |
| 440 { | |
| 441 return heapObjectHeader()->unmark(); | |
| 442 } | |
| 443 | |
| 444 template<typename Header> | |
| 445 bool LargeHeapObject<Header>::isMarked() | |
| 446 { | |
| 447 return heapObjectHeader()->isMarked(); | |
| 448 } | |
| 449 | |
| 450 template<typename Header> | |
| 451 void LargeHeapObject<Header>::setDeadMark() | |
| 452 { | |
| 453 heapObjectHeader()->setDeadMark(); | |
| 454 } | |
| 455 | |
| 456 template<typename Header> | |
| 457 void LargeHeapObject<Header>::checkAndMarkPointer(Visitor* visitor, Address addr
ess) | |
| 458 { | |
| 459 ASSERT(contains(address)); | |
| 460 if (!objectContains(address) || heapObjectHeader()->hasDeadMark()) | |
| 461 return; | |
| 462 #if ENABLE(GC_PROFILE_MARKING) | |
| 463 visitor->setHostInfo(&address, "stack"); | |
| 464 #endif | |
| 465 mark(visitor); | |
| 466 } | |
| 467 | |
| 468 #if ENABLE(ASSERT) | |
| 469 static bool isUninitializedMemory(void* objectPointer, size_t objectSize) | |
| 470 { | |
| 471 // Scan through the object's fields and check that they are all zero. | |
| 472 Address* objectFields = reinterpret_cast<Address*>(objectPointer); | |
| 473 for (size_t i = 0; i < objectSize / sizeof(Address); ++i) { | |
| 474 if (objectFields[i] != 0) | |
| 475 return false; | |
| 476 } | |
| 477 return true; | |
| 478 } | |
| 479 #endif | |
| 480 | |
| 481 template<> | |
| 482 void LargeHeapObject<FinalizedHeapObjectHeader>::mark(Visitor* visitor) | |
| 483 { | |
| 484 if (heapObjectHeader()->hasVTable() && !vTableInitialized(payload())) { | |
| 485 FinalizedHeapObjectHeader* header = heapObjectHeader(); | |
| 486 visitor->markNoTracing(header); | |
| 487 ASSERT(isUninitializedMemory(header->payload(), header->payloadSize())); | |
| 488 } else { | |
| 489 visitor->mark(heapObjectHeader(), heapObjectHeader()->traceCallback()); | |
| 490 } | |
| 491 } | |
| 492 | |
| 493 template<> | |
| 494 void LargeHeapObject<HeapObjectHeader>::mark(Visitor* visitor) | |
| 495 { | |
| 496 ASSERT(gcInfo()); | |
| 497 if (gcInfo()->hasVTable() && !vTableInitialized(payload())) { | |
| 498 HeapObjectHeader* header = heapObjectHeader(); | |
| 499 visitor->markNoTracing(header); | |
| 500 ASSERT(isUninitializedMemory(header->payload(), header->payloadSize())); | |
| 501 } else { | |
| 502 visitor->mark(heapObjectHeader(), gcInfo()->m_trace); | |
| 503 } | |
| 504 } | |
| 505 | |
| 506 template<> | |
| 507 void LargeHeapObject<FinalizedHeapObjectHeader>::finalize() | |
| 508 { | |
| 509 heapObjectHeader()->finalize(); | |
| 510 } | |
| 511 | |
| 512 template<> | |
| 513 void LargeHeapObject<HeapObjectHeader>::finalize() | |
| 514 { | |
| 515 ASSERT(gcInfo()); | |
| 516 HeapObjectHeader::finalize(gcInfo(), payload(), payloadSize()); | |
| 517 } | |
| 518 | |
| 519 FinalizedHeapObjectHeader* FinalizedHeapObjectHeader::fromPayload(const void* pa
yload) | |
| 520 { | |
| 521 Address addr = reinterpret_cast<Address>(const_cast<void*>(payload)); | |
| 522 FinalizedHeapObjectHeader* header = | |
| 523 reinterpret_cast<FinalizedHeapObjectHeader*>(addr - finalizedHeaderSize)
; | |
| 524 return header; | |
| 525 } | |
| 526 | |
| 527 template<typename Header> | |
| 528 ThreadHeap<Header>::ThreadHeap(ThreadState* state, int index) | |
| 529 : m_currentAllocationPoint(0) | |
| 530 , m_remainingAllocationSize(0) | |
| 531 , m_firstPage(0) | |
| 532 , m_firstLargeHeapObject(0) | |
| 533 , m_firstPageAllocatedDuringSweeping(0) | |
| 534 , m_lastPageAllocatedDuringSweeping(0) | |
| 535 , m_mergePoint(0) | |
| 536 , m_biggestFreeListIndex(0) | |
| 537 , m_threadState(state) | |
| 538 , m_index(index) | |
| 539 , m_numberOfNormalPages(0) | |
| 540 , m_promptlyFreedCount(0) | |
| 541 { | |
| 542 clearFreeLists(); | |
| 543 } | |
| 544 | |
| 545 template<typename Header> | |
| 546 ThreadHeap<Header>::~ThreadHeap() | |
| 547 { | |
| 548 ASSERT(!m_firstPage); | |
| 549 ASSERT(!m_firstLargeHeapObject); | |
| 550 } | |
| 551 | |
| 552 template<typename Header> | |
| 553 void ThreadHeap<Header>::cleanupPages() | |
| 554 { | |
| 555 clearFreeLists(); | |
| 556 flushHeapContainsCache(); | |
| 557 | |
| 558 // Add the ThreadHeap's pages to the orphanedPagePool. | |
| 559 for (HeapPage<Header>* page = m_firstPage; page; page = page->m_next) | |
| 560 Heap::orphanedPagePool()->addOrphanedPage(m_index, page); | |
| 561 m_firstPage = 0; | |
| 562 | |
| 563 for (LargeHeapObject<Header>* largeObject = m_firstLargeHeapObject; largeObj
ect; largeObject = largeObject->m_next) | |
| 564 Heap::orphanedPagePool()->addOrphanedPage(m_index, largeObject); | |
| 565 m_firstLargeHeapObject = 0; | |
| 566 } | |
| 567 | |
| 568 template<typename Header> | |
| 569 Address ThreadHeap<Header>::outOfLineAllocate(size_t size, const GCInfo* gcInfo) | |
| 570 { | |
| 571 size_t allocationSize = allocationSizeFromSize(size); | |
| 572 if (threadState()->shouldGC()) { | |
| 573 if (threadState()->shouldForceConservativeGC()) | |
| 574 Heap::collectGarbage(ThreadState::HeapPointersOnStack); | |
| 575 else | |
| 576 threadState()->setGCRequested(); | |
| 577 } | |
| 578 ensureCurrentAllocation(allocationSize, gcInfo); | |
| 579 return allocate(size, gcInfo); | |
| 580 } | |
| 581 | |
| 582 template<typename Header> | |
| 583 bool ThreadHeap<Header>::allocateFromFreeList(size_t minSize) | |
| 584 { | |
| 585 size_t bucketSize = 1 << m_biggestFreeListIndex; | |
| 586 int i = m_biggestFreeListIndex; | |
| 587 for (; i > 0; i--, bucketSize >>= 1) { | |
| 588 if (bucketSize < minSize) | |
| 589 break; | |
| 590 FreeListEntry* entry = m_freeLists[i]; | |
| 591 if (entry) { | |
| 592 m_biggestFreeListIndex = i; | |
| 593 entry->unlink(&m_freeLists[i]); | |
| 594 setAllocationPoint(entry->address(), entry->size()); | |
| 595 ASSERT(currentAllocationPoint() && remainingAllocationSize() >= minS
ize); | |
| 596 return true; | |
| 597 } | |
| 598 } | |
| 599 m_biggestFreeListIndex = i; | |
| 600 return false; | |
| 601 } | |
| 602 | |
| 603 template<typename Header> | |
| 604 void ThreadHeap<Header>::ensureCurrentAllocation(size_t minSize, const GCInfo* g
cInfo) | |
| 605 { | |
| 606 ASSERT(minSize >= allocationGranularity); | |
| 607 if (remainingAllocationSize() >= minSize) | |
| 608 return; | |
| 609 | |
| 610 if (remainingAllocationSize() > 0) | |
| 611 addToFreeList(currentAllocationPoint(), remainingAllocationSize()); | |
| 612 if (allocateFromFreeList(minSize)) | |
| 613 return; | |
| 614 if (coalesce(minSize) && allocateFromFreeList(minSize)) | |
| 615 return; | |
| 616 addPageToHeap(gcInfo); | |
| 617 bool success = allocateFromFreeList(minSize); | |
| 618 RELEASE_ASSERT(success); | |
| 619 } | |
| 620 | |
| 621 template<typename Header> | |
| 622 BaseHeapPage* ThreadHeap<Header>::heapPageFromAddress(Address address) | |
| 623 { | |
| 624 for (HeapPage<Header>* page = m_firstPage; page; page = page->next()) { | |
| 625 if (page->contains(address)) | |
| 626 return page; | |
| 627 } | |
| 628 for (HeapPage<Header>* page = m_firstPageAllocatedDuringSweeping; page; page
= page->next()) { | |
| 629 if (page->contains(address)) | |
| 630 return page; | |
| 631 } | |
| 632 for (LargeHeapObject<Header>* current = m_firstLargeHeapObject; current; cur
rent = current->next()) { | |
| 633 // Check that large pages are blinkPageSize aligned (modulo the | |
| 634 // osPageSize for the guard page). | |
| 635 ASSERT(reinterpret_cast<Address>(current) - osPageSize() == roundToBlink
PageStart(reinterpret_cast<Address>(current))); | |
| 636 if (current->contains(address)) | |
| 637 return current; | |
| 638 } | |
| 639 return 0; | |
| 640 } | |
| 641 | |
| 642 #if ENABLE(GC_PROFILE_MARKING) | |
| 643 template<typename Header> | |
| 644 const GCInfo* ThreadHeap<Header>::findGCInfoOfLargeHeapObject(Address address) | |
| 645 { | |
| 646 for (LargeHeapObject<Header>* current = m_firstLargeHeapObject; current; cur
rent = current->next()) { | |
| 647 if (current->contains(address)) | |
| 648 return current->gcInfo(); | |
| 649 } | |
| 650 return 0; | |
| 651 } | |
| 652 #endif | |
| 653 | |
| 654 #if ENABLE(GC_PROFILE_HEAP) | |
| 655 #define GC_PROFILE_HEAP_PAGE_SNAPSHOT_THRESHOLD 0 | |
| 656 template<typename Header> | |
| 657 void ThreadHeap<Header>::snapshot(TracedValue* json, ThreadState::SnapshotInfo*
info) | |
| 658 { | |
| 659 size_t previousPageCount = info->pageCount; | |
| 660 | |
| 661 json->beginArray("pages"); | |
| 662 for (HeapPage<Header>* page = m_firstPage; page; page = page->next(), ++info
->pageCount) { | |
| 663 // FIXME: To limit the size of the snapshot we only output "threshold" m
any page snapshots. | |
| 664 if (info->pageCount < GC_PROFILE_HEAP_PAGE_SNAPSHOT_THRESHOLD) { | |
| 665 json->beginArray(); | |
| 666 json->pushInteger(reinterpret_cast<intptr_t>(page)); | |
| 667 page->snapshot(json, info); | |
| 668 json->endArray(); | |
| 669 } else { | |
| 670 page->snapshot(0, info); | |
| 671 } | |
| 672 } | |
| 673 json->endArray(); | |
| 674 | |
| 675 json->beginArray("largeObjects"); | |
| 676 for (LargeHeapObject<Header>* current = m_firstLargeHeapObject; current; cur
rent = current->next()) { | |
| 677 json->beginDictionary(); | |
| 678 current->snapshot(json, info); | |
| 679 json->endDictionary(); | |
| 680 } | |
| 681 json->endArray(); | |
| 682 | |
| 683 json->setInteger("pageCount", info->pageCount - previousPageCount); | |
| 684 } | |
| 685 #endif | |
| 686 | |
| 687 template<typename Header> | |
| 688 void ThreadHeap<Header>::addToFreeList(Address address, size_t size) | |
| 689 { | |
| 690 ASSERT(heapPageFromAddress(address)); | |
| 691 ASSERT(heapPageFromAddress(address + size - 1)); | |
| 692 ASSERT(size < blinkPagePayloadSize()); | |
| 693 // The free list entries are only pointer aligned (but when we allocate | |
| 694 // from them we are 8 byte aligned due to the header size). | |
| 695 ASSERT(!((reinterpret_cast<uintptr_t>(address) + sizeof(Header)) & allocatio
nMask)); | |
| 696 ASSERT(!(size & allocationMask)); | |
| 697 ASAN_POISON_MEMORY_REGION(address, size); | |
| 698 FreeListEntry* entry; | |
| 699 if (size < sizeof(*entry)) { | |
| 700 // Create a dummy header with only a size and freelist bit set. | |
| 701 ASSERT(size >= sizeof(BasicObjectHeader)); | |
| 702 // Free list encode the size to mark the lost memory as freelist memory. | |
| 703 new (NotNull, address) BasicObjectHeader(BasicObjectHeader::freeListEnco
dedSize(size)); | |
| 704 // This memory gets lost. Sweeping can reclaim it. | |
| 705 return; | |
| 706 } | |
| 707 entry = new (NotNull, address) FreeListEntry(size); | |
| 708 #if defined(ADDRESS_SANITIZER) | |
| 709 // For ASan we don't add the entry to the free lists until the asanDeferMemo
ryReuseCount | |
| 710 // reaches zero. However we always add entire pages to ensure that adding a
new page will | |
| 711 // increase the allocation space. | |
| 712 if (HeapPage<Header>::payloadSize() != size && !entry->shouldAddToFreeList()
) | |
| 713 return; | |
| 714 #endif | |
| 715 int index = bucketIndexForSize(size); | |
| 716 entry->link(&m_freeLists[index]); | |
| 717 if (!m_lastFreeListEntries[index]) | |
| 718 m_lastFreeListEntries[index] = entry; | |
| 719 if (index > m_biggestFreeListIndex) | |
| 720 m_biggestFreeListIndex = index; | |
| 721 } | |
| 722 | |
| 723 template<typename Header> | |
| 724 void ThreadHeap<Header>::promptlyFreeObject(Header* header) | |
| 725 { | |
| 726 ASSERT(!m_threadState->isSweepInProgress()); | |
| 727 header->checkHeader(); | |
| 728 Address address = reinterpret_cast<Address>(header); | |
| 729 Address payload = header->payload(); | |
| 730 size_t size = header->size(); | |
| 731 size_t payloadSize = header->payloadSize(); | |
| 732 BaseHeapPage* page = pageHeaderFromObject(address); | |
| 733 ASSERT(size > 0); | |
| 734 ASSERT(page == heapPageFromAddress(address)); | |
| 735 | |
| 736 { | |
| 737 ThreadState::NoSweepScope scope(m_threadState); | |
| 738 HeapObjectHeader::finalize(header->gcInfo(), payload, payloadSize); | |
| 739 #if !ENABLE(ASSERT) && !defined(LEAK_SANITIZER) && !defined(ADDRESS_SANITIZER) | |
| 740 memset(payload, 0, payloadSize); | |
| 741 #endif | |
| 742 header->markPromptlyFreed(); | |
| 743 } | |
| 744 | |
| 745 page->addToPromptlyFreedSize(size); | |
| 746 m_promptlyFreedCount++; | |
| 747 } | |
| 748 | |
| 749 template<typename Header> | |
| 750 bool ThreadHeap<Header>::coalesce(size_t minSize) | |
| 751 { | |
| 752 if (m_threadState->isSweepInProgress()) | |
| 753 return false; | |
| 754 | |
| 755 if (m_promptlyFreedCount < 256) | |
| 756 return false; | |
| 757 | |
| 758 // The smallest bucket able to satisfy an allocation request for minSize is | |
| 759 // the bucket where all free-list entries are guarantied to be larger than | |
| 760 // minSize. That bucket is one larger than the bucket minSize would go into. | |
| 761 size_t neededBucketIndex = bucketIndexForSize(minSize) + 1; | |
| 762 size_t neededFreeEntrySize = 1 << neededBucketIndex; | |
| 763 size_t neededPromptlyFreedSize = neededFreeEntrySize * 3; | |
| 764 size_t foundFreeEntrySize = 0; | |
| 765 | |
| 766 // Bailout early on large requests because it is unlikely we will find a fre
e-list entry. | |
| 767 if (neededPromptlyFreedSize >= blinkPageSize) | |
| 768 return false; | |
| 769 | |
| 770 TRACE_EVENT_BEGIN2("blink_gc", "ThreadHeap::coalesce" , "requestedSize", (un
signed)minSize , "neededSize", (unsigned)neededFreeEntrySize); | |
| 771 | |
| 772 // Search for a coalescing candidate. | |
| 773 ASSERT(!ownsNonEmptyAllocationArea()); | |
| 774 size_t pageCount = 0; | |
| 775 HeapPage<Header>* page = m_firstPage; | |
| 776 while (page) { | |
| 777 // Only consider one of the first 'n' pages. A "younger" page is more li
kely to have freed backings. | |
| 778 if (++pageCount > numberOfPagesToConsiderForCoalescing) { | |
| 779 page = 0; | |
| 780 break; | |
| 781 } | |
| 782 // Only coalesce pages with "sufficient" promptly freed space. | |
| 783 if (page->promptlyFreedSize() >= neededPromptlyFreedSize) { | |
| 784 break; | |
| 785 } | |
| 786 page = page->next(); | |
| 787 } | |
| 788 | |
| 789 // If we found a likely candidate, fully coalesce all its promptly-freed ent
ries. | |
| 790 if (page) { | |
| 791 page->clearObjectStartBitMap(); | |
| 792 page->resetPromptlyFreedSize(); | |
| 793 size_t freedCount = 0; | |
| 794 Address startOfGap = page->payload(); | |
| 795 for (Address headerAddress = startOfGap; headerAddress < page->end(); )
{ | |
| 796 BasicObjectHeader* basicHeader = reinterpret_cast<BasicObjectHeader*
>(headerAddress); | |
| 797 ASSERT(basicHeader->size() > 0); | |
| 798 ASSERT(basicHeader->size() < blinkPagePayloadSize()); | |
| 799 | |
| 800 if (basicHeader->isPromptlyFreed()) { | |
| 801 stats().decreaseObjectSpace(reinterpret_cast<Header*>(basicHeade
r)->payloadSize()); | |
| 802 size_t size = basicHeader->size(); | |
| 803 ASSERT(size >= sizeof(Header)); | |
| 804 #if !ENABLE(ASSERT) && !defined(LEAK_SANITIZER) && !defined(ADDRESS_SANITIZER) | |
| 805 memset(headerAddress, 0, sizeof(Header)); | |
| 806 #endif | |
| 807 ++freedCount; | |
| 808 headerAddress += size; | |
| 809 continue; | |
| 810 } | |
| 811 | |
| 812 if (startOfGap != headerAddress) { | |
| 813 size_t size = headerAddress - startOfGap; | |
| 814 addToFreeList(startOfGap, size); | |
| 815 if (size > foundFreeEntrySize) | |
| 816 foundFreeEntrySize = size; | |
| 817 } | |
| 818 | |
| 819 headerAddress += basicHeader->size(); | |
| 820 startOfGap = headerAddress; | |
| 821 } | |
| 822 | |
| 823 if (startOfGap != page->end()) { | |
| 824 size_t size = page->end() - startOfGap; | |
| 825 addToFreeList(startOfGap, size); | |
| 826 if (size > foundFreeEntrySize) | |
| 827 foundFreeEntrySize = size; | |
| 828 } | |
| 829 | |
| 830 // Check before subtracting because freedCount might not be balanced wit
h freed entries. | |
| 831 if (freedCount < m_promptlyFreedCount) | |
| 832 m_promptlyFreedCount -= freedCount; | |
| 833 else | |
| 834 m_promptlyFreedCount = 0; | |
| 835 } | |
| 836 | |
| 837 TRACE_EVENT_END1("blink_gc", "ThreadHeap::coalesce", "foundFreeEntrySize", (
unsigned)foundFreeEntrySize); | |
| 838 | |
| 839 if (foundFreeEntrySize < neededFreeEntrySize) { | |
| 840 // If coalescing failed, reset the freed count to delay coalescing again
. | |
| 841 m_promptlyFreedCount = 0; | |
| 842 return false; | |
| 843 } | |
| 844 | |
| 845 return true; | |
| 846 } | |
| 847 | |
| 848 template<typename Header> | |
| 849 Address ThreadHeap<Header>::allocateLargeObject(size_t size, const GCInfo* gcInf
o) | |
| 850 { | |
| 851 // Caller already added space for object header and rounded up to allocation
alignment | |
| 852 ASSERT(!(size & allocationMask)); | |
| 853 | |
| 854 size_t allocationSize = sizeof(LargeHeapObject<Header>) + size; | |
| 855 | |
| 856 // Ensure that there is enough space for alignment. If the header | |
| 857 // is not a multiple of 8 bytes we will allocate an extra | |
| 858 // headerPadding<Header> bytes to ensure it 8 byte aligned. | |
| 859 allocationSize += headerPadding<Header>(); | |
| 860 | |
| 861 // If ASan is supported we add allocationGranularity bytes to the allocated
space and | |
| 862 // poison that to detect overflows | |
| 863 #if defined(ADDRESS_SANITIZER) | |
| 864 allocationSize += allocationGranularity; | |
| 865 #endif | |
| 866 if (threadState()->shouldGC()) | |
| 867 threadState()->setGCRequested(); | |
| 868 Heap::flushHeapDoesNotContainCache(); | |
| 869 PageMemory* pageMemory = PageMemory::allocate(allocationSize); | |
| 870 Address largeObjectAddress = pageMemory->writableStart(); | |
| 871 Address headerAddress = largeObjectAddress + sizeof(LargeHeapObject<Header>)
+ headerPadding<Header>(); | |
| 872 memset(headerAddress, 0, size); | |
| 873 Header* header = new (NotNull, headerAddress) Header(size, gcInfo); | |
| 874 Address result = headerAddress + sizeof(*header); | |
| 875 ASSERT(!(reinterpret_cast<uintptr_t>(result) & allocationMask)); | |
| 876 LargeHeapObject<Header>* largeObject = new (largeObjectAddress) LargeHeapObj
ect<Header>(pageMemory, gcInfo, threadState()); | |
| 877 | |
| 878 // Poison the object header and allocationGranularity bytes after the object | |
| 879 ASAN_POISON_MEMORY_REGION(header, sizeof(*header)); | |
| 880 ASAN_POISON_MEMORY_REGION(largeObject->address() + largeObject->size(), allo
cationGranularity); | |
| 881 largeObject->link(&m_firstLargeHeapObject); | |
| 882 stats().increaseAllocatedSpace(largeObject->size()); | |
| 883 stats().increaseObjectSpace(largeObject->payloadSize()); | |
| 884 return result; | |
| 885 } | |
| 886 | |
| 887 template<typename Header> | |
| 888 void ThreadHeap<Header>::freeLargeObject(LargeHeapObject<Header>* object, LargeH
eapObject<Header>** previousNext) | |
| 889 { | |
| 890 flushHeapContainsCache(); | |
| 891 object->unlink(previousNext); | |
| 892 object->finalize(); | |
| 893 | |
| 894 // Unpoison the object header and allocationGranularity bytes after the | |
| 895 // object before freeing. | |
| 896 ASAN_UNPOISON_MEMORY_REGION(object->heapObjectHeader(), sizeof(Header)); | |
| 897 ASAN_UNPOISON_MEMORY_REGION(object->address() + object->size(), allocationGr
anularity); | |
| 898 | |
| 899 if (object->terminating()) { | |
| 900 ASSERT(ThreadState::current()->isTerminating()); | |
| 901 // The thread is shutting down so this object is being removed as part | |
| 902 // of a thread local GC. In that case the object could be traced in the | |
| 903 // next global GC either due to a dead object being traced via a | |
| 904 // conservative pointer or due to a programming error where an object | |
| 905 // in another thread heap keeps a dangling pointer to this object. | |
| 906 // To guard against this we put the large object memory in the | |
| 907 // orphanedPagePool to ensure it is still reachable. After the next glob
al | |
| 908 // GC it can be released assuming no rogue/dangling pointers refer to | |
| 909 // it. | |
| 910 // NOTE: large objects are not moved to the free page pool as it is | |
| 911 // unlikely they can be reused due to their individual sizes. | |
| 912 Heap::orphanedPagePool()->addOrphanedPage(m_index, object); | |
| 913 } else { | |
| 914 ASSERT(!ThreadState::current()->isTerminating()); | |
| 915 PageMemory* memory = object->storage(); | |
| 916 object->~LargeHeapObject<Header>(); | |
| 917 delete memory; | |
| 918 } | |
| 919 } | |
| 920 | |
| 921 template<typename DataType> | |
| 922 PagePool<DataType>::PagePool() | |
| 923 { | |
| 924 for (int i = 0; i < NumberOfHeaps; ++i) { | |
| 925 m_pool[i] = 0; | |
| 926 } | |
| 927 } | |
| 928 | |
| 929 FreePagePool::~FreePagePool() | |
| 930 { | |
| 931 for (int index = 0; index < NumberOfHeaps; ++index) { | |
| 932 while (PoolEntry* entry = m_pool[index]) { | |
| 933 m_pool[index] = entry->next; | |
| 934 PageMemory* memory = entry->data; | |
| 935 ASSERT(memory); | |
| 936 delete memory; | |
| 937 delete entry; | |
| 938 } | |
| 939 } | |
| 940 } | |
| 941 | |
| 942 void FreePagePool::addFreePage(int index, PageMemory* memory) | |
| 943 { | |
| 944 // When adding a page to the pool we decommit it to ensure it is unused | |
| 945 // while in the pool. This also allows the physical memory, backing the | |
| 946 // page, to be given back to the OS. | |
| 947 memory->decommit(); | |
| 948 MutexLocker locker(m_mutex[index]); | |
| 949 PoolEntry* entry = new PoolEntry(memory, m_pool[index]); | |
| 950 m_pool[index] = entry; | |
| 951 } | |
| 952 | |
| 953 PageMemory* FreePagePool::takeFreePage(int index) | |
| 954 { | |
| 955 MutexLocker locker(m_mutex[index]); | |
| 956 while (PoolEntry* entry = m_pool[index]) { | |
| 957 m_pool[index] = entry->next; | |
| 958 PageMemory* memory = entry->data; | |
| 959 ASSERT(memory); | |
| 960 delete entry; | |
| 961 if (memory->commit()) | |
| 962 return memory; | |
| 963 | |
| 964 // We got some memory, but failed to commit it, try again. | |
| 965 delete memory; | |
| 966 } | |
| 967 return 0; | |
| 968 } | |
| 969 | |
| 970 OrphanedPagePool::~OrphanedPagePool() | |
| 971 { | |
| 972 for (int index = 0; index < NumberOfHeaps; ++index) { | |
| 973 while (PoolEntry* entry = m_pool[index]) { | |
| 974 m_pool[index] = entry->next; | |
| 975 BaseHeapPage* page = entry->data; | |
| 976 delete entry; | |
| 977 PageMemory* memory = page->storage(); | |
| 978 ASSERT(memory); | |
| 979 page->~BaseHeapPage(); | |
| 980 delete memory; | |
| 981 } | |
| 982 } | |
| 983 } | |
| 984 | |
| 985 void OrphanedPagePool::addOrphanedPage(int index, BaseHeapPage* page) | |
| 986 { | |
| 987 page->markOrphaned(); | |
| 988 PoolEntry* entry = new PoolEntry(page, m_pool[index]); | |
| 989 m_pool[index] = entry; | |
| 990 } | |
| 991 | |
| 992 NO_SANITIZE_ADDRESS | |
| 993 void OrphanedPagePool::decommitOrphanedPages() | |
| 994 { | |
| 995 #if ENABLE(ASSERT) | |
| 996 // No locking needed as all threads are at safepoints at this point in time. | |
| 997 ThreadState::AttachedThreadStateSet& threads = ThreadState::attachedThreads(
); | |
| 998 for (ThreadState::AttachedThreadStateSet::iterator it = threads.begin(), end
= threads.end(); it != end; ++it) | |
| 999 ASSERT((*it)->isAtSafePoint()); | |
| 1000 #endif | |
| 1001 | |
| 1002 for (int index = 0; index < NumberOfHeaps; ++index) { | |
| 1003 PoolEntry* entry = m_pool[index]; | |
| 1004 PoolEntry** prevNext = &m_pool[index]; | |
| 1005 while (entry) { | |
| 1006 BaseHeapPage* page = entry->data; | |
| 1007 if (page->tracedAfterOrphaned()) { | |
| 1008 // If the orphaned page was traced in the last GC it is not | |
| 1009 // decommited. We only decommit a page, ie. put it in the | |
| 1010 // memory pool, when the page has no objects pointing to it. | |
| 1011 // We remark the page as orphaned to clear the tracedAfterOrphan
ed | |
| 1012 // flag and any object trace bits that were set during tracing. | |
| 1013 page->markOrphaned(); | |
| 1014 prevNext = &entry->next; | |
| 1015 entry = entry->next; | |
| 1016 continue; | |
| 1017 } | |
| 1018 | |
| 1019 // Page was not traced. Check if we should reuse the memory or just | |
| 1020 // free it. Large object memory is not reused, but freed, normal | |
| 1021 // blink heap pages are reused. | |
| 1022 // NOTE: We call the destructor before freeing or adding to the | |
| 1023 // free page pool. | |
| 1024 PageMemory* memory = page->storage(); | |
| 1025 if (page->isLargeObject()) { | |
| 1026 page->~BaseHeapPage(); | |
| 1027 delete memory; | |
| 1028 } else { | |
| 1029 page->~BaseHeapPage(); | |
| 1030 // Clear out the page's memory before adding it to the free page | |
| 1031 // pool to ensure it is zero filled when being reused. | |
| 1032 clearMemory(memory); | |
| 1033 Heap::freePagePool()->addFreePage(index, memory); | |
| 1034 } | |
| 1035 | |
| 1036 PoolEntry* deadEntry = entry; | |
| 1037 entry = entry->next; | |
| 1038 *prevNext = entry; | |
| 1039 delete deadEntry; | |
| 1040 } | |
| 1041 } | |
| 1042 } | |
| 1043 | |
| 1044 NO_SANITIZE_ADDRESS | |
| 1045 void OrphanedPagePool::clearMemory(PageMemory* memory) | |
| 1046 { | |
| 1047 #if defined(ADDRESS_SANITIZER) | |
| 1048 // Don't use memset when running with ASan since this needs to zap | |
| 1049 // poisoned memory as well and the NO_SANITIZE_ADDRESS annotation | |
| 1050 // only works for code in this method and not for calls to memset. | |
| 1051 Address base = memory->writableStart(); | |
| 1052 for (Address current = base; current < base + blinkPagePayloadSize(); ++curr
ent) | |
| 1053 *current = 0; | |
| 1054 #else | |
| 1055 memset(memory->writableStart(), 0, blinkPagePayloadSize()); | |
| 1056 #endif | |
| 1057 } | |
| 1058 | |
| 1059 #if ENABLE(ASSERT) | |
| 1060 bool OrphanedPagePool::contains(void* object) | |
| 1061 { | |
| 1062 for (int index = 0; index < NumberOfHeaps; ++index) { | |
| 1063 for (PoolEntry* entry = m_pool[index]; entry; entry = entry->next) { | |
| 1064 BaseHeapPage* page = entry->data; | |
| 1065 if (page->contains(reinterpret_cast<Address>(object))) | |
| 1066 return true; | |
| 1067 } | |
| 1068 } | |
| 1069 return false; | |
| 1070 } | |
| 1071 #endif | |
| 1072 | |
| 1073 template<> | |
| 1074 void ThreadHeap<FinalizedHeapObjectHeader>::addPageToHeap(const GCInfo* gcInfo) | |
| 1075 { | |
| 1076 // When adding a page to the ThreadHeap using FinalizedHeapObjectHeaders the
GCInfo on | |
| 1077 // the heap should be unused (ie. 0). | |
| 1078 allocatePage(0); | |
| 1079 } | |
| 1080 | |
| 1081 template<> | |
| 1082 void ThreadHeap<HeapObjectHeader>::addPageToHeap(const GCInfo* gcInfo) | |
| 1083 { | |
| 1084 // When adding a page to the ThreadHeap using HeapObjectHeaders store the GC
Info on the heap | |
| 1085 // since it is the same for all objects | |
| 1086 ASSERT(gcInfo); | |
| 1087 allocatePage(gcInfo); | |
| 1088 } | |
| 1089 | |
| 1090 template <typename Header> | |
| 1091 void ThreadHeap<Header>::removePageFromHeap(HeapPage<Header>* page) | |
| 1092 { | |
| 1093 MutexLocker locker(m_threadState->sweepMutex()); | |
| 1094 flushHeapContainsCache(); | |
| 1095 if (page->terminating()) { | |
| 1096 // The thread is shutting down so this page is being removed as part | |
| 1097 // of a thread local GC. In that case the page could be accessed in the | |
| 1098 // next global GC either due to a dead object being traced via a | |
| 1099 // conservative pointer or due to a programming error where an object | |
| 1100 // in another thread heap keeps a dangling pointer to this object. | |
| 1101 // To guard against this we put the page in the orphanedPagePool to | |
| 1102 // ensure it is still reachable. After the next global GC it can be | |
| 1103 // decommitted and moved to the page pool assuming no rogue/dangling | |
| 1104 // pointers refer to it. | |
| 1105 Heap::orphanedPagePool()->addOrphanedPage(m_index, page); | |
| 1106 } else { | |
| 1107 PageMemory* memory = page->storage(); | |
| 1108 page->~HeapPage<Header>(); | |
| 1109 Heap::freePagePool()->addFreePage(m_index, memory); | |
| 1110 } | |
| 1111 } | |
| 1112 | |
| 1113 template<typename Header> | |
| 1114 void ThreadHeap<Header>::allocatePage(const GCInfo* gcInfo) | |
| 1115 { | |
| 1116 Heap::flushHeapDoesNotContainCache(); | |
| 1117 PageMemory* pageMemory = Heap::freePagePool()->takeFreePage(m_index); | |
| 1118 // We continue allocating page memory until we succeed in getting one. | |
| 1119 // Since the FreePagePool is global other threads could use all the | |
| 1120 // newly allocated page memory before this thread calls takeFreePage. | |
| 1121 while (!pageMemory) { | |
| 1122 // Allocate a memory region for blinkPagesPerRegion pages that | |
| 1123 // will each have the following layout. | |
| 1124 // | |
| 1125 // [ guard os page | ... payload ... | guard os page ] | |
| 1126 // ^---{ aligned to blink page size } | |
| 1127 PageMemoryRegion* region = PageMemoryRegion::allocate(blinkPageSize * bl
inkPagesPerRegion, blinkPagesPerRegion); | |
| 1128 // Setup the PageMemory object for each of the pages in the | |
| 1129 // region. | |
| 1130 size_t offset = 0; | |
| 1131 for (size_t i = 0; i < blinkPagesPerRegion; i++) { | |
| 1132 Heap::freePagePool()->addFreePage(m_index, PageMemory::setupPageMemo
ryInRegion(region, offset, blinkPagePayloadSize())); | |
| 1133 offset += blinkPageSize; | |
| 1134 } | |
| 1135 pageMemory = Heap::freePagePool()->takeFreePage(m_index); | |
| 1136 } | |
| 1137 HeapPage<Header>* page = new (pageMemory->writableStart()) HeapPage<Header>(
pageMemory, this, gcInfo); | |
| 1138 // Use a separate list for pages allocated during sweeping to make | |
| 1139 // sure that we do not accidentally sweep objects that have been | |
| 1140 // allocated during sweeping. | |
| 1141 if (m_threadState->isSweepInProgress()) { | |
| 1142 if (!m_lastPageAllocatedDuringSweeping) | |
| 1143 m_lastPageAllocatedDuringSweeping = page; | |
| 1144 page->link(&m_firstPageAllocatedDuringSweeping); | |
| 1145 } else { | |
| 1146 page->link(&m_firstPage); | |
| 1147 } | |
| 1148 ++m_numberOfNormalPages; | |
| 1149 addToFreeList(page->payload(), HeapPage<Header>::payloadSize()); | |
| 1150 } | |
| 1151 | |
| 1152 #if ENABLE(ASSERT) | |
| 1153 template<typename Header> | |
| 1154 bool ThreadHeap<Header>::pagesToBeSweptContains(Address address) | |
| 1155 { | |
| 1156 for (HeapPage<Header>* page = m_firstPage; page; page = page->next()) { | |
| 1157 if (page->contains(address)) | |
| 1158 return true; | |
| 1159 } | |
| 1160 return false; | |
| 1161 } | |
| 1162 | |
| 1163 template<typename Header> | |
| 1164 bool ThreadHeap<Header>::pagesAllocatedDuringSweepingContains(Address address) | |
| 1165 { | |
| 1166 for (HeapPage<Header>* page = m_firstPageAllocatedDuringSweeping; page; page
= page->next()) { | |
| 1167 if (page->contains(address)) | |
| 1168 return true; | |
| 1169 } | |
| 1170 return false; | |
| 1171 } | |
| 1172 | |
| 1173 template<typename Header> | |
| 1174 void ThreadHeap<Header>::getScannedStats(HeapStats& scannedStats) | |
| 1175 { | |
| 1176 ASSERT(!m_firstPageAllocatedDuringSweeping); | |
| 1177 for (HeapPage<Header>* page = m_firstPage; page; page = page->next()) | |
| 1178 page->getStats(scannedStats); | |
| 1179 for (LargeHeapObject<Header>* current = m_firstLargeHeapObject; current; cur
rent = current->next()) | |
| 1180 current->getStats(scannedStats); | |
| 1181 } | |
| 1182 #endif | |
| 1183 | |
| 1184 template<typename Header> | |
| 1185 void ThreadHeap<Header>::sweepNormalPages(HeapStats* stats) | |
| 1186 { | |
| 1187 HeapPage<Header>* page = m_firstPage; | |
| 1188 HeapPage<Header>** previousNext = &m_firstPage; | |
| 1189 HeapPage<Header>* previous = 0; | |
| 1190 while (page) { | |
| 1191 page->resetPromptlyFreedSize(); | |
| 1192 if (page->isEmpty()) { | |
| 1193 HeapPage<Header>* unused = page; | |
| 1194 if (unused == m_mergePoint) | |
| 1195 m_mergePoint = previous; | |
| 1196 page = page->next(); | |
| 1197 HeapPage<Header>::unlink(this, unused, previousNext); | |
| 1198 --m_numberOfNormalPages; | |
| 1199 } else { | |
| 1200 page->sweep(stats, this); | |
| 1201 previousNext = &page->m_next; | |
| 1202 previous = page; | |
| 1203 page = page->next(); | |
| 1204 } | |
| 1205 } | |
| 1206 } | |
| 1207 | |
| 1208 template<typename Header> | |
| 1209 void ThreadHeap<Header>::sweepLargePages(HeapStats* stats) | |
| 1210 { | |
| 1211 LargeHeapObject<Header>** previousNext = &m_firstLargeHeapObject; | |
| 1212 for (LargeHeapObject<Header>* current = m_firstLargeHeapObject; current;) { | |
| 1213 if (current->isMarked()) { | |
| 1214 stats->increaseAllocatedSpace(current->size()); | |
| 1215 stats->increaseObjectSpace(current->payloadSize()); | |
| 1216 current->unmark(); | |
| 1217 previousNext = ¤t->m_next; | |
| 1218 current = current->next(); | |
| 1219 } else { | |
| 1220 LargeHeapObject<Header>* next = current->next(); | |
| 1221 freeLargeObject(current, previousNext); | |
| 1222 current = next; | |
| 1223 } | |
| 1224 } | |
| 1225 } | |
| 1226 | |
| 1227 | |
| 1228 // STRICT_ASAN_finalIZATION_CHECKING turns on poisoning of all objects during | |
| 1229 // sweeping to catch cases where dead objects touch each other. This is not | |
| 1230 // turned on by default because it also triggers for cases that are safe. | |
| 1231 // Examples of such safe cases are context life cycle observers and timers | |
| 1232 // embedded in garbage collected objects. | |
| 1233 #define STRICT_ASAN_finalIZATION_CHECKING 0 | |
| 1234 | |
| 1235 template<typename Header> | |
| 1236 void ThreadHeap<Header>::sweep(HeapStats* stats) | |
| 1237 { | |
| 1238 ASSERT(isConsistentForSweeping()); | |
| 1239 #if defined(ADDRESS_SANITIZER) && STRICT_ASAN_finalIZATION_CHECKING | |
| 1240 // When using ASan do a pre-sweep where all unmarked objects are | |
| 1241 // poisoned before calling their finalizer methods. This can catch | |
| 1242 // the case where the finalizer of an object tries to modify | |
| 1243 // another object as part of finalization. | |
| 1244 for (HeapPage<Header>* page = m_firstPage; page; page = page->next()) | |
| 1245 page->poisonUnmarkedObjects(); | |
| 1246 #endif | |
| 1247 sweepNormalPages(stats); | |
| 1248 sweepLargePages(stats); | |
| 1249 } | |
| 1250 | |
| 1251 template<typename Header> | |
| 1252 void ThreadHeap<Header>::postSweepProcessing() | |
| 1253 { | |
| 1254 // If pages have been allocated during sweeping, link them into | |
| 1255 // the list of pages. | |
| 1256 if (m_firstPageAllocatedDuringSweeping) { | |
| 1257 m_lastPageAllocatedDuringSweeping->m_next = m_firstPage; | |
| 1258 m_firstPage = m_firstPageAllocatedDuringSweeping; | |
| 1259 m_lastPageAllocatedDuringSweeping = 0; | |
| 1260 m_firstPageAllocatedDuringSweeping = 0; | |
| 1261 } | |
| 1262 } | |
| 1263 | |
| 1264 #if ENABLE(ASSERT) | |
| 1265 template<typename Header> | |
| 1266 bool ThreadHeap<Header>::isConsistentForSweeping() | |
| 1267 { | |
| 1268 // A thread heap is consistent for sweeping if none of the pages to | |
| 1269 // be swept contain a freelist block or the current allocation | |
| 1270 // point. | |
| 1271 for (size_t i = 0; i < blinkPageSizeLog2; i++) { | |
| 1272 for (FreeListEntry* freeListEntry = m_freeLists[i]; freeListEntry; freeL
istEntry = freeListEntry->next()) { | |
| 1273 if (pagesToBeSweptContains(freeListEntry->address())) { | |
| 1274 return false; | |
| 1275 } | |
| 1276 ASSERT(pagesAllocatedDuringSweepingContains(freeListEntry->address()
)); | |
| 1277 } | |
| 1278 } | |
| 1279 if (ownsNonEmptyAllocationArea()) { | |
| 1280 ASSERT(pagesToBeSweptContains(currentAllocationPoint()) | |
| 1281 || pagesAllocatedDuringSweepingContains(currentAllocationPoint())); | |
| 1282 return !pagesToBeSweptContains(currentAllocationPoint()); | |
| 1283 } | |
| 1284 return true; | |
| 1285 } | |
| 1286 #endif | |
| 1287 | |
| 1288 template<typename Header> | |
| 1289 void ThreadHeap<Header>::makeConsistentForSweeping() | |
| 1290 { | |
| 1291 if (ownsNonEmptyAllocationArea()) | |
| 1292 addToFreeList(currentAllocationPoint(), remainingAllocationSize()); | |
| 1293 setAllocationPoint(0, 0); | |
| 1294 clearFreeLists(); | |
| 1295 } | |
| 1296 | |
| 1297 template<typename Header> | |
| 1298 void ThreadHeap<Header>::clearLiveAndMarkDead() | |
| 1299 { | |
| 1300 ASSERT(isConsistentForSweeping()); | |
| 1301 for (HeapPage<Header>* page = m_firstPage; page; page = page->next()) | |
| 1302 page->clearLiveAndMarkDead(); | |
| 1303 for (LargeHeapObject<Header>* current = m_firstLargeHeapObject; current; cur
rent = current->next()) { | |
| 1304 if (current->isMarked()) | |
| 1305 current->unmark(); | |
| 1306 else | |
| 1307 current->setDeadMark(); | |
| 1308 } | |
| 1309 } | |
| 1310 | |
| 1311 template<typename Header> | |
| 1312 void ThreadHeap<Header>::clearFreeLists() | |
| 1313 { | |
| 1314 m_promptlyFreedCount = 0; | |
| 1315 for (size_t i = 0; i < blinkPageSizeLog2; i++) { | |
| 1316 m_freeLists[i] = 0; | |
| 1317 m_lastFreeListEntries[i] = 0; | |
| 1318 } | |
| 1319 } | |
| 1320 | |
| 1321 int BaseHeap::bucketIndexForSize(size_t size) | |
| 1322 { | |
| 1323 ASSERT(size > 0); | |
| 1324 int index = -1; | |
| 1325 while (size) { | |
| 1326 size >>= 1; | |
| 1327 index++; | |
| 1328 } | |
| 1329 return index; | |
| 1330 } | |
| 1331 | |
| 1332 template<typename Header> | |
| 1333 HeapPage<Header>::HeapPage(PageMemory* storage, ThreadHeap<Header>* heap, const
GCInfo* gcInfo) | |
| 1334 : BaseHeapPage(storage, gcInfo, heap->threadState()) | |
| 1335 , m_next(0) | |
| 1336 { | |
| 1337 COMPILE_ASSERT(!(sizeof(HeapPage<Header>) & allocationMask), page_header_inc
orrectly_aligned); | |
| 1338 m_objectStartBitMapComputed = false; | |
| 1339 ASSERT(isPageHeaderAddress(reinterpret_cast<Address>(this))); | |
| 1340 heap->stats().increaseAllocatedSpace(blinkPageSize); | |
| 1341 } | |
| 1342 | |
| 1343 template<typename Header> | |
| 1344 void HeapPage<Header>::link(HeapPage** prevNext) | |
| 1345 { | |
| 1346 m_next = *prevNext; | |
| 1347 *prevNext = this; | |
| 1348 } | |
| 1349 | |
| 1350 template<typename Header> | |
| 1351 void HeapPage<Header>::unlink(ThreadHeap<Header>* heap, HeapPage* unused, HeapPa
ge** prevNext) | |
| 1352 { | |
| 1353 *prevNext = unused->m_next; | |
| 1354 heap->removePageFromHeap(unused); | |
| 1355 } | |
| 1356 | |
| 1357 template<typename Header> | |
| 1358 void HeapPage<Header>::getStats(HeapStats& stats) | |
| 1359 { | |
| 1360 stats.increaseAllocatedSpace(blinkPageSize); | |
| 1361 Address headerAddress = payload(); | |
| 1362 ASSERT(headerAddress != end()); | |
| 1363 do { | |
| 1364 Header* header = reinterpret_cast<Header*>(headerAddress); | |
| 1365 if (!header->isFree()) | |
| 1366 stats.increaseObjectSpace(header->payloadSize()); | |
| 1367 ASSERT(header->size() < blinkPagePayloadSize()); | |
| 1368 headerAddress += header->size(); | |
| 1369 ASSERT(headerAddress <= end()); | |
| 1370 } while (headerAddress < end()); | |
| 1371 } | |
| 1372 | |
| 1373 template<typename Header> | |
| 1374 bool HeapPage<Header>::isEmpty() | |
| 1375 { | |
| 1376 BasicObjectHeader* header = reinterpret_cast<BasicObjectHeader*>(payload()); | |
| 1377 return header->isFree() && (header->size() == payloadSize()); | |
| 1378 } | |
| 1379 | |
| 1380 template<typename Header> | |
| 1381 void HeapPage<Header>::sweep(HeapStats* stats, ThreadHeap<Header>* heap) | |
| 1382 { | |
| 1383 clearObjectStartBitMap(); | |
| 1384 stats->increaseAllocatedSpace(blinkPageSize); | |
| 1385 Address startOfGap = payload(); | |
| 1386 for (Address headerAddress = startOfGap; headerAddress < end(); ) { | |
| 1387 BasicObjectHeader* basicHeader = reinterpret_cast<BasicObjectHeader*>(he
aderAddress); | |
| 1388 ASSERT(basicHeader->size() > 0); | |
| 1389 ASSERT(basicHeader->size() < blinkPagePayloadSize()); | |
| 1390 | |
| 1391 if (basicHeader->isFree()) { | |
| 1392 size_t size = basicHeader->size(); | |
| 1393 #if !ENABLE(ASSERT) && !defined(LEAK_SANITIZER) && !defined(ADDRESS_SANITIZER) | |
| 1394 // Zero the memory in the free list header to maintain the | |
| 1395 // invariant that memory on the free list is zero filled. | |
| 1396 // The rest of the memory is already on the free list and is | |
| 1397 // therefore already zero filled. | |
| 1398 if (size < sizeof(FreeListEntry)) | |
| 1399 memset(headerAddress, 0, size); | |
| 1400 else | |
| 1401 memset(headerAddress, 0, sizeof(FreeListEntry)); | |
| 1402 #endif | |
| 1403 headerAddress += size; | |
| 1404 continue; | |
| 1405 } | |
| 1406 // At this point we know this is a valid object of type Header | |
| 1407 Header* header = static_cast<Header*>(basicHeader); | |
| 1408 | |
| 1409 if (!header->isMarked()) { | |
| 1410 // For ASan we unpoison the specific object when calling the finaliz
er and | |
| 1411 // poison it again when done to allow the object's own finalizer to
operate | |
| 1412 // on the object, but not have other finalizers be allowed to access
it. | |
| 1413 ASAN_UNPOISON_MEMORY_REGION(header->payload(), header->payloadSize()
); | |
| 1414 finalize(header); | |
| 1415 size_t size = header->size(); | |
| 1416 #if !ENABLE(ASSERT) && !defined(LEAK_SANITIZER) && !defined(ADDRESS_SANITIZER) | |
| 1417 // This memory will be added to the freelist. Maintain the invariant | |
| 1418 // that memory on the freelist is zero filled. | |
| 1419 memset(headerAddress, 0, size); | |
| 1420 #endif | |
| 1421 ASAN_POISON_MEMORY_REGION(header->payload(), header->payloadSize()); | |
| 1422 headerAddress += size; | |
| 1423 continue; | |
| 1424 } | |
| 1425 | |
| 1426 if (startOfGap != headerAddress) | |
| 1427 heap->addToFreeList(startOfGap, headerAddress - startOfGap); | |
| 1428 header->unmark(); | |
| 1429 headerAddress += header->size(); | |
| 1430 stats->increaseObjectSpace(header->payloadSize()); | |
| 1431 startOfGap = headerAddress; | |
| 1432 } | |
| 1433 if (startOfGap != end()) | |
| 1434 heap->addToFreeList(startOfGap, end() - startOfGap); | |
| 1435 } | |
| 1436 | |
| 1437 template<typename Header> | |
| 1438 void HeapPage<Header>::clearLiveAndMarkDead() | |
| 1439 { | |
| 1440 for (Address headerAddress = payload(); headerAddress < end();) { | |
| 1441 Header* header = reinterpret_cast<Header*>(headerAddress); | |
| 1442 ASSERT(header->size() < blinkPagePayloadSize()); | |
| 1443 // Check if a free list entry first since we cannot call | |
| 1444 // isMarked on a free list entry. | |
| 1445 if (header->isFree()) { | |
| 1446 headerAddress += header->size(); | |
| 1447 continue; | |
| 1448 } | |
| 1449 if (header->isMarked()) | |
| 1450 header->unmark(); | |
| 1451 else | |
| 1452 header->setDeadMark(); | |
| 1453 headerAddress += header->size(); | |
| 1454 } | |
| 1455 } | |
| 1456 | |
| 1457 template<typename Header> | |
| 1458 void HeapPage<Header>::populateObjectStartBitMap() | |
| 1459 { | |
| 1460 memset(&m_objectStartBitMap, 0, objectStartBitMapSize); | |
| 1461 Address start = payload(); | |
| 1462 for (Address headerAddress = start; headerAddress < end();) { | |
| 1463 Header* header = reinterpret_cast<Header*>(headerAddress); | |
| 1464 size_t objectOffset = headerAddress - start; | |
| 1465 ASSERT(!(objectOffset & allocationMask)); | |
| 1466 size_t objectStartNumber = objectOffset / allocationGranularity; | |
| 1467 size_t mapIndex = objectStartNumber / 8; | |
| 1468 ASSERT(mapIndex < objectStartBitMapSize); | |
| 1469 m_objectStartBitMap[mapIndex] |= (1 << (objectStartNumber & 7)); | |
| 1470 headerAddress += header->size(); | |
| 1471 ASSERT(headerAddress <= end()); | |
| 1472 } | |
| 1473 m_objectStartBitMapComputed = true; | |
| 1474 } | |
| 1475 | |
| 1476 template<typename Header> | |
| 1477 void HeapPage<Header>::clearObjectStartBitMap() | |
| 1478 { | |
| 1479 m_objectStartBitMapComputed = false; | |
| 1480 } | |
| 1481 | |
| 1482 static int numberOfLeadingZeroes(uint8_t byte) | |
| 1483 { | |
| 1484 if (!byte) | |
| 1485 return 8; | |
| 1486 int result = 0; | |
| 1487 if (byte <= 0x0F) { | |
| 1488 result += 4; | |
| 1489 byte = byte << 4; | |
| 1490 } | |
| 1491 if (byte <= 0x3F) { | |
| 1492 result += 2; | |
| 1493 byte = byte << 2; | |
| 1494 } | |
| 1495 if (byte <= 0x7F) | |
| 1496 result++; | |
| 1497 return result; | |
| 1498 } | |
| 1499 | |
| 1500 template<typename Header> | |
| 1501 Header* HeapPage<Header>::findHeaderFromAddress(Address address) | |
| 1502 { | |
| 1503 if (address < payload()) | |
| 1504 return 0; | |
| 1505 if (!isObjectStartBitMapComputed()) | |
| 1506 populateObjectStartBitMap(); | |
| 1507 size_t objectOffset = address - payload(); | |
| 1508 size_t objectStartNumber = objectOffset / allocationGranularity; | |
| 1509 size_t mapIndex = objectStartNumber / 8; | |
| 1510 ASSERT(mapIndex < objectStartBitMapSize); | |
| 1511 size_t bit = objectStartNumber & 7; | |
| 1512 uint8_t byte = m_objectStartBitMap[mapIndex] & ((1 << (bit + 1)) - 1); | |
| 1513 while (!byte) { | |
| 1514 ASSERT(mapIndex > 0); | |
| 1515 byte = m_objectStartBitMap[--mapIndex]; | |
| 1516 } | |
| 1517 int leadingZeroes = numberOfLeadingZeroes(byte); | |
| 1518 objectStartNumber = (mapIndex * 8) + 7 - leadingZeroes; | |
| 1519 objectOffset = objectStartNumber * allocationGranularity; | |
| 1520 Address objectAddress = objectOffset + payload(); | |
| 1521 Header* header = reinterpret_cast<Header*>(objectAddress); | |
| 1522 if (header->isFree()) | |
| 1523 return 0; | |
| 1524 return header; | |
| 1525 } | |
| 1526 | |
| 1527 template<typename Header> | |
| 1528 void HeapPage<Header>::checkAndMarkPointer(Visitor* visitor, Address address) | |
| 1529 { | |
| 1530 ASSERT(contains(address)); | |
| 1531 Header* header = findHeaderFromAddress(address); | |
| 1532 if (!header || header->hasDeadMark()) | |
| 1533 return; | |
| 1534 | |
| 1535 #if ENABLE(GC_PROFILE_MARKING) | |
| 1536 visitor->setHostInfo(&address, "stack"); | |
| 1537 #endif | |
| 1538 if (hasVTable(header) && !vTableInitialized(header->payload())) { | |
| 1539 visitor->markNoTracing(header); | |
| 1540 ASSERT(isUninitializedMemory(header->payload(), header->payloadSize())); | |
| 1541 } else { | |
| 1542 visitor->mark(header, traceCallback(header)); | |
| 1543 } | |
| 1544 } | |
| 1545 | |
| 1546 #if ENABLE(GC_PROFILE_MARKING) | |
| 1547 template<typename Header> | |
| 1548 const GCInfo* HeapPage<Header>::findGCInfo(Address address) | |
| 1549 { | |
| 1550 if (address < payload()) | |
| 1551 return 0; | |
| 1552 | |
| 1553 if (gcInfo()) // for non FinalizedObjectHeader | |
| 1554 return gcInfo(); | |
| 1555 | |
| 1556 Header* header = findHeaderFromAddress(address); | |
| 1557 if (!header) | |
| 1558 return 0; | |
| 1559 | |
| 1560 return header->gcInfo(); | |
| 1561 } | |
| 1562 #endif | |
| 1563 | |
| 1564 #if ENABLE(GC_PROFILE_HEAP) | |
| 1565 template<typename Header> | |
| 1566 void HeapPage<Header>::snapshot(TracedValue* json, ThreadState::SnapshotInfo* in
fo) | |
| 1567 { | |
| 1568 Header* header = 0; | |
| 1569 for (Address addr = payload(); addr < end(); addr += header->size()) { | |
| 1570 header = reinterpret_cast<Header*>(addr); | |
| 1571 if (json) | |
| 1572 json->pushInteger(header->encodedSize()); | |
| 1573 if (header->isFree()) { | |
| 1574 info->freeSize += header->size(); | |
| 1575 continue; | |
| 1576 } | |
| 1577 | |
| 1578 const GCInfo* gcinfo = header->gcInfo() ? header->gcInfo() : gcInfo(); | |
| 1579 size_t tag = info->getClassTag(gcinfo); | |
| 1580 size_t age = header->age(); | |
| 1581 if (json) | |
| 1582 json->pushInteger(tag); | |
| 1583 if (header->isMarked()) { | |
| 1584 info->liveCount[tag] += 1; | |
| 1585 info->liveSize[tag] += header->size(); | |
| 1586 // Count objects that are live when promoted to the final generation
. | |
| 1587 if (age == maxHeapObjectAge - 1) | |
| 1588 info->generations[tag][maxHeapObjectAge] += 1; | |
| 1589 header->incAge(); | |
| 1590 } else { | |
| 1591 info->deadCount[tag] += 1; | |
| 1592 info->deadSize[tag] += header->size(); | |
| 1593 // Count objects that are dead before the final generation. | |
| 1594 if (age < maxHeapObjectAge) | |
| 1595 info->generations[tag][age] += 1; | |
| 1596 } | |
| 1597 } | |
| 1598 } | |
| 1599 #endif | |
| 1600 | |
| 1601 #if defined(ADDRESS_SANITIZER) | |
| 1602 template<typename Header> | |
| 1603 void HeapPage<Header>::poisonUnmarkedObjects() | |
| 1604 { | |
| 1605 for (Address headerAddress = payload(); headerAddress < end(); ) { | |
| 1606 Header* header = reinterpret_cast<Header*>(headerAddress); | |
| 1607 ASSERT(header->size() < blinkPagePayloadSize()); | |
| 1608 | |
| 1609 if (!header->isFree() && !header->isMarked()) | |
| 1610 ASAN_POISON_MEMORY_REGION(header->payload(), header->payloadSize()); | |
| 1611 headerAddress += header->size(); | |
| 1612 } | |
| 1613 } | |
| 1614 #endif | |
| 1615 | |
| 1616 template<> | |
| 1617 inline void HeapPage<FinalizedHeapObjectHeader>::finalize(FinalizedHeapObjectHea
der* header) | |
| 1618 { | |
| 1619 header->finalize(); | |
| 1620 } | |
| 1621 | |
| 1622 template<> | |
| 1623 inline void HeapPage<HeapObjectHeader>::finalize(HeapObjectHeader* header) | |
| 1624 { | |
| 1625 ASSERT(gcInfo()); | |
| 1626 HeapObjectHeader::finalize(gcInfo(), header->payload(), header->payloadSize(
)); | |
| 1627 } | |
| 1628 | |
| 1629 template<> | |
| 1630 inline TraceCallback HeapPage<HeapObjectHeader>::traceCallback(HeapObjectHeader*
header) | |
| 1631 { | |
| 1632 ASSERT(gcInfo()); | |
| 1633 return gcInfo()->m_trace; | |
| 1634 } | |
| 1635 | |
| 1636 template<> | |
| 1637 inline TraceCallback HeapPage<FinalizedHeapObjectHeader>::traceCallback(Finalize
dHeapObjectHeader* header) | |
| 1638 { | |
| 1639 return header->traceCallback(); | |
| 1640 } | |
| 1641 | |
| 1642 template<> | |
| 1643 inline bool HeapPage<HeapObjectHeader>::hasVTable(HeapObjectHeader* header) | |
| 1644 { | |
| 1645 ASSERT(gcInfo()); | |
| 1646 return gcInfo()->hasVTable(); | |
| 1647 } | |
| 1648 | |
| 1649 template<> | |
| 1650 inline bool HeapPage<FinalizedHeapObjectHeader>::hasVTable(FinalizedHeapObjectHe
ader* header) | |
| 1651 { | |
| 1652 return header->hasVTable(); | |
| 1653 } | |
| 1654 | |
| 1655 template<typename Header> | |
| 1656 void LargeHeapObject<Header>::getStats(HeapStats& stats) | |
| 1657 { | |
| 1658 stats.increaseAllocatedSpace(size()); | |
| 1659 stats.increaseObjectSpace(payloadSize()); | |
| 1660 } | |
| 1661 | |
| 1662 #if ENABLE(GC_PROFILE_HEAP) | |
| 1663 template<typename Header> | |
| 1664 void LargeHeapObject<Header>::snapshot(TracedValue* json, ThreadState::SnapshotI
nfo* info) | |
| 1665 { | |
| 1666 Header* header = heapObjectHeader(); | |
| 1667 size_t tag = info->getClassTag(header->gcInfo()); | |
| 1668 size_t age = header->age(); | |
| 1669 if (isMarked()) { | |
| 1670 info->liveCount[tag] += 1; | |
| 1671 info->liveSize[tag] += header->size(); | |
| 1672 // Count objects that are live when promoted to the final generation. | |
| 1673 if (age == maxHeapObjectAge - 1) | |
| 1674 info->generations[tag][maxHeapObjectAge] += 1; | |
| 1675 header->incAge(); | |
| 1676 } else { | |
| 1677 info->deadCount[tag] += 1; | |
| 1678 info->deadSize[tag] += header->size(); | |
| 1679 // Count objects that are dead before the final generation. | |
| 1680 if (age < maxHeapObjectAge) | |
| 1681 info->generations[tag][age] += 1; | |
| 1682 } | |
| 1683 | |
| 1684 if (json) { | |
| 1685 json->setInteger("class", tag); | |
| 1686 json->setInteger("size", header->size()); | |
| 1687 json->setInteger("isMarked", isMarked()); | |
| 1688 } | |
| 1689 } | |
| 1690 #endif | |
| 1691 | |
| 1692 template<typename Entry> | |
| 1693 void HeapExtentCache<Entry>::flush() | |
| 1694 { | |
| 1695 if (m_hasEntries) { | |
| 1696 for (int i = 0; i < numberOfEntries; i++) | |
| 1697 m_entries[i] = Entry(); | |
| 1698 m_hasEntries = false; | |
| 1699 } | |
| 1700 } | |
| 1701 | |
| 1702 template<typename Entry> | |
| 1703 size_t HeapExtentCache<Entry>::hash(Address address) | |
| 1704 { | |
| 1705 size_t value = (reinterpret_cast<size_t>(address) >> blinkPageSizeLog2); | |
| 1706 value ^= value >> numberOfEntriesLog2; | |
| 1707 value ^= value >> (numberOfEntriesLog2 * 2); | |
| 1708 value &= numberOfEntries - 1; | |
| 1709 return value & ~1; // Returns only even number. | |
| 1710 } | |
| 1711 | |
| 1712 template<typename Entry> | |
| 1713 typename Entry::LookupResult HeapExtentCache<Entry>::lookup(Address address) | |
| 1714 { | |
| 1715 size_t index = hash(address); | |
| 1716 ASSERT(!(index & 1)); | |
| 1717 Address cachePage = roundToBlinkPageStart(address); | |
| 1718 if (m_entries[index].address() == cachePage) | |
| 1719 return m_entries[index].result(); | |
| 1720 if (m_entries[index + 1].address() == cachePage) | |
| 1721 return m_entries[index + 1].result(); | |
| 1722 return 0; | |
| 1723 } | |
| 1724 | |
| 1725 template<typename Entry> | |
| 1726 void HeapExtentCache<Entry>::addEntry(Address address, typename Entry::LookupRes
ult entry) | |
| 1727 { | |
| 1728 m_hasEntries = true; | |
| 1729 size_t index = hash(address); | |
| 1730 ASSERT(!(index & 1)); | |
| 1731 Address cachePage = roundToBlinkPageStart(address); | |
| 1732 m_entries[index + 1] = m_entries[index]; | |
| 1733 m_entries[index] = Entry(cachePage, entry); | |
| 1734 } | |
| 1735 | |
| 1736 // These should not be needed, but it seems impossible to persuade clang to | |
| 1737 // instantiate the template functions and export them from a shared library, so | |
| 1738 // we add these in the non-templated subclass, which does not have that issue. | |
| 1739 void HeapContainsCache::addEntry(Address address, BaseHeapPage* page) | |
| 1740 { | |
| 1741 HeapExtentCache<PositiveEntry>::addEntry(address, page); | |
| 1742 } | |
| 1743 | |
| 1744 BaseHeapPage* HeapContainsCache::lookup(Address address) | |
| 1745 { | |
| 1746 return HeapExtentCache<PositiveEntry>::lookup(address); | |
| 1747 } | |
| 1748 | |
| 1749 void Heap::flushHeapDoesNotContainCache() | |
| 1750 { | |
| 1751 s_heapDoesNotContainCache->flush(); | |
| 1752 } | |
| 1753 | |
| 1754 void CallbackStack::init(CallbackStack** first) | |
| 1755 { | |
| 1756 // The stacks are chained, so we start by setting this to null as terminator
. | |
| 1757 *first = 0; | |
| 1758 *first = new CallbackStack(first); | |
| 1759 } | |
| 1760 | |
| 1761 void CallbackStack::shutdown(CallbackStack** first) | |
| 1762 { | |
| 1763 CallbackStack* next; | |
| 1764 for (CallbackStack* current = *first; current; current = next) { | |
| 1765 next = current->m_next; | |
| 1766 delete current; | |
| 1767 } | |
| 1768 *first = 0; | |
| 1769 } | |
| 1770 | |
| 1771 CallbackStack::~CallbackStack() | |
| 1772 { | |
| 1773 #if ENABLE(ASSERT) | |
| 1774 clearUnused(); | |
| 1775 #endif | |
| 1776 } | |
| 1777 | |
| 1778 void CallbackStack::clearUnused() | |
| 1779 { | |
| 1780 for (size_t i = 0; i < bufferSize; i++) | |
| 1781 m_buffer[i] = Item(0, 0); | |
| 1782 } | |
| 1783 | |
| 1784 bool CallbackStack::isEmpty() | |
| 1785 { | |
| 1786 return currentBlockIsEmpty() && !m_next; | |
| 1787 } | |
| 1788 | |
| 1789 CallbackStack* CallbackStack::takeCallbacks(CallbackStack** first) | |
| 1790 { | |
| 1791 // If there is a full next block unlink and return it. | |
| 1792 if (m_next) { | |
| 1793 CallbackStack* result = m_next; | |
| 1794 m_next = result->m_next; | |
| 1795 result->m_next = 0; | |
| 1796 return result; | |
| 1797 } | |
| 1798 // Only the current block is in the stack. If the current block is | |
| 1799 // empty return 0. | |
| 1800 if (currentBlockIsEmpty()) | |
| 1801 return 0; | |
| 1802 // The current block is not empty. Return this block and insert a | |
| 1803 // new empty block as the marking stack. | |
| 1804 *first = 0; | |
| 1805 *first = new CallbackStack(first); | |
| 1806 return this; | |
| 1807 } | |
| 1808 | |
| 1809 template<CallbackInvocationMode Mode> | |
| 1810 bool CallbackStack::popAndInvokeCallback(CallbackStack** first, Visitor* visitor
) | |
| 1811 { | |
| 1812 if (currentBlockIsEmpty()) { | |
| 1813 if (!m_next) { | |
| 1814 #if ENABLE(ASSERT) | |
| 1815 clearUnused(); | |
| 1816 #endif | |
| 1817 return false; | |
| 1818 } | |
| 1819 CallbackStack* nextStack = m_next; | |
| 1820 *first = nextStack; | |
| 1821 delete this; | |
| 1822 return nextStack->popAndInvokeCallback<Mode>(first, visitor); | |
| 1823 } | |
| 1824 Item* item = --m_current; | |
| 1825 | |
| 1826 // If the object being traced is located on a page which is dead don't | |
| 1827 // trace it. This can happen when a conservative GC kept a dead object | |
| 1828 // alive which pointed to a (now gone) object on the cleaned up page. | |
| 1829 // Also if doing a thread local GC don't trace objects that are located | |
| 1830 // on other thread's heaps, ie. pages where the terminating flag is not | |
| 1831 // set. | |
| 1832 BaseHeapPage* heapPage = pageHeaderFromObject(item->object()); | |
| 1833 if (Mode == GlobalMarking && heapPage->orphaned()) { | |
| 1834 // When doing a global GC we should only get a trace callback to an orph
aned | |
| 1835 // page if the GC is conservative. If it is not conservative there is | |
| 1836 // a bug in the code where we have a dangling pointer to a page | |
| 1837 // on the dead thread. | |
| 1838 RELEASE_ASSERT(Heap::lastGCWasConservative()); | |
| 1839 heapPage->setTracedAfterOrphaned(); | |
| 1840 return true; | |
| 1841 } | |
| 1842 if (Mode == ThreadLocalMarking && (heapPage->orphaned() || !heapPage->termin
ating())) | |
| 1843 return true; | |
| 1844 // For WeaknessProcessing we should never reach orphaned pages since | |
| 1845 // they should never be registered as objects on orphaned pages are not | |
| 1846 // traced. We cannot assert this here since we might have an off-heap | |
| 1847 // collection. However we assert it in Heap::pushWeakObjectPointerCallback. | |
| 1848 | |
| 1849 VisitorCallback callback = item->callback(); | |
| 1850 #if ENABLE(GC_PROFILE_MARKING) | |
| 1851 if (ThreadState::isAnyThreadInGC()) // weak-processing will also use popAndI
nvokeCallback | |
| 1852 visitor->setHostInfo(item->object(), classOf(item->object())); | |
| 1853 #endif | |
| 1854 callback(visitor, item->object()); | |
| 1855 | |
| 1856 return true; | |
| 1857 } | |
| 1858 | |
| 1859 void CallbackStack::invokeCallbacks(CallbackStack** first, Visitor* visitor) | |
| 1860 { | |
| 1861 CallbackStack* stack = 0; | |
| 1862 // The first block is the only one where new ephemerons are added, so we | |
| 1863 // call the callbacks on that last, to catch any new ephemerons discovered | |
| 1864 // in the callbacks. | |
| 1865 // However, if enough ephemerons were added, we may have a new block that | |
| 1866 // has been prepended to the chain. This will be very rare, but we can | |
| 1867 // handle the situation by starting again and calling all the callbacks | |
| 1868 // a second time. | |
| 1869 while (stack != *first) { | |
| 1870 stack = *first; | |
| 1871 stack->invokeOldestCallbacks(visitor); | |
| 1872 } | |
| 1873 } | |
| 1874 | |
| 1875 void CallbackStack::invokeOldestCallbacks(Visitor* visitor) | |
| 1876 { | |
| 1877 // Recurse first (bufferSize at a time) so we get to the newly added entries | |
| 1878 // last. | |
| 1879 if (m_next) | |
| 1880 m_next->invokeOldestCallbacks(visitor); | |
| 1881 | |
| 1882 // This loop can tolerate entries being added by the callbacks after | |
| 1883 // iteration starts. | |
| 1884 for (unsigned i = 0; m_buffer + i < m_current; i++) { | |
| 1885 Item& item = m_buffer[i]; | |
| 1886 | |
| 1887 // We don't need to check for orphaned pages when popping an ephemeron | |
| 1888 // callback since the callback is only pushed after the object containin
g | |
| 1889 // it has been traced. There are basically three cases to consider: | |
| 1890 // 1. Member<EphemeronCollection> | |
| 1891 // 2. EphemeronCollection is part of a containing object | |
| 1892 // 3. EphemeronCollection is a value object in a collection | |
| 1893 // | |
| 1894 // Ad. 1. In this case we push the start of the ephemeron on the | |
| 1895 // marking stack and do the orphaned page check when popping it off | |
| 1896 // the marking stack. | |
| 1897 // Ad. 2. The containing object cannot be on an orphaned page since | |
| 1898 // in that case we wouldn't have traced its parts. This also means | |
| 1899 // the ephemeron collection is not on the orphaned page. | |
| 1900 // Ad. 3. Is the same as 2. The collection containing the ephemeron | |
| 1901 // collection as a value object cannot be on an orphaned page since | |
| 1902 // it would not have traced its values in that case. | |
| 1903 item.callback()(visitor, item.object()); | |
| 1904 } | |
| 1905 } | |
| 1906 | |
| 1907 #if ENABLE(ASSERT) | |
| 1908 bool CallbackStack::hasCallbackForObject(const void* object) | |
| 1909 { | |
| 1910 for (unsigned i = 0; m_buffer + i < m_current; i++) { | |
| 1911 Item* item = &m_buffer[i]; | |
| 1912 if (item->object() == object) { | |
| 1913 return true; | |
| 1914 } | |
| 1915 } | |
| 1916 if (m_next) | |
| 1917 return m_next->hasCallbackForObject(object); | |
| 1918 | |
| 1919 return false; | |
| 1920 } | |
| 1921 #endif | |
| 1922 | |
| 1923 // The marking mutex is used to ensure sequential access to data | |
| 1924 // structures during marking. The marking mutex needs to be acquired | |
| 1925 // during marking when elements are taken from the global marking | |
| 1926 // stack or when elements are added to the global ephemeron, | |
| 1927 // post-marking, and weak processing stacks. In debug mode the mutex | |
| 1928 // also needs to be acquired when asserts use the heap contains | |
| 1929 // caches. | |
| 1930 static Mutex& markingMutex() | |
| 1931 { | |
| 1932 AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); | |
| 1933 return mutex; | |
| 1934 } | |
| 1935 | |
| 1936 static ThreadCondition& markingCondition() | |
| 1937 { | |
| 1938 AtomicallyInitializedStatic(ThreadCondition&, condition = *new ThreadConditi
on); | |
| 1939 return condition; | |
| 1940 } | |
| 1941 | |
| 1942 static void markNoTracingCallback(Visitor* visitor, void* object) | |
| 1943 { | |
| 1944 visitor->markNoTracing(object); | |
| 1945 } | |
| 1946 | |
| 1947 class MarkingVisitor : public Visitor { | |
| 1948 public: | |
| 1949 #if ENABLE(GC_PROFILE_MARKING) | |
| 1950 typedef HashSet<uintptr_t> LiveObjectSet; | |
| 1951 typedef HashMap<String, LiveObjectSet> LiveObjectMap; | |
| 1952 typedef HashMap<uintptr_t, std::pair<uintptr_t, String> > ObjectGraph; | |
| 1953 #endif | |
| 1954 | |
| 1955 MarkingVisitor(CallbackStack** markingStack) : m_markingStack(markingStack) | |
| 1956 { | |
| 1957 } | |
| 1958 | |
| 1959 inline void visitHeader(HeapObjectHeader* header, const void* objectPointer,
TraceCallback callback) | |
| 1960 { | |
| 1961 ASSERT(header); | |
| 1962 #if ENABLE(ASSERT) | |
| 1963 { | |
| 1964 // Check that we are not marking objects that are outside | |
| 1965 // the heap by calling Heap::contains. However we cannot | |
| 1966 // call Heap::contains when outside a GC and we call mark | |
| 1967 // when doing weakness for ephemerons. Hence we only check | |
| 1968 // when called within. | |
| 1969 MutexLocker locker(markingMutex()); | |
| 1970 ASSERT(!ThreadState::isAnyThreadInGC() || Heap::containedInHeapOrOrp
hanedPage(header)); | |
| 1971 } | |
| 1972 #endif | |
| 1973 ASSERT(objectPointer); | |
| 1974 if (header->isMarked()) | |
| 1975 return; | |
| 1976 header->mark(); | |
| 1977 #if ENABLE(GC_PROFILE_MARKING) | |
| 1978 MutexLocker locker(objectGraphMutex()); | |
| 1979 String className(classOf(objectPointer)); | |
| 1980 { | |
| 1981 LiveObjectMap::AddResult result = currentlyLive().add(className, Liv
eObjectSet()); | |
| 1982 result.storedValue->value.add(reinterpret_cast<uintptr_t>(objectPoin
ter)); | |
| 1983 } | |
| 1984 ObjectGraph::AddResult result = objectGraph().add(reinterpret_cast<uintp
tr_t>(objectPointer), std::make_pair(reinterpret_cast<uintptr_t>(m_hostObject),
m_hostName)); | |
| 1985 ASSERT(result.isNewEntry); | |
| 1986 // fprintf(stderr, "%s[%p] -> %s[%p]\n", m_hostName.ascii().data(), m_ho
stObject, className.ascii().data(), objectPointer); | |
| 1987 #endif | |
| 1988 if (callback) | |
| 1989 Heap::pushTraceCallback(m_markingStack, const_cast<void*>(objectPoin
ter), callback); | |
| 1990 } | |
| 1991 | |
| 1992 virtual void mark(HeapObjectHeader* header, TraceCallback callback) override | |
| 1993 { | |
| 1994 // We need both the HeapObjectHeader and FinalizedHeapObjectHeader | |
| 1995 // version to correctly find the payload. | |
| 1996 visitHeader(header, header->payload(), callback); | |
| 1997 } | |
| 1998 | |
| 1999 virtual void mark(FinalizedHeapObjectHeader* header, TraceCallback callback)
override | |
| 2000 { | |
| 2001 // We need both the HeapObjectHeader and FinalizedHeapObjectHeader | |
| 2002 // version to correctly find the payload. | |
| 2003 visitHeader(header, header->payload(), callback); | |
| 2004 } | |
| 2005 | |
| 2006 virtual void mark(const void* objectPointer, TraceCallback callback) overrid
e | |
| 2007 { | |
| 2008 if (!objectPointer) | |
| 2009 return; | |
| 2010 FinalizedHeapObjectHeader* header = FinalizedHeapObjectHeader::fromPaylo
ad(objectPointer); | |
| 2011 visitHeader(header, header->payload(), callback); | |
| 2012 } | |
| 2013 | |
| 2014 virtual void registerDelayedMarkNoTracing(const void* object) override | |
| 2015 { | |
| 2016 Heap::pushPostMarkingCallback(const_cast<void*>(object), markNoTracingCa
llback); | |
| 2017 } | |
| 2018 | |
| 2019 virtual void registerWeakMembers(const void* closure, const void* containing
Object, WeakPointerCallback callback) override | |
| 2020 { | |
| 2021 Heap::pushWeakObjectPointerCallback(const_cast<void*>(closure), const_ca
st<void*>(containingObject), callback); | |
| 2022 } | |
| 2023 | |
| 2024 virtual void registerWeakTable(const void* closure, EphemeronCallback iterat
ionCallback, EphemeronCallback iterationDoneCallback) | |
| 2025 { | |
| 2026 Heap::registerWeakTable(const_cast<void*>(closure), iterationCallback, i
terationDoneCallback); | |
| 2027 } | |
| 2028 | |
| 2029 #if ENABLE(ASSERT) | |
| 2030 virtual bool weakTableRegistered(const void* closure) | |
| 2031 { | |
| 2032 return Heap::weakTableRegistered(closure); | |
| 2033 } | |
| 2034 #endif | |
| 2035 | |
| 2036 virtual bool isMarked(const void* objectPointer) override | |
| 2037 { | |
| 2038 return FinalizedHeapObjectHeader::fromPayload(objectPointer)->isMarked()
; | |
| 2039 } | |
| 2040 | |
| 2041 // This macro defines the necessary visitor methods for typed heaps | |
| 2042 #define DEFINE_VISITOR_METHODS(Type)
\ | |
| 2043 virtual void mark(const Type* objectPointer, TraceCallback callback) overrid
e \ | |
| 2044 {
\ | |
| 2045 if (!objectPointer)
\ | |
| 2046 return;
\ | |
| 2047 HeapObjectHeader* header =
\ | |
| 2048 HeapObjectHeader::fromPayload(objectPointer);
\ | |
| 2049 visitHeader(header, header->payload(), callback);
\ | |
| 2050 }
\ | |
| 2051 virtual bool isMarked(const Type* objectPointer) override
\ | |
| 2052 {
\ | |
| 2053 return HeapObjectHeader::fromPayload(objectPointer)->isMarked();
\ | |
| 2054 } | |
| 2055 | |
| 2056 FOR_EACH_TYPED_HEAP(DEFINE_VISITOR_METHODS) | |
| 2057 #undef DEFINE_VISITOR_METHODS | |
| 2058 | |
| 2059 #if ENABLE(GC_PROFILE_MARKING) | |
| 2060 void reportStats() | |
| 2061 { | |
| 2062 fprintf(stderr, "\n---------- AFTER MARKING -------------------\n"); | |
| 2063 for (LiveObjectMap::iterator it = currentlyLive().begin(), end = current
lyLive().end(); it != end; ++it) { | |
| 2064 fprintf(stderr, "%s %u", it->key.ascii().data(), it->value.size()); | |
| 2065 | |
| 2066 if (it->key == "blink::Document") | |
| 2067 reportStillAlive(it->value, previouslyLive().get(it->key)); | |
| 2068 | |
| 2069 fprintf(stderr, "\n"); | |
| 2070 } | |
| 2071 | |
| 2072 previouslyLive().swap(currentlyLive()); | |
| 2073 currentlyLive().clear(); | |
| 2074 | |
| 2075 for (HashSet<uintptr_t>::iterator it = objectsToFindPath().begin(), end
= objectsToFindPath().end(); it != end; ++it) { | |
| 2076 dumpPathToObjectFromObjectGraph(objectGraph(), *it); | |
| 2077 } | |
| 2078 } | |
| 2079 | |
| 2080 static void reportStillAlive(LiveObjectSet current, LiveObjectSet previous) | |
| 2081 { | |
| 2082 int count = 0; | |
| 2083 | |
| 2084 fprintf(stderr, " [previously %u]", previous.size()); | |
| 2085 for (LiveObjectSet::iterator it = current.begin(), end = current.end();
it != end; ++it) { | |
| 2086 if (previous.find(*it) == previous.end()) | |
| 2087 continue; | |
| 2088 count++; | |
| 2089 } | |
| 2090 | |
| 2091 if (!count) | |
| 2092 return; | |
| 2093 | |
| 2094 fprintf(stderr, " {survived 2GCs %d: ", count); | |
| 2095 for (LiveObjectSet::iterator it = current.begin(), end = current.end();
it != end; ++it) { | |
| 2096 if (previous.find(*it) == previous.end()) | |
| 2097 continue; | |
| 2098 fprintf(stderr, "%ld", *it); | |
| 2099 if (--count) | |
| 2100 fprintf(stderr, ", "); | |
| 2101 } | |
| 2102 ASSERT(!count); | |
| 2103 fprintf(stderr, "}"); | |
| 2104 } | |
| 2105 | |
| 2106 static void dumpPathToObjectFromObjectGraph(const ObjectGraph& graph, uintpt
r_t target) | |
| 2107 { | |
| 2108 ObjectGraph::const_iterator it = graph.find(target); | |
| 2109 if (it == graph.end()) | |
| 2110 return; | |
| 2111 fprintf(stderr, "Path to %lx of %s\n", target, classOf(reinterpret_cast<
const void*>(target)).ascii().data()); | |
| 2112 while (it != graph.end()) { | |
| 2113 fprintf(stderr, "<- %lx of %s\n", it->value.first, it->value.second.
utf8().data()); | |
| 2114 it = graph.find(it->value.first); | |
| 2115 } | |
| 2116 fprintf(stderr, "\n"); | |
| 2117 } | |
| 2118 | |
| 2119 static void dumpPathToObjectOnNextGC(void* p) | |
| 2120 { | |
| 2121 objectsToFindPath().add(reinterpret_cast<uintptr_t>(p)); | |
| 2122 } | |
| 2123 | |
| 2124 static Mutex& objectGraphMutex() | |
| 2125 { | |
| 2126 AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex); | |
| 2127 return mutex; | |
| 2128 } | |
| 2129 | |
| 2130 static LiveObjectMap& previouslyLive() | |
| 2131 { | |
| 2132 DEFINE_STATIC_LOCAL(LiveObjectMap, map, ()); | |
| 2133 return map; | |
| 2134 } | |
| 2135 | |
| 2136 static LiveObjectMap& currentlyLive() | |
| 2137 { | |
| 2138 DEFINE_STATIC_LOCAL(LiveObjectMap, map, ()); | |
| 2139 return map; | |
| 2140 } | |
| 2141 | |
| 2142 static ObjectGraph& objectGraph() | |
| 2143 { | |
| 2144 DEFINE_STATIC_LOCAL(ObjectGraph, graph, ()); | |
| 2145 return graph; | |
| 2146 } | |
| 2147 | |
| 2148 static HashSet<uintptr_t>& objectsToFindPath() | |
| 2149 { | |
| 2150 DEFINE_STATIC_LOCAL(HashSet<uintptr_t>, set, ()); | |
| 2151 return set; | |
| 2152 } | |
| 2153 #endif | |
| 2154 | |
| 2155 protected: | |
| 2156 virtual void registerWeakCell(void** cell, WeakPointerCallback callback) ove
rride | |
| 2157 { | |
| 2158 Heap::pushWeakCellPointerCallback(cell, callback); | |
| 2159 } | |
| 2160 | |
| 2161 private: | |
| 2162 CallbackStack** m_markingStack; | |
| 2163 }; | |
| 2164 | |
| 2165 BaseHeapPage* Heap::contains(Address address) | |
| 2166 { | |
| 2167 ASSERT(ThreadState::isAnyThreadInGC()); | |
| 2168 ThreadState::AttachedThreadStateSet& threads = ThreadState::attachedThreads(
); | |
| 2169 for (ThreadState::AttachedThreadStateSet::iterator it = threads.begin(), end
= threads.end(); it != end; ++it) { | |
| 2170 BaseHeapPage* page = (*it)->contains(address); | |
| 2171 if (page) | |
| 2172 return page; | |
| 2173 } | |
| 2174 return 0; | |
| 2175 } | |
| 2176 | |
| 2177 #if ENABLE(ASSERT) | |
| 2178 bool Heap::containedInHeapOrOrphanedPage(void* object) | |
| 2179 { | |
| 2180 return contains(object) || orphanedPagePool()->contains(object); | |
| 2181 } | |
| 2182 #endif | |
| 2183 | |
| 2184 Address Heap::checkAndMarkPointer(Visitor* visitor, Address address) | |
| 2185 { | |
| 2186 ASSERT(ThreadState::isAnyThreadInGC()); | |
| 2187 | |
| 2188 #if !ENABLE(ASSERT) | |
| 2189 if (s_heapDoesNotContainCache->lookup(address)) | |
| 2190 return 0; | |
| 2191 #endif | |
| 2192 | |
| 2193 ThreadState::AttachedThreadStateSet& threads = ThreadState::attachedThreads(
); | |
| 2194 for (ThreadState::AttachedThreadStateSet::iterator it = threads.begin(), end
= threads.end(); it != end; ++it) { | |
| 2195 if ((*it)->checkAndMarkPointer(visitor, address)) { | |
| 2196 // Pointer was in a page of that thread. If it actually pointed | |
| 2197 // into an object then that object was found and marked. | |
| 2198 ASSERT(!s_heapDoesNotContainCache->lookup(address)); | |
| 2199 s_lastGCWasConservative = true; | |
| 2200 return address; | |
| 2201 } | |
| 2202 } | |
| 2203 | |
| 2204 #if !ENABLE(ASSERT) | |
| 2205 s_heapDoesNotContainCache->addEntry(address, true); | |
| 2206 #else | |
| 2207 if (!s_heapDoesNotContainCache->lookup(address)) | |
| 2208 s_heapDoesNotContainCache->addEntry(address, true); | |
| 2209 #endif | |
| 2210 return 0; | |
| 2211 } | |
| 2212 | |
| 2213 #if ENABLE(GC_PROFILE_MARKING) | |
| 2214 const GCInfo* Heap::findGCInfo(Address address) | |
| 2215 { | |
| 2216 return ThreadState::findGCInfoFromAllThreads(address); | |
| 2217 } | |
| 2218 #endif | |
| 2219 | |
| 2220 #if ENABLE(GC_PROFILE_MARKING) | |
| 2221 void Heap::dumpPathToObjectOnNextGC(void* p) | |
| 2222 { | |
| 2223 static_cast<MarkingVisitor*>(s_markingVisitor)->dumpPathToObjectOnNextGC(p); | |
| 2224 } | |
| 2225 | |
| 2226 String Heap::createBacktraceString() | |
| 2227 { | |
| 2228 int framesToShow = 3; | |
| 2229 int stackFrameSize = 16; | |
| 2230 ASSERT(stackFrameSize >= framesToShow); | |
| 2231 typedef void* FramePointer; | |
| 2232 FramePointer* stackFrame = static_cast<FramePointer*>(alloca(sizeof(FramePoi
nter) * stackFrameSize)); | |
| 2233 WTFGetBacktrace(stackFrame, &stackFrameSize); | |
| 2234 | |
| 2235 StringBuilder builder; | |
| 2236 builder.append("Persistent"); | |
| 2237 bool didAppendFirstName = false; | |
| 2238 // Skip frames before/including "blink::Persistent". | |
| 2239 bool didSeePersistent = false; | |
| 2240 for (int i = 0; i < stackFrameSize && framesToShow > 0; ++i) { | |
| 2241 FrameToNameScope frameToName(stackFrame[i]); | |
| 2242 if (!frameToName.nullableName()) | |
| 2243 continue; | |
| 2244 if (strstr(frameToName.nullableName(), "blink::Persistent")) { | |
| 2245 didSeePersistent = true; | |
| 2246 continue; | |
| 2247 } | |
| 2248 if (!didSeePersistent) | |
| 2249 continue; | |
| 2250 if (!didAppendFirstName) { | |
| 2251 didAppendFirstName = true; | |
| 2252 builder.append(" ... Backtrace:"); | |
| 2253 } | |
| 2254 builder.append("\n\t"); | |
| 2255 builder.append(frameToName.nullableName()); | |
| 2256 --framesToShow; | |
| 2257 } | |
| 2258 return builder.toString().replace("blink::", ""); | |
| 2259 } | |
| 2260 #endif | |
| 2261 | |
| 2262 void Heap::pushTraceCallback(CallbackStack** stack, void* object, TraceCallback
callback) | |
| 2263 { | |
| 2264 #if ENABLE(ASSERT) | |
| 2265 { | |
| 2266 MutexLocker locker(markingMutex()); | |
| 2267 ASSERT(Heap::containedInHeapOrOrphanedPage(object)); | |
| 2268 } | |
| 2269 #endif | |
| 2270 CallbackStack::Item* slot = (*stack)->allocateEntry(stack); | |
| 2271 *slot = CallbackStack::Item(object, callback); | |
| 2272 } | |
| 2273 | |
| 2274 template<CallbackInvocationMode Mode> | |
| 2275 bool Heap::popAndInvokeTraceCallback(Visitor* visitor) | |
| 2276 { | |
| 2277 return s_markingStack->popAndInvokeCallback<Mode>(&s_markingStack, visitor); | |
| 2278 } | |
| 2279 | |
| 2280 void Heap::pushPostMarkingCallback(void* object, TraceCallback callback) | |
| 2281 { | |
| 2282 MutexLocker locker(markingMutex()); | |
| 2283 ASSERT(!Heap::orphanedPagePool()->contains(object)); | |
| 2284 CallbackStack::Item* slot = s_postMarkingCallbackStack->allocateEntry(&s_pos
tMarkingCallbackStack); | |
| 2285 *slot = CallbackStack::Item(object, callback); | |
| 2286 } | |
| 2287 | |
| 2288 bool Heap::popAndInvokePostMarkingCallback(Visitor* visitor) | |
| 2289 { | |
| 2290 return s_postMarkingCallbackStack->popAndInvokeCallback<PostMarking>(&s_post
MarkingCallbackStack, visitor); | |
| 2291 } | |
| 2292 | |
| 2293 void Heap::pushWeakCellPointerCallback(void** cell, WeakPointerCallback callback
) | |
| 2294 { | |
| 2295 MutexLocker locker(markingMutex()); | |
| 2296 ASSERT(!Heap::orphanedPagePool()->contains(cell)); | |
| 2297 CallbackStack::Item* slot = s_weakCallbackStack->allocateEntry(&s_weakCallba
ckStack); | |
| 2298 *slot = CallbackStack::Item(cell, callback); | |
| 2299 } | |
| 2300 | |
| 2301 void Heap::pushWeakObjectPointerCallback(void* closure, void* object, WeakPointe
rCallback callback) | |
| 2302 { | |
| 2303 MutexLocker locker(markingMutex()); | |
| 2304 ASSERT(Heap::contains(object)); | |
| 2305 BaseHeapPage* heapPageForObject = pageHeaderFromObject(object); | |
| 2306 ASSERT(!heapPageForObject->orphaned()); | |
| 2307 ASSERT(Heap::contains(object) == heapPageForObject); | |
| 2308 ThreadState* state = heapPageForObject->threadState(); | |
| 2309 state->pushWeakObjectPointerCallback(closure, callback); | |
| 2310 } | |
| 2311 | |
| 2312 bool Heap::popAndInvokeWeakPointerCallback(Visitor* visitor) | |
| 2313 { | |
| 2314 return s_weakCallbackStack->popAndInvokeCallback<WeaknessProcessing>(&s_weak
CallbackStack, visitor); | |
| 2315 } | |
| 2316 | |
| 2317 void Heap::registerWeakTable(void* table, EphemeronCallback iterationCallback, E
phemeronCallback iterationDoneCallback) | |
| 2318 { | |
| 2319 { | |
| 2320 MutexLocker locker(markingMutex()); | |
| 2321 // Check that the ephemeron table being pushed onto the stack is not on
an | |
| 2322 // orphaned page. | |
| 2323 ASSERT(!Heap::orphanedPagePool()->contains(table)); | |
| 2324 CallbackStack::Item* slot = s_ephemeronStack->allocateEntry(&s_ephemeron
Stack); | |
| 2325 *slot = CallbackStack::Item(table, iterationCallback); | |
| 2326 } | |
| 2327 | |
| 2328 // Register a post-marking callback to tell the tables that | |
| 2329 // ephemeron iteration is complete. | |
| 2330 pushPostMarkingCallback(table, iterationDoneCallback); | |
| 2331 } | |
| 2332 | |
| 2333 #if ENABLE(ASSERT) | |
| 2334 bool Heap::weakTableRegistered(const void* table) | |
| 2335 { | |
| 2336 MutexLocker locker(markingMutex()); | |
| 2337 ASSERT(s_ephemeronStack); | |
| 2338 return s_ephemeronStack->hasCallbackForObject(table); | |
| 2339 } | |
| 2340 #endif | |
| 2341 | |
| 2342 void Heap::prepareForGC() | |
| 2343 { | |
| 2344 ASSERT(ThreadState::isAnyThreadInGC()); | |
| 2345 ThreadState::AttachedThreadStateSet& threads = ThreadState::attachedThreads(
); | |
| 2346 for (ThreadState::AttachedThreadStateSet::iterator it = threads.begin(), end
= threads.end(); it != end; ++it) | |
| 2347 (*it)->prepareForGC(); | |
| 2348 } | |
| 2349 | |
| 2350 void Heap::collectGarbage(ThreadState::StackState stackState) | |
| 2351 { | |
| 2352 } | |
| 2353 | |
| 2354 void Heap::collectGarbageForTerminatingThread(ThreadState* state) | |
| 2355 { | |
| 2356 } | |
| 2357 | |
| 2358 void Heap::processMarkingStackEntries(int* runningMarkingThreads) | |
| 2359 { | |
| 2360 CallbackStack* stack = 0; | |
| 2361 MarkingVisitor visitor(&stack); | |
| 2362 { | |
| 2363 MutexLocker locker(markingMutex()); | |
| 2364 stack = s_markingStack->takeCallbacks(&s_markingStack); | |
| 2365 } | |
| 2366 while (stack) { | |
| 2367 while (stack->popAndInvokeCallback<GlobalMarking>(&stack, &visitor)) { } | |
| 2368 delete stack; | |
| 2369 { | |
| 2370 MutexLocker locker(markingMutex()); | |
| 2371 stack = s_markingStack->takeCallbacks(&s_markingStack); | |
| 2372 } | |
| 2373 } | |
| 2374 { | |
| 2375 MutexLocker locker(markingMutex()); | |
| 2376 if (!--(*runningMarkingThreads)) | |
| 2377 markingCondition().signal(); | |
| 2378 } | |
| 2379 } | |
| 2380 | |
| 2381 void Heap::processMarkingStackOnMultipleThreads() | |
| 2382 { | |
| 2383 } | |
| 2384 | |
| 2385 void Heap::processMarkingStackInParallel() | |
| 2386 { | |
| 2387 static const int numberOfBlocksForParallelMarking = 2; | |
| 2388 // Ephemeron fixed point loop run on the garbage collecting thread. | |
| 2389 do { | |
| 2390 // Iteratively mark all objects that are reachable from the objects | |
| 2391 // currently pushed onto the marking stack. Do so in parallel if there | |
| 2392 // are multiple blocks on the global marking stack. | |
| 2393 if (s_markingStack->numberOfBlocksExceeds(numberOfBlocksForParallelMarki
ng)) { | |
| 2394 processMarkingStackOnMultipleThreads(); | |
| 2395 } else { | |
| 2396 while (popAndInvokeTraceCallback<GlobalMarking>(s_markingVisitor)) {
} | |
| 2397 } | |
| 2398 | |
| 2399 // Mark any strong pointers that have now become reachable in ephemeron | |
| 2400 // maps. | |
| 2401 CallbackStack::invokeCallbacks(&s_ephemeronStack, s_markingVisitor); | |
| 2402 | |
| 2403 // Rerun loop if ephemeron processing queued more objects for tracing. | |
| 2404 } while (!s_markingStack->isEmpty()); | |
| 2405 } | |
| 2406 | |
| 2407 template<CallbackInvocationMode Mode> | |
| 2408 void Heap::processMarkingStack() | |
| 2409 { | |
| 2410 // Ephemeron fixed point loop. | |
| 2411 do { | |
| 2412 // Iteratively mark all objects that are reachable from the objects | |
| 2413 // currently pushed onto the marking stack. If Mode is ThreadLocalMarkin
g | |
| 2414 // don't continue tracing if the trace hits an object on another thread'
s | |
| 2415 // heap. | |
| 2416 while (popAndInvokeTraceCallback<Mode>(s_markingVisitor)) { } | |
| 2417 | |
| 2418 // Mark any strong pointers that have now become reachable in ephemeron | |
| 2419 // maps. | |
| 2420 CallbackStack::invokeCallbacks(&s_ephemeronStack, s_markingVisitor); | |
| 2421 | |
| 2422 // Rerun loop if ephemeron processing queued more objects for tracing. | |
| 2423 } while (!s_markingStack->isEmpty()); | |
| 2424 } | |
| 2425 | |
| 2426 void Heap::postMarkingProcessing() | |
| 2427 { | |
| 2428 // Call post-marking callbacks including: | |
| 2429 // 1. the ephemeronIterationDone callbacks on weak tables to do cleanup | |
| 2430 // (specifically to clear the queued bits for weak hash tables), and | |
| 2431 // 2. the markNoTracing callbacks on collection backings to mark them | |
| 2432 // if they are only reachable from their front objects. | |
| 2433 while (popAndInvokePostMarkingCallback(s_markingVisitor)) { } | |
| 2434 | |
| 2435 CallbackStack::clear(&s_ephemeronStack); | |
| 2436 | |
| 2437 // Post-marking callbacks should not trace any objects and | |
| 2438 // therefore the marking stack should be empty after the | |
| 2439 // post-marking callbacks. | |
| 2440 ASSERT(s_markingStack->isEmpty()); | |
| 2441 } | |
| 2442 | |
| 2443 void Heap::globalWeakProcessing() | |
| 2444 { | |
| 2445 // Call weak callbacks on objects that may now be pointing to dead | |
| 2446 // objects. | |
| 2447 while (popAndInvokeWeakPointerCallback(s_markingVisitor)) { } | |
| 2448 | |
| 2449 // It is not permitted to trace pointers of live objects in the weak | |
| 2450 // callback phase, so the marking stack should still be empty here. | |
| 2451 ASSERT(s_markingStack->isEmpty()); | |
| 2452 } | |
| 2453 | |
| 2454 void Heap::collectAllGarbage() | |
| 2455 { | |
| 2456 // FIXME: oilpan: we should perform a single GC and everything | |
| 2457 // should die. Unfortunately it is not the case for all objects | |
| 2458 // because the hierarchy was not completely moved to the heap and | |
| 2459 // some heap allocated objects own objects that contain persistents | |
| 2460 // pointing to other heap allocated objects. | |
| 2461 for (int i = 0; i < 5; i++) | |
| 2462 collectGarbage(ThreadState::NoHeapPointersOnStack); | |
| 2463 } | |
| 2464 | |
| 2465 void Heap::setForcePreciseGCForTesting() | |
| 2466 { | |
| 2467 ThreadState::current()->setForcePreciseGCForTesting(true); | |
| 2468 } | |
| 2469 | |
| 2470 template<typename Header> | |
| 2471 void ThreadHeap<Header>::prepareHeapForTermination() | |
| 2472 { | |
| 2473 for (HeapPage<Header>* page = m_firstPage; page; page = page->next()) { | |
| 2474 page->setTerminating(); | |
| 2475 } | |
| 2476 for (LargeHeapObject<Header>* current = m_firstLargeHeapObject; current; cur
rent = current->next()) { | |
| 2477 current->setTerminating(); | |
| 2478 } | |
| 2479 } | |
| 2480 | |
| 2481 template<typename Header> | |
| 2482 BaseHeap* ThreadHeap<Header>::split(int numberOfNormalPages) | |
| 2483 { | |
| 2484 // Create a new split off thread heap containing | |
| 2485 // |numberOfNormalPages| of the pages of this ThreadHeap for | |
| 2486 // parallel sweeping. The split off thread heap will be merged | |
| 2487 // with this heap at the end of sweeping and the temporary | |
| 2488 // ThreadHeap object will be deallocated after the merge. | |
| 2489 ASSERT(numberOfNormalPages > 0); | |
| 2490 ThreadHeap<Header>* splitOff = new ThreadHeap(m_threadState, m_index); | |
| 2491 HeapPage<Header>* splitPoint = m_firstPage; | |
| 2492 for (int i = 1; i < numberOfNormalPages; i++) | |
| 2493 splitPoint = splitPoint->next(); | |
| 2494 splitOff->m_firstPage = m_firstPage; | |
| 2495 m_firstPage = splitPoint->m_next; | |
| 2496 splitOff->m_mergePoint = splitPoint; | |
| 2497 splitOff->m_numberOfNormalPages = numberOfNormalPages; | |
| 2498 m_numberOfNormalPages -= numberOfNormalPages; | |
| 2499 splitPoint->m_next = 0; | |
| 2500 return splitOff; | |
| 2501 } | |
| 2502 | |
| 2503 template<typename Header> | |
| 2504 void ThreadHeap<Header>::merge(BaseHeap* splitOffBase) | |
| 2505 { | |
| 2506 ThreadHeap<Header>* splitOff = static_cast<ThreadHeap<Header>*>(splitOffBase
); | |
| 2507 // If the mergePoint is zero all split off pages became empty in | |
| 2508 // this round and we don't have to merge. There are no pages and | |
| 2509 // nothing on the freelists. | |
| 2510 ASSERT(splitOff->m_mergePoint || splitOff->m_numberOfNormalPages == 0); | |
| 2511 if (splitOff->m_mergePoint) { | |
| 2512 // Link the split off pages into the beginning of the list again. | |
| 2513 splitOff->m_mergePoint->m_next = m_firstPage; | |
| 2514 m_firstPage = splitOff->m_firstPage; | |
| 2515 m_numberOfNormalPages += splitOff->m_numberOfNormalPages; | |
| 2516 splitOff->m_firstPage = 0; | |
| 2517 // Merge free lists. | |
| 2518 for (size_t i = 0; i < blinkPageSizeLog2; i++) { | |
| 2519 if (!m_freeLists[i]) { | |
| 2520 m_freeLists[i] = splitOff->m_freeLists[i]; | |
| 2521 } else if (splitOff->m_freeLists[i]) { | |
| 2522 m_lastFreeListEntries[i]->append(splitOff->m_freeLists[i]); | |
| 2523 m_lastFreeListEntries[i] = splitOff->m_lastFreeListEntries[i]; | |
| 2524 } | |
| 2525 } | |
| 2526 } | |
| 2527 delete splitOffBase; | |
| 2528 } | |
| 2529 | |
| 2530 void Heap::getHeapSpaceSize(uint64_t* objectSpaceSize, uint64_t* allocatedSpaceS
ize) | |
| 2531 { | |
| 2532 *objectSpaceSize = 0; | |
| 2533 *allocatedSpaceSize = 0; | |
| 2534 ASSERT(ThreadState::isAnyThreadInGC()); | |
| 2535 ThreadState::AttachedThreadStateSet& threads = ThreadState::attachedThreads(
); | |
| 2536 typedef ThreadState::AttachedThreadStateSet::iterator ThreadStateIterator; | |
| 2537 for (ThreadStateIterator it = threads.begin(), end = threads.end(); it != en
d; ++it) { | |
| 2538 *objectSpaceSize += (*it)->stats().totalObjectSpace(); | |
| 2539 *allocatedSpaceSize += (*it)->stats().totalAllocatedSpace(); | |
| 2540 } | |
| 2541 } | |
| 2542 | |
| 2543 void Heap::getStats(HeapStats* stats) | |
| 2544 { | |
| 2545 stats->clear(); | |
| 2546 ASSERT(ThreadState::isAnyThreadInGC()); | |
| 2547 ThreadState::AttachedThreadStateSet& threads = ThreadState::attachedThreads(
); | |
| 2548 typedef ThreadState::AttachedThreadStateSet::iterator ThreadStateIterator; | |
| 2549 for (ThreadStateIterator it = threads.begin(), end = threads.end(); it != en
d; ++it) { | |
| 2550 HeapStats temp; | |
| 2551 (*it)->getStats(temp); | |
| 2552 stats->add(&temp); | |
| 2553 } | |
| 2554 } | |
| 2555 | |
| 2556 #if ENABLE(ASSERT) | |
| 2557 bool Heap::isConsistentForSweeping() | |
| 2558 { | |
| 2559 ASSERT(ThreadState::isAnyThreadInGC()); | |
| 2560 ThreadState::AttachedThreadStateSet& threads = ThreadState::attachedThreads(
); | |
| 2561 for (ThreadState::AttachedThreadStateSet::iterator it = threads.begin(), end
= threads.end(); it != end; ++it) { | |
| 2562 if (!(*it)->isConsistentForSweeping()) | |
| 2563 return false; | |
| 2564 } | |
| 2565 return true; | |
| 2566 } | |
| 2567 #endif | |
| 2568 | |
| 2569 void Heap::makeConsistentForSweeping() | |
| 2570 { | |
| 2571 ASSERT(ThreadState::isAnyThreadInGC()); | |
| 2572 ThreadState::AttachedThreadStateSet& threads = ThreadState::attachedThreads(
); | |
| 2573 for (ThreadState::AttachedThreadStateSet::iterator it = threads.begin(), end
= threads.end(); it != end; ++it) | |
| 2574 (*it)->makeConsistentForSweeping(); | |
| 2575 } | |
| 2576 | |
| 2577 void HeapAllocator::backingFree(void* address) | |
| 2578 { | |
| 2579 if (!address || ThreadState::isAnyThreadInGC()) | |
| 2580 return; | |
| 2581 | |
| 2582 ThreadState* state = ThreadState::current(); | |
| 2583 if (state->isSweepInProgress()) | |
| 2584 return; | |
| 2585 | |
| 2586 // Don't promptly free large objects because their page is never reused | |
| 2587 // and don't free backings allocated on other threads. | |
| 2588 BaseHeapPage* page = pageHeaderFromObject(address); | |
| 2589 if (page->isLargeObject() || page->threadState() != state) | |
| 2590 return; | |
| 2591 | |
| 2592 typedef HeapIndexTrait<CollectionBackingHeap> HeapTraits; | |
| 2593 typedef HeapTraits::HeapType HeapType; | |
| 2594 typedef HeapTraits::HeaderType HeaderType; | |
| 2595 | |
| 2596 HeaderType* header = HeaderType::fromPayload(address); | |
| 2597 header->checkHeader(); | |
| 2598 | |
| 2599 const GCInfo* gcInfo = header->gcInfo(); | |
| 2600 int heapIndex = HeapTraits::index(gcInfo->hasFinalizer()); | |
| 2601 HeapType* heap = static_cast<HeapType*>(state->heap(heapIndex)); | |
| 2602 heap->promptlyFreeObject(header); | |
| 2603 } | |
| 2604 | |
| 2605 // Force template instantiations for the types that we need. | |
| 2606 template class HeapPage<FinalizedHeapObjectHeader>; | |
| 2607 template class HeapPage<HeapObjectHeader>; | |
| 2608 template class ThreadHeap<FinalizedHeapObjectHeader>; | |
| 2609 template class ThreadHeap<HeapObjectHeader>; | |
| 2610 template bool CallbackStack::popAndInvokeCallback<GlobalMarking>(CallbackStack**
, Visitor*); | |
| 2611 template bool CallbackStack::popAndInvokeCallback<ThreadLocalMarking>(CallbackSt
ack**, Visitor*); | |
| 2612 template bool CallbackStack::popAndInvokeCallback<WeaknessProcessing>(CallbackSt
ack**, Visitor*); | |
| 2613 | |
| 2614 Visitor* Heap::s_markingVisitor; | |
| 2615 Vector<OwnPtr<blink::WebThread> >* Heap::s_markingThreads; | |
| 2616 CallbackStack* Heap::s_markingStack; | |
| 2617 CallbackStack* Heap::s_postMarkingCallbackStack; | |
| 2618 CallbackStack* Heap::s_weakCallbackStack; | |
| 2619 CallbackStack* Heap::s_ephemeronStack; | |
| 2620 HeapDoesNotContainCache* Heap::s_heapDoesNotContainCache; | |
| 2621 bool Heap::s_shutdownCalled = false; | |
| 2622 bool Heap::s_lastGCWasConservative = false; | |
| 2623 FreePagePool* Heap::s_freePagePool; | |
| 2624 OrphanedPagePool* Heap::s_orphanedPagePool; | |
| 2625 } | |
| OLD | NEW |