[TIMOB-6242] Android: V8 Memory Leak: views which contain backgroundImages not based packaged image assets
GitHub Issue | n/a |
---|---|
Type | Bug |
Priority | High |
Status | Closed |
Resolution | Fixed |
Resolution Date | 2012-01-09T16:47:24.000+0000 |
Affected Version/s | Release 1.8.0.1 |
Fix Version/s | Sprint 2011-46, Release 1.8.0.1 |
Components | Android |
Labels | branch-v8, module_memory, qe-testadded |
Reporter | Bill Dawson |
Assignee | Bill Dawson |
Created | 2011-11-19T13:39:58.000+0000 |
Updated | 2012-01-09T16:47:24.000+0000 |
Description
This happens only in V8, and only when the background image is based on something that is *not* a packaged asset. For example, it happens when the image is from a network url or from the device's sdcard.
Fail/Test Case
* Run the app.js below on an emulator. * Open DDMS, select the running app, and turn on heap updates (the green cylinder button). * In DDMS, click on the "Heap" tab. * In the app, click the "Show View" and "Remove View" buttons back and forth many times, and watch how the "Allocated" value on the "Heap" tab continues to grow and grow.Expected vs Actual Behavior
Each time "Remove View" is clicked, the visible view (with the background image) is unloaded from the window viawin.remove(view);
and the view
variable is set to null. It's *expected* that the view's memory footprint will reduce once it's removed, but what's *actually* happening is that memory usage continues to grow. (Specifically, we know that what's happening is that the bytes for the bitmap behind the backgroundImage of the view are not being released from memory.)
Titanium.UI.setBackgroundColor('#000');
var win = Titanium.UI.createWindow({
title:'Test',
backgroundColor:'#000',
exitOnClose: true
});
var view = null;
var btn1 = Ti.UI.createButton({
title: "Show View",
bottom: "5dp", height: "40dp", left: "10dp", width: "150dp"
});
btn1.addEventListener("click", function() {
btn1.enabled = false;
view = Ti.UI.createView({
backgroundImage: "http://www.appcelerator.com.s3.amazonaws.com/blog/images/frontpage/survey_hero_11112011.png",
top: "50dp", left: "50dp", bottom: "50dp", right: "50dp"
});
win.add(view);
btn2.enabled = true;
});
var btn2 = Ti.UI.createButton({
title: "Remove View",
bottom: "5dp", height: "40dp", left: "165dp", width: "150dp",
enabled: false
});
btn2.addEventListener("click", function() {
btn2.enabled = false;
win.remove(view);
view = null;
btn1.enabled = true;
});
win.add(btn1);
win.add(btn2);
win.open();
When a packaged asset is used as the source of a background, the end result is that
nativeDecodeAsset
(in BitmapFactory.cpp) is called:In that case the bitmap is always forced to be purgeable, no matter the options passed in. In other cases (sdcard, network url),
nativeDecodeStream
is called:In that case, purgeability is absolutely disallowed. That's the
false
value you see up there that is passed todoDecode
for theallowPurgeable
parameter. If you look atdoDecode
(too long to paste here), you'll see purgeability makes a lot of differences. I don't really understand it all and haven't looked deeper, but for example here's one snippet where it appears that if it's not purgeable then the allocator is specifically set to a "java allocator":What I definitely don't fully understand is why it makes a difference, Rhino vs. V8. The most striking difference to me is that when I view a V8 app in MAT, I see that our view proxies are all shown to be "[Native Stack](https://skitch.com/billdawson/gkdqk/memory-analysis-var-folders-9o-9o2efpbafear0jpshi374-ti-tmp-android3582123121634602581.hprof-eclipse-users-bill-projects-eclipse-workspace)", which makes it a "GC root". In Rhino, the view proxies are not GC roots, and their paths to GC roots lead to "androidy" things like view roots and handlers. I'd imagine this difference has something to do with it.
As additional test for the fix, we should be sure that we are still able to remove and re-add views successfully. To test that, run the app.js below. You should be able to remove/re-add in succession successfully and always get the blue-red-green embedding of views. Also, the "Click Me" button inside the green view should always show an alert of "Thanks".
Pull request ready: https://github.com/appcelerator/titanium_mobile/pull/743 Though this solution works and is probably good practice in any case, it doesn't address why there is this difference (see earlier comment) between V8 and Rhino.
Tested on Ti Studio 1.0.7.201112080131 Ti Mob SDK 1.8.0.1.v20111209102124 v8/rhino OSX Lion emulator 2.2 Tested both test cases and verified that the expected behavior is shown
Reopening/closing to add/remove labels