| | 1 | = Investigating Leaks and Bloat = |
| | 2 | ''by Simon Fraser'' |
| | 3 | |
| | 4 | - Or… a bunch of half-baked memory tools |
| | 5 | - Or… don’t be afraid to change code to collect data |
| | 6 | - Don’t be scared to do this |
| | 7 | |
| | 8 | - Starting problem |
| | 9 | - “WebContent physical footprint doesn’t go back down significantly after going from nytimes.com to a simple page (about:blank)” |
| | 10 | - When we say memory use is too high we look at Physical Footprint metric |
| | 11 | - When measuring memory: Beware of caches |
| | 12 | - Proper steps |
| | 13 | - Load about:blank |
| | 14 | - Get physical footprint with `vmmap` |
| | 15 | - Load nytimes.com |
| | 16 | - Go back to about:blank |
| | 17 | - Fire memory warning |
| | 18 | - Get physical footprint with `vmmap` |
| | 19 | - “Something is not going away” |
| | 20 | - Leaks? |
| | 21 | - Tool called “leaks”: `leaks <pid>` |
| | 22 | - Adding Quick & Easy “State of the world” Data Gathering |
| | 23 | - Handy too on Mac |
| | 24 | - `notifyutil -p` |
| | 25 | - Broadcasts notifications to processes, processes can register handlers |
| | 26 | - Handles: |
| | 27 | - org.WebKit.lowMemory |
| | 28 | - com.apple.WebKit.fullGC |
| | 29 | - com.apple.WebKit.deleteAllCode |
| | 30 | - com.apple.WebKit.Cache.dump |
| | 31 | - com.apple.WebKit.showAllDocuments* |
| | 32 | - com.apple.WebKit.showMemoryCache* |
| | 33 | - com.apple.WebKit.showPageCache* |
| | 34 | - * added in last 6 months |
| | 35 | - `notifyutil -p com.apple.WebKit.showAllDocuments` |
| | 36 | - Why aren’t they leaks? |
| | 37 | - Leaks tool looks for rooted graphs of objects |
| | 38 | - This is really “Abandonment” or “leaks” |
| | 39 | - So what objects are hanging around? |
| | 40 | - Object Counters |
| | 41 | - Is this a JS heap issue, or a C++ ref-counting issue? |
| | 42 | - Which world is the issue happening in? |
| | 43 | - Not always easy to tell |
| | 44 | - Do the JS* objects go away? |
| | 45 | - Did all the doc nodes get destroyed? |
| | 46 | - `notifyutil -p com.apple.WebKit.showAllDocuments` |
| | 47 | - Look at referencingNodeCount |
| | 48 | - JS Heap |
| | 49 | - GC |
| | 50 | - Visist s every object that JS knows about (as JSCell*), starting at GC roots |
| | 51 | - JSC::Heap, JSC::SlotVisitor, visistChildren() methods |
| | 52 | - Unvisisted objects can be GC |
| | 53 | - Doesn’t care about unrooted cycles |
| | 54 | - GC Heap Inspector |
| | 55 | - Heap Snapshots in Web Inspector |
| | 56 | - Not quite enough info, no C++ pointer objects |
| | 57 | - `notifyutil -p com.apple.WebKit.dumpGCHeap` |
| | 58 | - Checked-in: https://trac.webkit.org/r235271 |
| | 59 | - GC-related Document Leaks |
| | 60 | - DFG Scratch buffer |
| | 61 | - Exception backtraces |
| | 62 | - VM lastException |
| | 63 | - JSPerformanceObserverCallback: Strong<Function> |
| | 64 | - Strong references indicate GC problems |
| | 65 | - C++ leaks |
| | 66 | - Unbalanced ref()/deref(), retain reference cycles |
| | 67 | - `refCount 1` indicates a C++ leak |
| | 68 | - Ref tracking |
| | 69 | - Collate matching ref() and deref() via “tokens” |
| | 70 | - Ref Token Tracking |
| | 71 | - Fix Ref<> and RefPtr<> to store tokens |
| | 72 | - All ref() and deref() via Ref<> and RefPtr<> are tracked |
| | 73 | - RefTracker class example |
| | 74 | - Call stack records the ref |
| | 75 | - StackShot is your friend |
| | 76 | - Wrapper for WTFGetBacktrace() |
| | 77 | - Washable |
| | 78 | - Great for “accumulate all the calls stacks that did this thing” |
| | 79 | - Controllable stack depth |
| | 80 | - TrackedRefCounted<> |
| | 81 | - Dumping Unmatched Refs |
| | 82 | - `RefTracker::dumpRemainingReferences()` |
| | 83 | - RefTracker is powerful! |
| | 84 | - Sometimes dumps the only unmatched `ref()` callstack! |
| | 85 | - Easy to patch up manual `ref()`/`deref()` code paths by tracking RefTrackingTokens |
| | 86 | - webkit.org/b.186269 (reviewz plz) |
| | 87 | - Make it possible to track unbalanced ref()/deref() |
| | 88 | - RefTracker on Node |
| | 89 | - Give Node a RefTracker |
| | 90 | - Tracks Nodes, Elements, Documents |
| | 91 | - Now we can track Document refs |
| | 92 | - Hack the “com.apple.WebKit.showAllDocuments” callback to dump ref stacks for live Documents |
| | 93 | - You can turn it on and its not a huge perf hit |
| | 94 | - Making leaks not happen again |
| | 95 | - Used GCHeap Inspector |
| | 96 | - Fixed a leak |
| | 97 | - “Finding a regression in EWS is 1000x more efficient than finding it via a perf benchmark regression” — Simon Fraser |
| | 98 | - webkit.org/b/186214 |
| | 99 | - Isn’t this like the old “world leaks”? |
| | 100 | - Sort of is… |
| | 101 | - Leaks of world-level objects |
| | 102 | - Checking for Leaks in Testing |
| | 103 | - `./Tools/Scripts/run-webkit-tests --world-leaks` |
| | 104 | - How it Works |
| | 105 | - After each test |
| | 106 | - WKBundleGetLiveDocumentURLs() |
| | 107 | - Implications for run-webkit-tests |
| | 108 | - New failure type: “LEAK” |
| | 109 | - “LEAK” only relevant if you ran with `--world-leaks` |
| | 110 | - Leak checking is post-hoc |
| | 111 | - A test can pass, and then become a leak failure |
| | 112 | - Remaining Work |
| | 113 | - Add LEAK expectations for all platforms |
| | 114 | - Fix the causes of most common document leaks |
| | 115 | - Fix causes of false Leaks |
| | 116 | - Turn on by default |
| | 117 | - C++ Document Leaks |
| | 118 | - Web Animations |
| | 119 | - Document::removeFocusNavigationNodeofSubtree() |
| | 120 | - Content Filtering |
| | 121 | - IndexedDB |
| | 122 | - SVG text tests |
| | 123 | - WebFullScreenManager |
| | 124 | - C++ Document Lifetime Extenders |
| | 125 | - NavigationAction |
| | 126 | - Editor/Selection |
| | 127 | - ServicesOverlayController |
| | 128 | - DragController |
| | 129 | - To Do |
| | 130 | - When WebKitTestRunner detects a leak: |
| | 131 | - Dump unmatched refs when tests detect a leak |
| | 132 | - Dump a GC heap |
| | 133 | - Some auto analysis? |
| | 134 | - Halp |
| | 135 | - webkit.org/b/186214 |
| | 136 | - Now the memory problems are solved, right? |
| | 137 | - Not really, still some issues in vmmap |
| | 138 | - MALLOC zones |
| | 139 | - Dirty size column is interesting, WebKit MALLOC (411 MB) |
| | 140 | - “Where does all the memory go?” |
| | 141 | - webkit.org/b/186422 Create lots of different malloc zones for easier accounting of memory use |
| | 142 | - Created custom MALLOC zones for WebKit |
| | 143 | - Big ones |
| | 144 | - Vector capacity |
| | 145 | - HashTable |
| | 146 | - webkit.org/b/186698 Make it possible to track all sites that waste container capacity |
| | 147 | - Container Capacity Tracking |
| | 148 | - Give the Vector/HashTable an ID (can’t track by address) |
| | 149 | - Record allocation with a StackShot |
| | 150 | - On deallocation remove the record |
| | 151 | - Dump living capacity: |
| | 152 | - Aggregate by StackShot |
| | 153 | - Sort by most to least wasteful |
| | 154 | - Consider not using as many HashTables? |
| | 155 | - Maybe use different types of tables? |
| | 156 | - Memory Reduction |
| | 157 | - bmalloc space efficiency |
| | 158 | - Maybe as good as system malloc? Problem if using both, using one is fine |
| | 159 | - JSC optimizations for space efficiency (even with the JIT) |
| | 160 | - WebCore optimizations for space efficiency |
| | 161 | - Review Vector and HashTable growth policies |
| | 162 | - Resiable hashes (Robin Hood hashing?) |
| | 163 | - Vector shrinking |
| | 164 | - Convert from HashMaps and HashSets to other data structures |
| | 165 | - Adjust caching policies |
| | 166 | - Better Low Memory Handler |
| | 167 | - Don’t dirty more pages than you release |
| | 168 | - Organize data structures to release memory without visiting pages |
| | 169 | - More use of purgeable memory |
| | 170 | - WebProcess watermark (big 2.5 GB?) |
| | 171 | - Space-Efficient Class Layout |
| | 172 | - Micro-optimization? |
| | 173 | - `dump-class-layout -c Release WebCore FillLayer` |
| | 174 | - Look for `<Padding: n bytes>` entries |
| | 175 | - Also look at `<UNUSED BITS: n bits>` |
| | 176 | - std::optional causes these to get big |
| | 177 | - There’s been work on different optional types |
| | 178 | - Optional with a magic value (doesn’t waste any space) |
| | 179 | - Patch for a set of optionals in a bit field |
| | 180 | - Beware of enums |
| | 181 | - For enums, you should specify size |
| | 182 | - Big Stuff |
| | 183 | - Don’t just use all available memory |
| | 184 | - Manage cache sizes |
| | 185 | - Cache budgets |
| | 186 | - JS Heap size |
| | 187 | - Don’t Cause Leaks |
| | 188 | - Be careful with JS bindings code |
| | 189 | - JSC::strong<> is almost always wrong |
| | 190 | - Be careful with holding refs to Documents, elements |
| | 191 | - Avoid cycles |
| | 192 | - Avoid extending object lifetimes |
| | 193 | - Don’t write floaty classes |
| | 194 | - Shrink vectors, avoid unused inline capacity on heap-allocated objects |
| | 195 | - Do you really need to use HashTable? |
| | 196 | - Beware enums and std::optionals |
| | 197 | - Look for Leaks |
| | 198 | - Tools for investigating Leaks |
| | 199 | - cCache dumping |
| | 200 | - GC heap inspector |
| | 201 | - Ref token tracking |