After fixing a very annoying GC trigger problem I noticed that our heap doesn’t shrink to its original size. During startup we usually have 20 – 30MB in our JavaScript heap. After random surfing I noticed that it doesn’t go back even if I close all tabs and trigger the GC many times. The heap size stays at about 100MB. There are two possible reasons for that: 1) leaks and 2) fragmentation.

We allocate 1MB chunks from the OS and once they are completely empty, we can return them to the OS. Our problem was that we allocate all sort of objects in these chunks and we didn’t use obvious life-time information during allocation. A short profiling showed that after random surfing we end up in a situation where 30% of our 1MB chunks are only alive because on arena or 4KB are used. That’s terrible!

I implemented a patch that separates long and short-lived objects by just placing system and user objects into separate chunks. Long lived objects are all system objects that are created by the browser and not from a web-page and all immutable strings that are shared across the whole JavaScript VM. The main advantage of the patch is that single long-lived objects don’t keep whole 1MB chunks alive and we can return empty chunks to the OS much quicker.

The outcome was amazing! Measurements show that we reduce the memory footprint of the JavaScript heap by 30% on average during regular surfing and 500% if we close all tabs after surfing. In numbers…

Closing all tabs after surfing:

  • 108,003,328 B — js-gc-heap (without patch)
  • 20,971,520 B — js-gc-heap (with patch)

Memory footprint of the whole browser:

  • 310,890,496 B — resident (without patch)
  • 219,856,896 B — resident (with patch)

WOW a major improvement for all Firefox users that is already in the nightly and aurora builds. It will be shipped with the Firefox 7 release for everybody!