前言
因为公司APP会在Ipad mini(运行内存为512M)上会崩溃,在其他设备上能正常运行,在instrument上调试发现在崩溃页面占用内存暴涨,导致程序崩溃。调试发现主要原因是合成大尺寸图片时,合成图片的位图未被及时释放,VM CGRaster data中存在多张合成大图的位图,导致程序崩溃。
Instruments中的Allocations
Allocations工具用于检查程序的内存分配。其中Allocation主要分为All Heap及All Anonymous VM 两部分。All Heap代表程序真实占用的内存,而All Anonymous Vm代表系统为程序分配的虚拟内存。VM由以下三部分组成:
- VM:ImageIO_PNG_Data 主要用于系统缓存图片
- VM:CG raster data 主要用于缓存光栅化图片。如位图
- VM:CoreAnimation 主要用于核心动画的缓存。
VM:CG raster data导致内存暴涨问题
加载5120*2880像素的大图
//测试cg raster data
- (void)__rasterDataImage {
NSString *pic3Path = [NSBundle.mainBundle pathForResource:@"pic3" ofType:@"jpg"];
UIImage *pic3 = [UIImage imageWithContentsOfFile:pic3Path];
UIImageView *imageView1 = [[UIImageView alloc] initWithImage:pic3];
UIGraphicsBeginImageContextWithOptions(pic3.size, YES, 1);
[imageView1.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *pic1Img = UIGraphicsGetImageFromCurrentImageContext();
// CGContextClearRect(UIGraphicsGetCurrentContext(), imageView1.frame);
UIGraphicsEndImageContext();
UIImage *img = [UIImage imageWithData:UIImagePNGRepresentation(pic1Img)] ;
UIImageView *imageView = [[UIImageView alloc] initWithImage:img];
[self.view addSubview:imageView];
}
测试结果

注释UIImage *img = [UIImage imageWithData:UIImagePNGRepresentation(pic1Img)] ;
将
UIImageView *imageView = [[UIImageView alloc] initWithImage:img];
替换为
UIImageView *imageView = [[UIImageView alloc] initWithImage:picImg];
测试结果如下

结果分析
第一幅结果图中的VM:ImageIO_PNG_Data用于需要显示的大图位图数据。(采用imageWithContentsOfFile系统不会一直缓存,当无指针引用该大图时,该ImageIO_PNG_Data中的此位图会自动释放。而采用imageNamed系统会缓存该大图)
第二幅测试结果图中的VM:CG raster data大小为56.25MB = 5120 * 2880 * 4B / (1024 * 1024)。说明raster data中存放的是大图的位图。
结论
当合成图片时若该图不需理解显示,不应该直接返回该UIImage对象,若外部引用该UIImage对象会导致该位图一直不会被释放,占用大量内存。而应对其做处理将其转换NSData在生成新的UIImage对象返回回去
补充
VM:ImageIO_PNG_Data中的位图创建时机
//测试代码
- (void)__loadBigPngImage {
NSString *pic1Path = [NSBundle.mainBundle pathForResource:@"pic4" ofType:@"png"];
UIImage *pic1 = [UIImage imageWithContentsOfFile:pic1Path];
UIImageView *imageView1 = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, pic1.size.width, pic1.size.height)];
imageView1.image = pic1;
self.imgView = imageView1;
//self.view insertSubview:imageView1 atIndex:0];
}
测试结果

取消self.view insertSubview:imageView1 atIndex:0];的注释
测试结果

结论
从上测试效果可得,如果图片是PNG图片,那么Image_PNG_Data分配内存给位图是在图片需显示在界面上。
存在的疑问
- iphone/ipad设备的虚拟内存功能是否开启,假如虚拟内存功能开启,内存超过512M程序应该也不会崩溃。但是如果没有开启虚拟内存为什么All anonymous VM 分配了内存的。(猜测All anonymous VM应该是分配到RAM上的,才会导致崩溃。我仅找到了如何通过越狱开启iphone/ipad虚拟内存功能,求大佬解释)
网友评论