This is a solution to your memory woes

You must Login before you can answer or comment on any questions.

Hi guys,

My team recently came across a serious memory issue in our project. So like any good programmer, we researched the forums for an answer. Our search took us to using sub-contexts as a possible solution.

Unfortunately, because of the dynamism of our project, sub-contexts made it tremendously harder to implement. Soon, we ran across concurrency issues that were caused by a race-condition for access to our global data store.

For a few days we pondered on solutions and finally tonight, we've developed a solution that WILL work for any memory problems and a project of any size.

We've discovered that by creating a blank window which runs in its own sub-context, then adding any views we wished to rid into the blank window and closing it, will, definitively, free up all associated memory. Think of it as an auto-release pool of sorts. As we create objects for our views, we add them to the display, and when we are done with them, we throw the views into the pool, and we empty the pool by closing it. Then we reopen the "pool" so we get to use it again. SIMPLE!

Hope this works for you guys, let me know if you would like to see a code sample.

SDK: 1.6.0

— asked 2 years ago by Michael Peng
9 Comments
  • +1 for a code sample ;)

    — commented 2 years ago by Kosso .

  • this looks just brilliant. has anybody from Appcelerator weighed in on this method?

    — commented 2 years ago by Duncan Mapes

  • Thank you very much, this solved my memory problems I was having. Worked like a charm!

    — commented 2 years ago by Andreas Oberg

  • Show 6 more comments

12 Answers

Accepted Answer

big thanx for sharing this!

did anyone try to create a js-class for an example:

var pool = new Class_Autorelease(); // a js-class based on your code
pool.release(my_memeating_beast); // this adds objects to the garbage
pool.release(hungry_too);
 
pool.clear(); // this clears and recreates the pool
would this work, or would this be counterproductive to the solution you mentioned?

— answered 2 years ago by Christian Sigl
answer permalink
8 Comments
  • Is it possible that you could share the class you have created? It looks very interesting.

    — commented 2 years ago by James -

  • I create one that seems to work... using Michael Pengs example

    var MemoryPool = function() {
        var _window;
        /*
         Here we make our "auto-release" pool. It's simply a window.
         We hide it upon creation so it won't interfere with our view hierarchy.
     
         5/3/2011: It seems that the window does not need to be a subcontext, just a regular window will do.
         */
        this.init = function() {
            _window = Ti.UI.createWindow();
            _window.hide();
            _window.open();
        }
        // This is where we clear out the memPool by closing it then reopening it again.
        this.clean = function(obj) {
            if(obj instanceof Array) {
                var arLen=obj.length;
                for ( var i=0, len=arLen; i<len; ++i ) {
                    // We then stick the entire view into the pool
                    _window.add(obj[i]);
                }
            } else {
                // We then stick the entire view into the pool
                _window.add(obj);
            }
            Ti.API.info('Cleaning MemoryPool.');
     
            // We empty the pool by closing it.
            _window.close();
     
            // We recreate the window again for the next object
            this.init();
        };
        this.init();
    }

    — commented 2 years ago by Chris Whittle

  • what is the argument "obj" here? mywindow.children?

    — commented 2 years ago by dev 1605

  • Show 5 more comments

Here you go guys. Just tested on 1.5 and it works as well. Let me know how it goes for you eh :)

// This is the main window for this app
var rootWindow = Ti.UI.createWindow
({
    fullscreen: true,
    backgroundColor:'white'
});
rootWindow.open();
 
// This is the root view we will be adding stuff into
rootView = Ti.UI.createView
({
    height: 100,
    bottom: 0,
    backgroundColor: 'orange'
});
rootWindow.add(rootView);
 
/*
    Here we make our "auto-release" pool. It's simply a window.
    We hide it upon creation so it won't interfere with our view hierarchy.
 
    5/3/2011: It seems that the window does not need to be a subcontext, just a regular window will do.
*/
var memPool = Ti.UI.createWindow();
memPool.open();
memPool.hide();
 
