Android 内存泄漏和OOM分析(二)
来源:互联网 发布:每日龙虎榜数据 编辑:程序博客网 时间:2024/05/22 16:55
接下来我们来讨论OOM的问题。
在做美食生活的项目的时候,有个功能是拍照上传图片的功能,没有对图片进行压缩,直接进行了上传,
11-21 22:05:28.774: E/dalvikvm-heap(13216): Out of memory on a 10485476-byte allocation.11-21 22:05:28.784: E/AndroidRuntime(13216): java.lang.OutOfMemoryError
好了,出现了我们的OOM了!
一般造成OOM的最大的原因是Bitmap,Bitmap,Bitmap重要的事情说三遍!
一般的OOM经常出现在加载很多图片或者是加载高清大图的时候!当一个app启动后,虚拟机不停的申请内存资源来装载图片,当超过内存上限时就出现OOM。这个现象如果你在跑monkey进行测试的时候更容易出现!
Android的APP内存组成:
APP内存由 dalvik内存 和 native内存2部分组成,dalvik也就是java堆,创建的对象就是就是在这里分配的,而native是通过c/c++方式申请的内存,Bitmap就是以这种方式分配的。(android3.0以后,系统都默认通过dalvik分配的,native作为堆来管理)。这2部分加起来不能超过android对单个进程,虚拟机的内存限制。
从代码中获取每个手机的内存限制大小:
ActivityManager activityManager = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE) activityManager.getMemoryClass();
而对于head堆的大小限制,可以查看/system/build.prop文件。
dalvik.vm.heapstartsize = 5m dalvik.vm.heapgrowthlimit = 48m dalvik.vm.heapsize = 256m
注: heapsize参数表示单个进程heap可用的最大内存,但如果存在以下参数”dalvik.vm.headgrowthlimit =48m”表示单个进程heap内存被限定在48m,即程序运行过程实际只能使用48m内存。
怎么查看APP内存分配情况?
1 在我们的AS中可以通过Monitor监控得到
2 在App里面我们可以通过totalMemory与freeMemory:
Runtime.getRuntime().freeMemory() RUntime.getRuntime().totalMemory()
3 adb shell dumpsys meminfo com.android.demo
这个是adb命令了,com.android.demo是包名。不会的可以去看我的另一篇博客:ADB 常用命令及详解
常见避免OOM的几个注意点:
适当调整图像大小 。因为手机屏幕尺寸有限,分配给图像的显示区域有限,尤其对于超大图片,加载自网络或者sd卡,图片文件提及达到几M或者十几个M的:
加载到内存前,先算出该bitmap的大小,然后通过适当调节采样率使得加载的图片刚好,或稍大在手机屏幕上显示就满意了:
BimtapFactory.Option opts = new BitampFactory.Option(); opts.inJustDecodeBounds = true ; opts.inSampleSize=computeSample(opts, minSideLength, maxNumOfPixels); // Android 提供了一种动态计算的方法 computeSampleSize opts.inJustDecodeBounds = false ; try { return BitmapFactory.decodeFile(imageFile, opts); } catch (OutOfMemoryError err){
图像缓存
在listview或Gallery等控件中一次性加载大量图片时,只加载屏幕显示的资源,尚未显示的不加载,移出屏幕的资源及时释放,采用强引用+软引用2级缓存,提高加载性能。缓存图像到内存,采用软引用缓存到内存,而不是在每次使用的时候都从新加载到内存。
及时回收图像 。
如果引用了大量的Bitmap对象,而应用又不需要同时显示所有图片。可以将暂时不用到的Bitmap对象及时回收掉。对于一些明确直到图片使用情况的场景可以主动recycle回收
App的启动splash画面上的图片资源,使用完就recycle。对于帧动画,可以加载一张,画一张,释放一张。
不要在循环中创建过多的本地变量 。
慎用static,用static来修饰成员变量时,该变量就属于该类,而不是该类实例,它的生命周期是很长的。如果用它来引用一些内存占用太多的实例,这时候就要谨慎对待了。
重点要来了!!!
对于图App使用图片时避免OOM的几种方式有三四种,我只讲一种:
LruCache + sd的缓存方式 :
很多图片处理框架和网络请求框架都已经做了三级缓存技术,所以在使用框架加载图片时,利用好他们给的API和方法就行了。
如果就是简单的保存几张图片,简单的缓存,不想引入框架,那么可以看看这个!
/** * Bitmap缓存,简单缓存的设置类. * Created by ChangMingShan on 2016/12/26. */ public class BitmapMemoryCache { private static final String TAG = "BitmapMemoryCache"; private static BitmapMemoryCache sInstance = new BitmapMemoryCache(); private LruCache<String, Bitmap> mMemoryCache; /** * 单例模式. */ public static BitmapMemoryCache getInstance() { return BitmapMemoryCache.sInstance; } private BitmapMemoryCache() { int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); int cacheSize = maxMemory / 8; mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { @Override protected int sizeOf(String key, Bitmap bitmap) { // 重写此方法来衡量每张图片的大小,默认返回图片数量。 return bitmap.getByteCount() / 1024; } }; } public synchronized void addBitmapToMemoryCache(String key, Bitmap bitmap) { if (mMemoryCache.get(key) == null) { if (key != null && bitmap != null) mMemoryCache.put(key, bitmap); } else Log.w(TAG, "the res is aready exits"); } public synchronized Bitmap getBitmapFromMemCache(String key) { Bitmap bm = mMemoryCache.get(key); if (key != null) { return bm; } return null; } /** * 移除缓存 * * @param key */ public synchronized void removeImageCache(String key) { if (key != null) { if (mMemoryCache != null) { Bitmap bm = mMemoryCache.remove(key); if (bm != null) bm.recycle(); } } } /** * 移除缓存 */ public synchronized void clearImageCache() { if (mMemoryCache != null) { if (mMemoryCache.size() > 0) { Log.d("CacheUtils", "mMemoryCache.size() " + mMemoryCache.size()); mMemoryCache.evictAll(); Log.d("CacheUtils", "mMemoryCache.size()" + mMemoryCache.size()); } mMemoryCache = null; } } public void clearCache() { if (mMemoryCache != null) { if (mMemoryCache.size() > 0) { Log.d("CacheUtils", "mMemoryCache.size() " + mMemoryCache.size()); mMemoryCache.evictAll(); Log.d("CacheUtils", "mMemoryCache.size()" + mMemoryCache.size()); } mMemoryCache = null; } } public Bitmap loadLocal(String path) {/*这是加载本地的图片的步骤*/ Bitmap bitmap=BitmapFactory.decodeFile(path); addBitmapToMemoryCache(path, bitmap); return getBitmapFromMemCache(path); } /* 将图片进行压缩代码,本次未用到 BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = false; // 设置了此属性一定要记得将值设置为false Bitmap bitmap = null; bitmap = BitmapFactory.decodeFile(url, options); int be = (int) ((options.outHeight > options.outWidth ? options.outHeight / 150 : options.outWidth / 200)); if (be <= 0) // 判断200是否超过原始图片高度 be = 1; // 如果超过,则不进行缩放 options.inSampleSize = be; options.inPreferredConfig = Bitmap.Config.ARGB_4444; options.inPurgeable = true; options.inInputShareable = true; options.inJustDecodeBounds = false; try { bitmap = BitmapFactory.decodeFile(url, options); } catch (OutOfMemoryError e) { System.gc(); Log.e(TAG, "OutOfMemoryError"); } */ }
**我们可以这样定义:map里面的键是用来放图片地址的,既可以是网络上的图片地址,也可以SDcard上的图片地址,
map里面的值里面放的是持有软引用的Bitmap**
/** * Bitmap缓存网络图片或者是本地图片,简单缓存. * Created by ChangMingShan on 2016/12/26. */ public class SaveBitmapMemoryCache { private Map<String, SoftReference<Bitmap>> imageMap = new HashMap<String, SoftReference<Bitmap>>(); private static final String TAG = "NetBitmapMemoryCache"; public Bitmap loadBitmap(final String imageUrl,final ImageCallBack imageCallBack) { SoftReference<Bitmap> reference = imageMap.get(imageUrl); if(reference != null) { if(reference.get() != null) { return reference.get(); } } final Handler handler = new Handler() { public void handleMessage(final android.os.Message msg) { //加入到缓存中 Bitmap bitmap = (Bitmap)msg.obj; imageMap.put(imageUrl, new SoftReference<Bitmap>(bitmap)); if(imageCallBack != null) { imageCallBack.getBitmap(bitmap); } } }; new Thread(){ public void run() { Message message = handler.obtainMessage(); message.obj = downloadBitmap(imageUrl); handler.sendMessage(message); } }.start(); return null ; } // 从网上下载图片 private Bitmap downloadBitmap (String imageUrl) { Bitmap bitmap = null; try { bitmap = BitmapFactory.decodeStream(new URL(imageUrl).openStream()); return bitmap ; } catch (Exception e) { e.printStackTrace(); return null; } } /* public static boolean saveUrlAs(String fileUrl, String savePath)/*fileUrl网络资源地址*/ {//这是将图片缓存到本地的代码,此次也未用到,用到改一下就ok try { URL url = new URL(fileUrl);/*将网络资源地址传给,即赋值给url*/ HttpURLConnection connection = (HttpURLConnection)url.openConnection(); DataInputStream in = new DataInputStream(connection.getInputStream()); DataOutputStream out = new DataOutputStream(new FileOutputStream(savePath)); byte[] buffer = new byte[4096]; int count = 0; while ((count = in.read(buffer)) > 0)/*将输入流以字节的形式读取并写入buffer中*/ { out.write(buffer, 0, count); } out.close();/*前额安吉的将流关闭*/ in.close(); connection.disconnect(); return true; /*网络资源截取并存储本地成功返回true*/ } catch (Exception e) { return false; }*/ } public interface ImageCallBack{ void getBitmap(Bitmap bitmap); } }
如果能将Bitmap这个东西解决好,你的OOM基本不怎么会出现了,不建议修改系统或者是dalvike里面的东西。
还有一些OOM情况出现在加载非常多条目信息的listview,gridview等的情况,在这里推荐使用Recyclerview或者在这些adapter的getView函数里有个convertView参数。
本篇的使用要结合我的上一篇文章使用,避免不必要的麻烦。总的来说来好的代码规范可以帮你省去和解决很多不必要的麻烦!
- Android 内存泄漏和OOM分析(二)
- Android 内存泄漏和OOM分析(一)
- 一次内存泄漏导致的OOM实例分析和解决
- Android内存泄漏解决方案(OOM)
- android中的OOM和内存分析
- android 内存泄漏分析之二
- 内存溢出(oom)和内存泄漏(leak)
- Android OOM:内存管理分析和内存泄露原因总结
- Android内存泄漏和内存溢出(oom)的区别及解决方案
- Android 内存泄漏分析
- android 分析内存泄漏
- android内存泄漏分析
- android内存泄漏分析
- android 内存泄漏分析
- Android内存泄漏分析
- Android内存泄漏分析
- android 内存泄漏分析
- Android内存泄漏分析
- 01-考虑用静态工厂方法代替构造器
- Java木马(2)==服务端模块(判断操作系统)
- SASS基础
- Android+Eclipse开发环境配置
- eclipse采用第三方模拟器BlueStack…
- Android 内存泄漏和OOM分析(二)
- (01)java学习笔记——关键字/标识符/常…
- Editplus配置编写Java的环境
- (02)Java学习笔记——变量&数据类型&类…
- (03)Java学习笔记——运算
- (04)Java学习笔记——程序流程控制(控制语句)
- RecyclerView使用探索1--了解及使用
- (转)JAVA——成员变量 …
- 安卓开发环境的搭建2017-01-15更新