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
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 poolwould this work, or would this be counterproductive to the solution you mentioned?
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();
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.
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.
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.
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.
Your Answer
Think you can help? Login to answer this question!