图片在内存中的存储基于位图模式(通常也称作Bitmap),它把图片的长宽定义成多个像素点,每个像素点的颜色值有几个像素来保存,对那些尺寸比较大的图片一张可能就占据十兆左右的空间,应用只要多存储一些大尺寸照片就会导致内存溢出。Android的应用运行在JVM虚拟机上,每个JVM虚拟机进程分配的内存有限,这样才能保证多个应用同时运行时每个程序都有机会执行。系统究竟为每个应用分配的内存值大小是多少,Java提供了Runtime运行时类它可以提供JVM的内存信息。
Log.e(TAG, "Total Memory = " + Runtime.getRuntime().totalMemory());
Log.e(TAG, "Free Memory = " + Runtime.getRuntime().freeMemory());
Log.e(TAG, "Max Memory = " + Runtime.getRuntime().maxMemory());
Total Memory = 2754792
Free Memory = 1267952
Max Memory = 268435456
Runtime三个方法提供的数据都是JVM进程的内存数,其中totalMemory代表当前JVM从操作系统已经申请到的内存大小,freeMemory的值等于totalMemory减去已经使用了的内存大小,maxMemory代表JVM进程可以向操作系统申请的最大内存值。 从测试代码看当前申请totalMemory到的内存才2M多,而最大申请内存有268M那么大,如果应用继续执行需要更多的内存,totalMemory会不断地增大,最大增长到maxMemory的大小,之后继续申请内存就会导致内存溢出,JVM抛出OutOfMemory异常。
public void testMemory(View view) {
byte[][] array = new byte[1024 * 1024][];
for (int i = 0; i < 1024; i++) {
array[i] = new byte[1024 * 1024 * 4];
}
}
上面的内存申请代码首先定义了一个1024 * 1024长度的二维数组,之后为每个元素申请4 * 1024 * 1024也就是4M的内存空间,由于申请的内存始终都被array数组引用无法被回收,最终JVM抛出了OOM异常。从异常消息中可以看出系统限制了分配最大内存268M和前面获取值是一致的。
java.lang.OutOfMemoryError: Failed to allocate a 4194320 byte allocation with 2395752 free bytes and
2MB until OOM, max allowed footprint 268435456, growth limit 268435456
那么有没有方法增加最大分配内存大小呢,在AndroidManifest.xml文件的application节点有个属性android:largeHeap,配置属性值为true系统就会为应用提供更大的内存空间。
Total Memory = 2732016
Free Memory = 1202328
Max Memory = 536870912
java.lang.OutOfMemoryError: Failed to allocate a 4194320 byte allocation with 2133584 free bytes and
2MB until OOM, max allowed footprint 536870912, growth limit 536870912
重新运行前面的测试代码就会发现最大内存值变成了536M,比以前足足大了一倍,但largeHeap选项不建议一般的应用开启,试想如果所有应用都申请内存变大最终还是会导致内存不足的情况。
图片占用内存
应用中使用到的图片通常会放置在资源文件夹res/drawable-xxx、assets文件夹、本地缓存目录、用户相册,还有一些图片需要从网络上加载。首先来看从res资源文件夹加载图片实现,drawable文件夹通常看到的有drawable、drawable-hdpi、drawable-xxhdpi等多个文件夹,应用在不同尺寸的屏幕下会使用不同像素密度文件下的图片。如果同一副图片放在不同的像素密度资源文件夹下,将它加载到内存中它的大小是不是完全一样的呢?