Changeset 115156 in webkit
- Timestamp:
- Apr 24, 2012 6:29:42 PM (12 years ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/API/JSBase.cpp
r114772 r115156 101 101 APIEntryShim entryShim(exec, false); 102 102 103 exec->globalData().heap. activityCallback()->didAbandonObjectGraph();103 exec->globalData().heap.reportAbandonedObjectGraph(); 104 104 } 105 105 -
trunk/Source/JavaScriptCore/API/JSContextRef.cpp
r114511 r115156 150 150 } else if (releasingGlobalObject) { 151 151 globalData.heap.activityCallback()->synchronize(); 152 globalData.heap. activityCallback()->didAbandonObjectGraph();152 globalData.heap.reportAbandonedObjectGraph(); 153 153 } 154 154 -
trunk/Source/JavaScriptCore/ChangeLog
r115151 r115156 1 2012-04-24 Mark Hahnenberg <mhahnenberg@apple.com> 2 3 GC Activity Callback timer should be based on how much has been allocated since the last collection 4 https://bugs.webkit.org/show_bug.cgi?id=84763 5 6 Reviewed by Geoffrey Garen. 7 8 The desired behavior for the GC timer is to collect at some point in the future, 9 regardless of how little we've allocated. A secondary goal, which is almost if not 10 as important, is for the timer to collect sooner if there is the potential to 11 collect a greater amount of memory. Conversely, as we allocate more memory we'd 12 like to reduce the delay to the next collection. If we're allocating quickly enough, 13 the timer should be preempted in favor of a normal allocation-triggered collection. 14 If allocation were to slow or stop, we'd like the timer to be able to opportunistically 15 run a collection without us having to allocate to the hard limit set by the Heap. 16 17 This type of policy can be described in terms of the amount of CPU we are willing 18 to dedicate to reclaim a single MB of memory. For example, we might be willing to 19 dedicate 1% of our CPU to reclaim 1 MB. We base our CPU usage off of the length of 20 the last collection, e.g. if our last collection took 1ms, we would want to wait about 21 100ms before running another collection to reclaim 1 MB. These constants should be 22 tune-able, e.g. 0.1% CPU = 1 MB vs. 1% CPU = 1 MB vs. 10% CPU = 1 MB. 23 24 * API/JSBase.cpp: Use the new reportAbandonedObjectGraph. 25 (JSGarbageCollect): 26 * API/JSContextRef.cpp: Ditto. 27 * heap/Heap.cpp: 28 (JSC::Heap::Heap): 29 (JSC::Heap::reportAbandonedObjectGraph): Similar to reportExtraMemoryCost. Clients call 30 this function to notify the Heap that some unknown number of JSC objects might have just 31 been abandoned and are now garbage. The Heap might schedule a new collection timer based 32 on this notification. 33 (JSC): 34 (JSC::Heap::collect): Renamed m_lastFullGCSize to the less confusing m_sizeAfterLastCollect. 35 * heap/Heap.h: 36 (Heap): 37 * heap/MarkedAllocator.h: 38 (JSC::MarkedAllocator::zapFreeList): Fixed a bug in zapFreeList that failed to nullify the 39 current allocator's FreeList once zapping was complete. 40 * runtime/GCActivityCallback.cpp: Removed didAbandonObjectGraph because it was replaced by 41 Heap::reportAbandonedObjectGraph. 42 (JSC): 43 * runtime/GCActivityCallback.h: 44 (JSC::GCActivityCallback::willCollect): 45 (DefaultGCActivityCallback): 46 * runtime/GCActivityCallbackCF.cpp: Refactored the GC timer code so that we now schedule the 47 timer based on how much we have allocated since the last collection up to a certain amount. 48 We use the length of the previous GC to try to keep our total cost of opportunistic timer-triggered 49 collections around 1% of the CPU per MB of garbage we expect to reclaim up to a maximum of 5 MB. 50 (DefaultGCActivityCallbackPlatformData): 51 (JSC): 52 (JSC::DefaultGCActivityCallback::~DefaultGCActivityCallback): 53 (JSC::DefaultGCActivityCallback::commonConstructor): 54 (JSC::scheduleTimer): 55 (JSC::cancelTimer): 56 (JSC::DefaultGCActivityCallback::didAllocate): 57 1 58 2012-04-24 Michael Saboff <msaboff@apple.com> 2 59 -
trunk/Source/JavaScriptCore/heap/Heap.cpp
r115092 r115156 313 313 : m_heapSize(heapSize) 314 314 , m_minBytesPerCycle(heapSizeForHint(heapSize)) 315 , m_ lastFullGCSize(0)315 , m_sizeAfterLastCollect(0) 316 316 , m_bytesAllocatedLimit(m_minBytesPerCycle) 317 317 , m_bytesAllocated(0) … … 472 472 } 473 473 474 void Heap::reportAbandonedObjectGraph() 475 { 476 // Our clients don't know exactly how much memory they 477 // are abandoning so we just guess for them. 478 double abandonedBytes = 0.10 * m_sizeAfterLastCollect; 479 480 // We want to accelerate the next collection. Because memory has just 481 // been abandoned, the next collection has the potential to 482 // be more profitable. Since allocation is the trigger for collection, 483 // we hasten the next collection by pretending that we've allocated more memory. 484 didAllocate(abandonedBytes); 485 } 486 474 487 void Heap::protect(JSValue k) 475 488 { … … 813 826 bool fullGC = sweepToggle == DoSweep; 814 827 if (!fullGC) 815 fullGC = (capacity() > 4 * m_ lastFullGCSize);828 fullGC = (capacity() > 4 * m_sizeAfterLastCollect); 816 829 #else 817 830 bool fullGC = true; … … 862 875 size_t newSize = size(); 863 876 if (fullGC) { 864 m_ lastFullGCSize= newSize;877 m_sizeAfterLastCollect = newSize; 865 878 m_bytesAllocatedLimit = max(newSize, m_minBytesPerCycle); 866 879 } -
trunk/Source/JavaScriptCore/heap/Heap.h
r115092 r115156 118 118 bool shouldCollect(); 119 119 void collect(SweepToggle); 120 120 121 void reportExtraMemoryCost(size_t cost); 122 void reportAbandonedObjectGraph(); 121 123 122 124 JS_EXPORT_PRIVATE void protect(JSValue); … … 204 206 const HeapSize m_heapSize; 205 207 const size_t m_minBytesPerCycle; 206 size_t m_ lastFullGCSize;208 size_t m_sizeAfterLastCollect; 207 209 208 210 size_t m_bytesAllocatedLimit; -
trunk/Source/JavaScriptCore/heap/MarkedAllocator.h
r114698 r115156 92 92 93 93 m_currentBlock->zapFreeList(m_freeList); 94 m_freeList .head = 0;94 m_freeList = MarkedBlock::FreeList(); 95 95 } 96 96 -
trunk/Source/JavaScriptCore/runtime/GCActivityCallback.cpp
r114772 r115156 51 51 } 52 52 53 void DefaultGCActivityCallback::didAbandonObjectGraph()54 {55 }56 57 53 void DefaultGCActivityCallback::synchronize() 58 54 { -
trunk/Source/JavaScriptCore/runtime/GCActivityCallback.h
r114772 r115156 46 46 virtual void didAllocate(size_t) { } 47 47 virtual void willCollect() { } 48 virtual void didAbandonObjectGraph() { }49 48 virtual void synchronize() { } 50 49 … … 64 63 virtual void didAllocate(size_t); 65 64 virtual void willCollect(); 66 virtual void didAbandonObjectGraph();67 65 virtual void synchronize(); 68 66 -
trunk/Source/JavaScriptCore/runtime/GCActivityCallbackCF.cpp
r114772 r115156 51 51 RetainPtr<CFRunLoopRef> runLoop; 52 52 CFRunLoopTimerContext context; 53 bool timerIsActive;53 double delay; 54 54 }; 55 55 56 const double gcCPUBudget = 0.025; 57 const double gcTimerIntervalMultiplier = 1.0 / gcCPUBudget; 56 const double gcTimeSlicePerMB = 0.01; // Percentage of CPU time we will spend to reclaim 1 MB 57 const double maxGCTimeSlice = 0.05; // The maximum amount of CPU time we want to use for opportunistic timer-triggered collections. 58 const double timerSlop = 2.0; // Fudge factor to avoid performance cost of resetting timer. 58 59 const CFTimeInterval decade = 60 * 60 * 24 * 365 * 10; 59 60 const CFTimeInterval hour = 60 * 60; 60 const size_t minBytesBeforeCollect = 128 * KB;61 61 62 62 void DefaultGCActivityCallbackPlatformData::timerDidFire(CFRunLoopTimerRef, void *info) … … 81 81 CFRunLoopRemoveTimer(d->runLoop.get(), d->timer.get(), kCFRunLoopCommonModes); 82 82 CFRunLoopTimerInvalidate(d->timer.get()); 83 d->context.info = 0;84 d->runLoop = 0;85 d->timer = 0;86 83 } 87 84 … … 94 91 d->runLoop = runLoop; 95 92 d->timer.adoptCF(CFRunLoopTimerCreate(0, decade, decade, 0, 0, DefaultGCActivityCallbackPlatformData::timerDidFire, &d->context)); 96 d-> timerIsActive = false;93 d->delay = decade; 97 94 CFRunLoopAddTimer(d->runLoop.get(), d->timer.get(), kCFRunLoopCommonModes); 98 95 } 99 96 100 static void scheduleTimer(DefaultGCActivityCallbackPlatformData* d )97 static void scheduleTimer(DefaultGCActivityCallbackPlatformData* d, double newDelay) 101 98 { 102 if ( d->timerIsActive)99 if (newDelay * timerSlop > d->delay) 103 100 return; 104 d ->timerIsActive = true;105 CFTimeInterval triggerInterval = static_cast<Heap*>(d->context.info)->lastGCLength() * gcTimerIntervalMultiplier;106 CFRunLoopTimerSetNextFireDate(d->timer.get(), CF AbsoluteTimeGetCurrent() + triggerInterval);101 double delta = d->delay - newDelay; 102 d->delay = newDelay; 103 CFRunLoopTimerSetNextFireDate(d->timer.get(), CFRunLoopTimerGetNextFireDate(d->timer.get()) - delta); 107 104 } 108 105 109 106 static void cancelTimer(DefaultGCActivityCallbackPlatformData* d) 110 107 { 111 if (!d->timerIsActive) 112 return; 113 d->timerIsActive = false; 108 d->delay = decade; 114 109 CFRunLoopTimerSetNextFireDate(d->timer.get(), CFAbsoluteTimeGetCurrent() + decade); 115 110 } … … 117 112 void DefaultGCActivityCallback::didAllocate(size_t bytes) 118 113 { 119 if (bytes < minBytesBeforeCollect) 120 return; 121 scheduleTimer(d.get()); 114 // The first byte allocated in an allocation cycle will report 0 bytes to didAllocate. 115 // We pretend it's one byte so that we don't ignore this allocation entirely. 116 if (!bytes) 117 bytes = 1; 118 Heap* heap = static_cast<Heap*>(d->context.info); 119 double gcTimeSlice = std::min((static_cast<double>(bytes) / MB) * gcTimeSlicePerMB, maxGCTimeSlice); 120 double newDelay = heap->lastGCLength() / gcTimeSlice; 121 scheduleTimer(d.get(), newDelay); 122 122 } 123 123 … … 125 125 { 126 126 cancelTimer(d.get()); 127 }128 129 void DefaultGCActivityCallback::didAbandonObjectGraph()130 {131 scheduleTimer(d.get());132 127 } 133 128
Note: See TracChangeset
for help on using the changeset viewer.