// This is where we clear out the memPool by closing it then reopening it again.
var clearMem = function()
{
    Ti.API.info('Closing memPool.');
 
    // We first remove the view from our main display window
    rootWindow.remove(rootView);
 
    // We then stick the entire view into the pool
    memPool.add(rootView);
 
    // We empty the pool by closing it.
    memPool.close();
 
    // We reset the pool by reopening it again.
    memPool = Ti.UI.createWindow();
    memPool.open();
    memPool.hide();
 
    // We recreate the main view again, since it was removed and we want to use it again.
    rootView = Ti.UI.createView
    ({
        height: 100,
        bottom: 0,
        backgroundColor: 'orange'
    });
    rootWindow.add(rootView);
};
 
// This is just a button for us to add an arbitraty large amount of things in.
var adder = Ti.UI.createButton
({
    title: 'Add shit',
    top: 0,
    left: 0,
    width: 100,
    height: 50
});
adder.addEventListener('click',
function()
{
    Ti.API.info("Adding stuff");
    for(var i = 0; i < 6; i++)
    {
        // This is just a random image that we've made arbitrarily large for testing purposes
        aView = Ti.UI.createImageView({image:'P9180046.JPG', height: 1024, width: 23465});
        rootView.add(aView);
 
        // We update the memory counter we display on screen.
        disp.text=Ti.Platform.availableMemory;
    }
});
rootWindow.add(adder);
 
// This is the button that when clicked, will invoke our clearMem method up top.
var remover = Ti.UI.createButton
({
    title: 'Remove stuff',
    top: 0,
    right: 0,
    width: 100,
    height: 50
});
remover.addEventListener('click',clearMem);
rootWindow.add(remover);
 
Ti.API.info(Ti.Platform.availableMemory);
 
// This is the memory counter that is visible on the screen.
var disp = Ti.UI.createLabel
({
    top:0,
    height: 50,
    width:300,
    text: Ti.Platform.availableMemory
});
rootWindow.add(disp);
 
 
// We check up on the memory status every second
setInterval
(
    function()
    {
        Ti.API.info(Ti.Platform.availableMemory);
        disp.text=Ti.Platform.availableMemory;
    },
    1000
);

yeh this would be good, and example would be good!!! when you release an object do you meant to close it after the user has finished with the view!!!

viewObject.close();

— answered 2 years ago by suliaman saleh
answer permalink
3 Comments
  • That's pretty much what happens. Say I'm done with objX ..

    memPool.add(objX);
    memPool.close();

    — commented 2 years ago by Michael Peng

  • I am a bit confused. When am I "done" with an object? At the bottom of a page? Thankful for all input!

    — commented 2 years ago by James -

  • when you intend to close a window for example, daniel

    — commented 2 years ago by dev 1605

Hi Michael, Thanks for this post. But have a confusion, where do i need to add this code. I have application with 3 different tabs and all are creating new windows using url after i get back json data from web service. Also in one main tab we create about 6 new windows to get to final detail page. Is the code meant for app.js only or on all the js files that i have. Any explanation for this would greatly help.

Thanks.

NB: SDK 1.7.5 on Android, haven't tried any other SDK versions and don't know if this affects iOS - This doesn't necessarily answer all memory leaks/out of memory scenario's but certainly this one caused me a big headache and the workaround is NOT obvious at all. See my comments on 'Fernan Delgados' post above for the background but basically if you use the 'backgroundImage' property of any type of view and you then 'remove' that view from it's parent you will get a lot of native memory left unfreed until the next GC. This can result in you're app getting OutOfMemory errors in the log of even a 'Force Close'.

Long story short to workaround this issue you can do one of two things:

1 - If you don't need your image stretched to fit the view then use an 'ImageView' and set the 'image' property instead of the 'backgroundImage' property. Then when you want to remove your view and free the memory be sure to remove it from it's parent view, set the 'image' property to empty string and then 'null' your reference variable. e.g.

