> Testing memory usage on mobile Safari

memory_chip

How many elements can I load into the iPhone browser before it crashes? I’ve been asked this question many times. We don’t have memory management on Safari mobile browser, and we don’t know when it is going to crash. This level of uncertainty bothered me enough that I decided to do some (admittedly empiric) tests.

UPDATE: Please note that the following post was related to iOS 3.2. Memory management greatly improved in recent mobile webkit versions. The bottom line is: to let the browser peacefully handle your device memory do not alter the DOM with innerHTML, but use append/removeChild instead.

——

It is said that you can allocate 10M on the iPhone browser, from my tests it turned out that this number is closer to 6M.

I created a 100k image and injected the DOM with the following code:

var i = 0;
function newPic () {
	var el = document.createElement('img');
	el.src = 'image.jpg?v=' + i++;
	el.onload = function () { newPic() };
	document.body.appendChild(el);
}

Before all tests I cleared the browser cache and hard-booted the device. The result is: the iPhone is able to load exactly 63 images before it dies. And this is valid for an old first gen iPod touch up to the iPad.

If the 10M limit is true, it means that a 100k image takes 150k of actual browser memory. The DOM is of course incrementing in size image after image but I was not expecting a 50% of overhead.

I repeated the test with 10k images and 4k images and the results are consistent: I could allocate ~600 10k images and ~1500 4k images.

The good news is that jpg, png24 or png8 makes no or very little difference in the overall memory allocation (so basically: use alpha channel PNGs freely).

Your application of course needs more than just images. To stress test the device I added translated3d() to each image, and the result is not encouraging.

The code first:

var i = 0;
function newPic () {
	var el = document.createElement('img');
	el.src = 'image.jpg?v=' + i++;
	el.style.webkitTransform = 'translate3d(0,0,0)';
	el.onload = function () { newPic() };
	document.body.appendChild(el);
}

Result: 15 images before crash. The bad news is that 15 images is valid only for a completely clean system (ie: unrealistic scenario). With standard usage I’ve got from 9 to 12 images before crash.

Fortunately memory de-allocation works very well, and with the following code…

var i = 0;
function newPic () {
	var el = document.createElement('img');
	el.src = 'image.jpg?v=' + i++;
	el.style.webkitTransform = 'translate3d(0,0,0)';
	el.onload = function () { newPic() };
	document.body.appendChild(el);
	if (i>0) document.getElementsByTagName('img')[i-1].style.webkitTransform = '';
}

… you get back 63 images.

What about Android? Well, things get a little bit more complicated there. Generally speaking it seems that on my HTC Desire (that should be close to the Nexus One performance-wise) I can allocate up to 20M, but memory management seems completely different on the G-phone. In one of my tests the browser was still working after 2000 100k images, I stopped the test and went back to the home screen which crashed. On iPhone the browser seems better sandboxed.

In the coming days I’ll make more tests with background images and gradients. Stay tuned.

/Share the joy

/Reactions

  • Thanks for sharing your findings, Matteo. :) Love the trick for removing the webkitTransform property.

  • Hi Matteo,
    this mean that in an “open” enviroment is up to developer to better use the memory with less care of the system overall stability.

    I still prefer to have someone, like Apple and Microsoft, that take care of the entire platform. Freedom is not always good for customer.

    Did you agree?

    • If you want to develop for the web, you have little alternatives.

      • Author: Toh Yit Ming
      • Posted on: 2012/01/13
      • At: 04:28

      i do agree.. which i always think that too much freedom to for the developer is not beneficial to general smart phone users (case will be different for “Power” users).
      i always think that control that iOS have imposed using the sandbox is one of the very good way to protect the OS from using too much memory or other hacks that might hard the whole system.
      but in another hand, when all these restrictions are being imposed, the OS sometimes become too rigid if the OS programmer does not provide alternative to access to some of the features.
      so we gotta strike a balance and improve.

    • Author: Jon
    • Posted on: 2010/10/21
    • At: 23:04

    Does anyone know how to remove the images from the memory / DOM?
    Tole from Jaipho accomplishes this, but I haven’t figured out how yet..

    • probably just parent.removeChild(img) ?

      • Author: Jon
      • Posted on: 2010/10/22
      • At: 18:07

      tried like this:

      function newPic () {
      var el = document.createElement(‘img’);
      el.src = ‘image.jpg?v=’ + i++;
      el.style.webkitTransform = ‘translate3d(0,0,0)’;
      el.onload = function () { newPic() };
      document.body.appendChild(el);
      setTimeout( function() {
      document.body.removeChild(el)
      }, 3000);
      }

      still crashes @ about 15 after removing the elements..

    • remove the el.style.webkitTransform property before deleting the element.

      • Author: JGG
      • Posted on: 2010/12/03
      • At: 13:30

      unfortunately parent.removeChild() doesnt remove the image from memory. Only thing working is image.src = ‘blank.png’ (loading a tiny image will free up memory)

    • Author: ChrisH
    • Posted on: 2010/12/02
    • At: 01:23

    Hi Matteo,

    I was curious about these tests. What are the dimensions of the jpeg you’re using?

    I would assume that Translate3D is attempting to keep a buffer in memory. The size of that buffer is going to be (ostensibly) Width x Height x4(RGBA) bytes. If it’s making use of hardware acceleration, the buffer could be rounded up to the nearest power of two dimension. ( 2×2, 4×4, 8×8, 16×16… As you would if you were allocating textures in OpenGL).

    On an iPhone4 the width and height could be doubled to take into account the retina display pixel doubling.

    • good point. The image was 800×789. I’ll make other tests with other dimensions.

      • Author: JGG
      • Posted on: 2010/12/03
      • At: 13:31

      That’s also my experience, that dimensions matters …

    • Author: JGG
    • Posted on: 2010/12/03
    • At: 13:45

    Loading images as background through CSS takes up less memory. In general, avoid img tags on mobile Safari …

  • There seems to be many differences between Mobile Safari and using UIWebView…

    I currently using (the under appreciated and little known) MacGap created by @Shazron to port my iOS apps to desktop for distribution in the OSX App Store….

    it would be great to run these tests for UIWebView.

    LINK: https://github.com/shazron/phonegap-mac

    • Author: Morten F. Thomsen
    • Posted on: 2011/08/24
    • At: 16:34

    Old post, but this might be of interest. If you use base64 encoded images, iOS somehow does not perform the garbage collection properly – causing Safari to crash at what seems like random places.

    Using image file instead of base64 encoded data makes it possible to read unlimited amounts of images succesively.

    Just thought SOMEONE might save some time with this – it took as a few months to solve this actually!

    • Author: Toh Yit Ming
    • Posted on: 2012/01/13
    • At: 04:27

    i do agree.. which i always think that too much freedom to for the developer is not beneficial to general smart phone users (case will be different for “Power” users).

    i always think that control that iOS have imposed using the sandbox is one of the very good way to protect the OS from using too much memory or other hacks that might hard the whole system.

    but in another hand, when all these restrictions are being imposed, the OS sometimes become too rigid if the OS programmer does not provide alternative to access to some of the features.

    so we gotta strike a balance and improve. :)