iOS: Memory leak on iOS when items in section are changed on ListView after ListView has been created.
(Not tested on Android.)
* Run the app below in Instruments and profile memory usage
* With every opening of the window containing the list view, 12 TiUILabelProxy and TiUIListItemProxy objects are allocated and never freed again.
* Only setting all the sections again on the list view via "listView.setSections()" (Variant 4 in code below) prevents a memory leak
var baseWin = Ti.UI.createWindow({backgroundColor: 'red'});
var b = Ti.UI.createButton({
title: 'Click',
color: 'red',
selectedColor: 'blue',
height: '100dp',
width: '150dp',
backgroundImage: 'appicon.png',
backgroundColor: 'green',
style: Titanium.UI.iPhone ? Titanium.UI.iPhone.SystemButtonStyle.PLAIN : 0
});
b.addEventListener('click', function(evt) {
var win = Ti.UI.createWindow({backgroundColor: 'white'});
// Create a custom template that displays an image on the left,
// then a title next to it with a subtitle below it.
var myTemplate = {
childTemplates: [
{ // Title
type: 'Ti.UI.Label', // Use a label for the title
bindId: 'info', // Maps to a custom info property of the item data
properties: { // Sets the label properties
color: 'black',
font: { fontFamily:'Arial', fontSize: '20dp', fontWeight:'bold' },
left: '60dp', top: 0,
}
},
{ // Subtitle
type: 'Ti.UI.Label', // Use a label for the subtitle
bindId: 'es_info', // Maps to a custom es_info property of the item data
properties: { // Sets the label properties
color: 'gray',
font: { fontFamily:'Arial', fontSize: '14dp' },
left: '60dp', top: '25dp',
}
}
]
};
var listView = Ti.UI.createListView({
// Maps myTemplate dictionary to 'template' string
templates: { 'template': myTemplate },
// Use 'template', that is, the myTemplate dict created earlier
// for all items as long as the template property is not defined for an item.
defaultItemTemplate: 'template'
});
var sections = [];
var fruitSection = Ti.UI.createListSection({ headerTitle: 'Fruits / Frutas'});
var fruitDataSet = [
// the text property of info maps to the text property of the title label
// the text property of es_info maps to text property of the subtitle label
// the image property of pic maps to the image property of the image view
{ info: {text: 'Apple'}, es_info: {text: 'Manzana'}, pic: {image: 'apple.png'}},
{ info: {text: 'Banana'}, es_info: {text: 'Banana'}, pic: {image: 'banana.png'}}
];
fruitSection.setItems(fruitDataSet);
sections.push(fruitSection);
var vegSection = Ti.UI.createListSection({ headerTitle: 'Vegetables / Verduras'});
var vegDataSet = [
{ info: {text: 'Carrot'}, es_info: {text: 'Zanahoria'}, pic: {image: 'carrot.png'}},
{ info: {text: 'Potato'}, es_info: {text: 'Patata'}, pic: {image: 'potato.png'}}
];
vegSection.setItems(vegDataSet);
sections.push(vegSection);
var grainSection = Ti.UI.createListSection({ headerTitle: 'Grains / Granos'});
sections.push(grainSection);
listView.setSections(sections);
var grainDataSet = [
{ info: {text: 'Corn'}, es_info: {text: 'Maiz'}, pic: {image: 'corn.png'}},
{ info: {text: 'Rice'}, es_info: {text: 'Arroz'}, pic: {image: 'rice.png'}}
];
// VARIANT 1: CAUSES MEMORY LEAK
grainSection.setItems(grainDataSet);
// VARIANT 2: CAUSES MEMORY LEAK
//grainSection.replaceItemsAt(0, 99, grainDataSet);
// VARIANT 3: CAUSES MEMORY LEAK
/*
var grainSection2 = Ti.UI.createListSection({ headerTitle: 'Grains / Granos'});
grainSection2.setItems(grainDataSet);
listView.replaceSectionAt(2, grainSection2);
*/
// VARIANT 4: NO MEMORY LEAK
/*
var grainSection2 = Ti.UI.createListSection({ headerTitle: 'Grains / Granos'});
grainSection2.setItems(grainDataSet);
sections = [fruitSection, vegSection, grainSection2];
listView.setSections(sections);
*/
var button = Ti.UI.createButton({
title: 'Close Window',
color: 'black',
height: '100dp',
width: '150dp',
backgroundColor: 'green',
});
button.addEventListener('click', function(evt) {
win.close();
});
win.add(listView);
win.add(button);
win.open();
});
baseWin.add(b);
baseWin.open();
Hello! Can you please remove the elements of the window, then null them after closing it? Check this sample: https://gist.github.com/mauropm/5923980 Right now, your code says "I will close this window, but I didn't null it, so I can reopen it if I want". Best, Mauro
Hi Mauro Thanks for your input. I have adjusted my test app in the following way:
The memory leak however still exists. Any other suggestions? Many thanks & regards, Philippe
Hello, this looks like a DUP of TC-3326 :) In the second example of click, please remove the event from the label before nulling it. Remember: in order to remove an event, you have not to use anonymous functions, but named ones, so it will be easier. Right now your object is holding in memory because it does have an event listener attached. BTW, the GC won't usually kick in all time. You need to repeat your testing a lot of times, so the memory reach a memory warning or similar, so that will force the GC to clean the memory. Best, Mauro
Hello Mauro No, this is not a duplicate. This is a slightly different case. Here, I update items in the ListView after the ListView itself has been created (see sections VARIANT 1-4 in the code). I do not have a 'click' event listener on the template here. Cleaning up the view did unfortunately not fix the memory leak. {quote} BTW, the GC won't usually kick in all time. You need to repeat your testing a lot of times, so the memory reach a memory warning or similar, so that will force the GC to clean the memory. {quote} I am aware of this. I have tested it quite extensively in my opinion. Do you have any further inputs? Many thanks & best regards, Philippe
I can confirm a memory leak exists with the ListView when you update sections. I've attached two files. 1) Create a default Alloy project. Replace its app folder with the contents of app.zip 2) Create a "classic" project. Replace its app.js file with the contents of the app.js.zip file. Run either and monitor with instruments. You will see the allocation of TiUILabelProxy, TiUIListItemProxy, and TiUILabel grow continuously as you repeatedly click Load List and Clear List. Issue was raised today in the Q&A at http://developer.appcelerator.com/question/162348/alloy-listview-memory-leak
I can also confirm this. The bad thing is, even though this belongs to a separate Window and a separate Alloy Controller, the memory is not released after the Window is closed and the controller is nulled. The memory that was allocated by ListView seems not to be released at all regardless of what I do (nulling the listview, the window, etc). Consequently, Alloy binding is also problematic. If you bind any Alloy collection to a ListView, it will have memory leak.
I think this is a critical bug. You cannot really use Listviews because of the leak unless it's static. The app crashes pretty fast if your ListItems are bulky (TiUIListItem, TiUIListItemProxy, TiUIListSectionProxy, etc are never released even when you remove all the references). It is also buggy in 3.2.0
Pull pending master - https://github.com/appcelerator/titanium_mobile/pull/5358 3_2_X - https://github.com/appcelerator/titanium_mobile/pull/5359
@Vishal, I dont think this is fixed. I have major memory leaks in my app, using listview bindings. I tried to use 3.2.2.v20140221161255, which should include this fix right? Here is my leaking use case: https://gist.github.com/viezel/07d4ac387609bba3630d Thats still a bug...
[`viezel]the memory leak we fixed was against test case reported in this bug. We are not sure if you are encountering the same issue as reported in this bug. We cannot determine what your exact memory leak case is from The excerpt of test case you have pasted above. If you could give us a more complete test case, we would be able to track down the bug more precisely. Cheers Sabil
Closing ticket as fixed. Using the app.js.zip attachment, I was able to verify TiUILabelProxy, TiUIListItemProxy, and TiUILabel will not grow continuously via Instruments. Tested on: Appcelerator Studio, build: 3.2.1.201402061120 SDK build: 3.2.2.v20140221161255, 3.3.0.v20140224114904 CLI: 3.2.1 Alloy: 1.3.1 Xcode: 5.0.2, 5.1 beta 5 Devices: iphone 4s (6.0.1), iphone 4 (7.0.6), iphone 5 (7.1)
I have just tried the Q&A test case that Tim referenced (http://developer.appcelerator.com/question/162348/alloy-listview-memory-leak) - this was an entry that I raised. And in my Alloy project I still get the same results with Instruments. I am testing with App Studio: 3.2.1.201402041146 (vanilla install on a clean machine) SDK:3.2.2.v20140214120903.802eaf8 XCODE:5.0.2 (5A3005) Device: Iphone 3.5 inch Retina (emulator - 7.0.3)
My project can be found in Git - https://github.com/magnatronus/listviewleak
@Sabil - Stene Rogers repo also covers my leak.
[~viezel], [~magnatronus]: Were you guys able to try with the latest 3.2.2 SDK build (3.2.2.v20140221161255)?
Hi, just tried my test project with SDK 3.2.2.v20140221161255 and Instruments now shows that no increase in the 'Live' proxy objects :-)
Wilson Luu - Nope its not working with 3.2.2.v20140221161255. Ive created a test app with a simple test case: https://dl.dropboxusercontent.com/u/2132088/LeakApp.zip This is the result: http://postimg.org/image/qcw3ktm3t/ Its just keeps leaking..
[~viezel] I am able to reproduce your issue now! It looks like the issue at hand applies only to iOS simulators and not on iOS devices. I will file another ticket for your issue later today and link it to this ticket.
[~viezel] While I was in the process of writing your ticket, I was not able to reproduce your issue any more with your app on iOS simulator. The only difference from this morning and this evening is the text are not appearing in the ListView (see https://db.tt/GifZiOlp). On the side of precaution, I tried reproducing the issue using Steve's app (https://github.com/magnatronus/listviewleak) on an iOS simulator, and still was not able to reproduce the memory leak issue that I saw this morning or the original bug behavior.