| 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 |