win.remove(imgView);
imgView.image = '';
imgView = null;
2 - If you do need your image stretched to fit then because 'ImageView' maintains aspect ratio you have to use a bit of a hack. Basically you can use a 'Button' view but make it disabled (i.e. enabled = false), set 'style = 0', and 'width' and 'height' to 100% otherwise it won't expand to fill the parent. Then use the 'backgroundDisabledImage' property to set your desired image. Then you want to remove your view and free the memory it's the same as above but you set the 'backgroundDisabledImage' to an empty string, e.g.
win.remove(button);
button.backgroundDisabledImage = '';
button = null;
NB: For tracking this problem down the DDMS tools was absolutely essential. Strongly recommend you know how to use this tool effectively for investigating memory leaks.

This sounds interesting. Can you provide an example?

An example would be amazing this has been holding my app back for weeks! (web services)

Thank you for the example.

However, I tried to implement your solution down to the detail.

Here's my problem:

When I flip through pages (I've created a pageflipper) the memory goes down for every page (even though I clear earlier views with your clearMem method).

The memory goes all the way down to 2-3MB and sometimes it desides to crash and other times it releases all the way up to 100MB again.

Why doesn't it release dynamically?

Thank you.

— answered 2 years ago by John Johnson
answer permalink
5 Comments
  • Could u please attach code John? Thanks

    — commented 2 years ago by Michael Peng

  • Do you have an e-mail I can contact you at? I'm not allowed to show my code in the public.

    — commented 2 years ago by John Johnson

  • John,

    I assume you are under a NDA? In this case, please be aware that I will not be able to bind myself to any contracts. If this is acceptable for you, then please provide me with your email address and I shall email you promptly. Cheers.

    M

    — commented 2 years ago by Michael Peng

  • Show 2 more comments

Hey,

My android app was showing force close alert randomly so I added window.close() on back button click event. And now it looks like it is releasing the memory for the view elements on click of menu button on android device.

win.addEventListener('android:back', function(){ win.close(); });

This method works in a single context with SDK 1.7.x, but not with 1.6.x, right?

using this method, do you still need to null out variables and remove event listeners? or does this clear everything for you?

Using DDMS i found that Memory allocated to application is get increases as i am navigating screen by screen and after some time it crash application with force close message/Fail to load resources. Even i have remove all objects from window and after that assign null to it. i.e window.remove(viewname); viewname=null; window.close(); window=null. Please help Me OUT.

— answered 1 year ago by Fernan Delgado
answer permalink
3 Comments
  • I'm getting a similar issue, I'm using the memory pool cleanup method above but memory still growing (not VM heap but actual native memory as visualised in the 'SysInfo' tab in DDMS. I find if I use the 'Cause GC' button on the 'VM Heap' tab in DDMS the memory does get freed.

    I think what is happening here is that native memory allocated by Titanium is not being freed because the VM hasn't garbage collected the Ti proxy objects to which the memory relates. This causing me a real headache and at the moment I don't have a decent solution :(

    — commented 1 year ago by Andy Potter

  • OK, I've narrowed the source of my issue down to the 'backgroundImage' property. It seems if you use this on a view (any kind of view, plain, button, imageview, etc) and then you remove the view and null the reference you get a nice big leak in native memory. This leak does get freed when the GC runs, but because it leaks so much (nearly 1MB per view in my case), you can easily get the device to run out of memory and force close your app before the GC kicks in (NB: You don't always get killed, sometimes you just start getting OutOfMemory exceptions in the log).

    Changing my app to use 'ImageView' instead of a plain 'View' AND using the 'image' property instead of the 'backgroundImage' property fixes the leak. However this doesn't entirely help me because I need my images stretched to fit the screen, but the ImageView always maintains aspect ratio if you specify the image via the 'image' property.

    So I'm still a bit stuck, the investigation continues...

    — commented 1 year ago by Andy Potter

  • Andy

    Please see my response here.

    If this does not resolve it, please open a new question, so that other may find it easily.

    Cheers

    — commented 1 year ago by Paul Dowsett

Your Answer

Think you can help? Login to answer this question!