Closed pages give their memory back whole, and JavaScript measured at 87
Every page now lives in its own region of memory, so closing it returns its memory in one piece, and the small leftovers pages used to leave scattered are handed back too: memory after closing pages dropped about another fifth on our test browse. DevTools shows exactly what each closed page left behind. JavaScript got faster again: our measured score against the reference JavaScript runtime on 42 real-world workloads went from 80 to 87, with loops that create or call functions, classes, array callbacks, integer math and Map and Set now running as compiled code.
Each page gets its own memory region. Everything a page allocates lands in memory only that page uses, so when you leave it, its memory goes back to your computer in one step instead of piece by piece, and Floati knows exactly (not by sampling) how much the page still leaves behind.
Memory & reading
Scattered leftovers are returned too. A page that closes can leave a few small objects spread across many blocks of memory, and each one used to keep its whole block alive: after a browse, 238 MB of blocks were being kept for 33 MB of objects. Floati now hands back the empty parts of such blocks (the way the reference browser's own allocator does) and takes them back only if they fill up again. Memory after closing all pages on our encyclopedia, code-hosting, news and framework-docs browse went from about 300 MB to 245 MB.
Tabs & interface
DevTools Page memory shows the exact bytes each closed page left behind, next to the live estimate.
JavaScript & JIT
Loops that create functions are compiled. A loop that makes a closure per iteration (for (let i ...) { handlers.push(() => i) }) and loops that call functions stored in an array (handlers[i]()) now run in compiled code, including machine code, with each iteration getting its own copy of i exactly as the standard says: 3.4 times faster on our benchmark.
JavaScript & JIT
Classes are compiled. new on a derived class (with super(...) and fields computed from the arguments), methods that call super.method(), and getters defined on a class run inline in compiled loops, and objects built at the same place share one layout from the start: the class benchmark is about 4 times faster and calling a method on each element of an array about 1.5 times.
JavaScript & JIT
Array callbacks are faster. map, forEach, filter, find, some, every and reduce no longer copy the whole array on every call when the callback provably cannot change it, and the loop over the elements now runs as machine code: about 1.7 times faster.
Memory & reading
Integer math stays in registers. Hot loops kept their counter in memory because of a rarely used conversion helper; that conversion is now computed inline for every value, and loops doing integer math such as hashing run about twice as fast.
JavaScript & JIT
Map and Set in loops. Loops calling Map get, set, has and delete and Set add, has and delete work directly on the collection's storage: about 1.7 times faster.
JavaScript & JIT
Array holes behave like the reference browser. In [1,,3], new Array(n), after length grows or after a write past the end, the missing indexes are now truly missing: 1 in [1,,3] is false, Object.keys and for-in skip them, and forEach, map, filter, some, every and reduce do not visit them (find and findIndex do, as in the reference browser), while every read still gives undefined.
Rendering
arguments.callee works: inside an ordinary function it is the function being called, and in strict code reading it throws a TypeError, as the standard requires.
JavaScript & JIT
Correctness fixes found by the new checks: array callbacks now see changes the callback makes to later elements and stop at a shortened length (they used to work on a stale copy), a method replaced or shadowed inside a compiled loop is now picked up in that same iteration, and o.__proto__ = ... inside a compiled loop now changes the prototype instead of setting a plain property.