[TIMOB-17249] iOS: ImageCache keeps uncompressed image data in memory
GitHub Issue | n/a |
---|---|
Type | Bug |
Priority | High |
Status | Closed |
Resolution | Invalid |
Resolution Date | 2014-07-01T21:53:52.000+0000 |
Affected Version/s | Release 3.2.3, Release 3.3.0 |
Fix Version/s | n/a |
Components | n/a |
Labels | n/a |
Reporter | Chris Bowley |
Assignee | Ingo Muschenetz |
Created | 2014-06-30T15:36:38.000+0000 |
Updated | 2014-07-01T21:53:52.000+0000 |
Description
When loading a remote image on iOS, the uncompressed image data is stored in memory for the duration of the app causing memory issues.
Specifically: an instance of ImageCacheEntry class is instantiated for every image fetched by URL. This holds a reference to a UIImage created from the image data on disk:
https://github.com/appcelerator/titanium_mobile/blob/master/iphone/Classes/ImageLoader.m#L76
As ImageCacheEntry objects are never released (AFAIK) image data is stored in memory for the duration of the app.
One possible solution is to only keep cached image data on disk and return [UIImage imageWithContentsOfFile:localPath] for -[ImageCacheEntry fullImage] instead of holding a reference to the UIImage object:
- (UIImage *)fullImage {
return [UIImage imageWithContentsOfFile:localPath];
}
This code snippet loads an image every second, running object allocations in Instruments shows the image data increasing (see attached screenshot).
function loadImage() {
var url = "http://dummyimage.com/600x400/" + getRandomColor() + "/" + getRandomColor() + ".jpg&text=Hello";
Ti.API.info(url);
$.imageView.image = url;
}
function getRandomColor() {
var letters = '0123456789abcdef'.split('');
var color = '';
for (var i = 0; i < 6; i++ ) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
$.index.open();
loadImage();
setInterval(function() {
loadImage();
}, 1000);
Attachments
File | Date | Size |
---|---|---|
image_cache_jira.png | 2014-06-30T15:36:38.000+0000 | 712789 |
Screenshot 2014-07-01 19.00.21.png | 2014-07-01T18:16:08.000+0000 | 220512 |
Hello, after doing a change in your code, i checked this running code at Instruments, without any sign of leak. Modified function:
Full test case:
Best Regards
Mauro, sorry if I didn't explain the issue clearly but I specifically didn't report this as a memory leak. Image data is cached and stored in memory for the lifetime of the application, whether or not the visible image view is destroyed. For each image fetched by URL we create an ImageCacheEntry object with a reference to a UIImage object containing the image data. This is held in memory. With or without nulling out the image view in JS, this reference is stored in memory for the life of the application. This does not result in a leak displayed in instruments because this is how the image cache has been implemented. However there is no way from JS to remove this cached data so it increases with every remote image fetched. My suggestion is to keep the cached image data on disk and only load into memory when required for display - see Description. Use the allocations instrument and you will see the number of ImageCacheEntry objects increasing and memory allocated to ImageIO_jpeg_Data increasing, as shown in my attached screenshot. I have run the code again with the additional line nulling out the image but the result is the same - see attached. Chris
Hello Chris! I tested again with your view (only tracking the allocations) and it's clear that the GC is kicking in and cleaning the ImageIO component (you will see a stair in the graph). Check the image here: http://picpaste.com/81ly5UL2.png From your screenshot, that is happening in iOS8. iOS 8 is not final yet, and likely to contain bugs (like a faulty GC). Best Regards
Hi Mauro I'm not sure why but I don't see any GC behaviour on 3.2.3/3.3.0 iOS 7 on simulator and iPhone 5. ImageCacheEntry objects and ImageIO continue to increase. I'm using your code but would you be able to post your project so I can be sure I'm running the same thing? You can get my test project here: https://www.dropbox.com/s/0c3jabu6cd2fz3g/ImageCacheTest.zip.
Hello [~cbowley], here is my project: https://drive.google.com/file/d/0B439umN-AOyxRHpyTnBybW9fM0U/edit?usp=sharing Best Regards
Mauro It looks like I simply haven't run the app for long enough for the cache to be purged - it took over 6 minutes to kick in. Sorry for raising the issue and thanks for investigating it. Chris