因为咱们的线程是Activity的内部类,因此MyThread中保存了Activity的一个引用,当MyThread的run函数没有结束 时,MyThread是不会被销毁的,所以它所引用的老的Activity也不会被销毁,所以就出现了内存泄露的问题。 java
这种线程致使的内存泄露问题应该如何解决呢?
第1、将线程的内部类,改成静态内部类。
第2、在线程内部采用弱引用保存Context引用。
另外,咱们都知道Hanlder是线程与Activity通讯的桥梁,咱们在开发好多应用中会用到线程,有些人处理不当,会致使当程序结束时,线程并无 被销毁,而是一直在后台运行着,当咱们从新启动应用时,又会从新启动一个线程,周而复始,你启动应用次数越多,开启的线程数就越多,你的机器就会变得越 慢。
package com.tutor.thread;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
public class ThreadDemo extends Activity {
private static final String TAG = "ThreadDemo";
private int count = 0;
private Handler mHandler = new Handler();
private Runnable mRunnable = new Runnable() {
public void run() {
//为了方便 查看,咱们用Log打印出来
Log.e(TAG, Thread.currentThread().getName() + " " +count);
count++;
setTitle("" +count);
//每2秒执行一次
mHandler.postDelayed(mRunnable, 2000);
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//经过Handler启动线程
mHandler.post(mRunnable);
}
}
因此咱们在应用退出时,要将线程销毁,咱们只要在Activity中的,onDestory()方法处理一下就OK了,以下代码所示:
@Override
protected void onDestroy() {
mHandler.removeCallbacks(mRunnable);
super.onDestroy();
}
3.超级大胖子Bitmap
能够说出现OutOfMemory问题的绝大多数人,都是由于Bitmap的问题。由于Bitmap占用的内存实在是太多了,它是一个“超级大胖子”,特别是分辨率大的图片,若是要显示多张那问题就更显著了。
如何解决Bitmap带给咱们的内存问题?
第1、及时的销毁。
虽然,系统可以确认Bitmap分配的内存最终会被销毁,可是因为它占用的内存过多,因此极可能会超过java堆的限制。所以,在用完Bitmap时,要 及时的recycle掉。recycle并不能肯定当即就会将Bitmap释放掉,可是会给虚拟机一个暗示:“该图片能够释放了”。
第2、设置必定的采样率。
有时候,咱们要显示的区域很小,没有必要将整个图片都加载出来,而只须要记载一个缩小过的图片,这时候能够设置必定的采样率,那么就能够大大减少占用的内存。以下面的代码:
private ImageView preview;
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;//图片宽高都为原来的二分之一,即图片为原来的四分之一
Bitmap bitmap = BitmapFactory.decodeStream(cr.openInputStream(uri), null, options); preview.setImageBitmap(bitmap);
第3、巧妙的运用软引用(SoftRefrence)
有些时候,咱们使用Bitmap后没有保留对它的引用,所以就没法调用Recycle函数。这时候巧妙的运用软引用,可使Bitmap在内存快不足时获得有效的释放。
4.行踪诡异的Cursor
Cursor是Android查询数据后获得的一个管理数据集合的类,正常状况下,若是查询获得的数据量较小时不会有内存问题,并且虚拟机可以保证Cusor最终会被释放掉。
然而若是Cursor的数据量特表大,特别是若是里面有Blob信息时,应该保证Cursor占用的内存被及时的释放掉,而不是等待GC来处理。而且Android明显是倾向于编程者手动的将Cursor close掉
5.构造Adapter时,没有使用缓存的 convertView
描述:
以构造ListView的BaseAdapter为例,在BaseAdapter中提升了方法:
public View getView(int position, View convertView, ViewGroup parent)
来向ListView提供每个item所须要的view对象。初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化必定数量的 view对象,同时ListView会将这些view对象缓存起来。当向上滚动ListView时,原先位于最上面的list item的view对象会被回收,而后被用来构造新出现的最下面的list item。这个构造过程就是由getView()方法完成的,getView()的第二个形参 View convertView就是被缓存起来的list item的view对象(初始化时缓存中没有view对象则convertView是null)。
由此能够看出,若是咱们不去使用convertView,而是每次都在getView()中从新实例化一个View对象的话,即浪费资源也浪费时间,也会使得内存占用愈来愈大。ListView回收list item的view对象的过程能够查看:
android.widget.AbsListView.java --> void addScrapView(View scrap) 方法。
示例代码:
public View getView(int position, View convertView, ViewGroup parent) {
View view = new Xxx(...);
... ...
return view;
}
修正示例代码:
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
if (convertView != null) {
view = convertView;
populate(view, getItem(position));
...
} else {
view = new Xxx(...);
...
}
return view;
}
小结:
static:引用了大对象如context
线程:切屏时Activity由于线程引用而没有如期被销毁;handler有关,Activity意外终止但线程还在
Bitmap:要及时recycle,下降采样率
Cursor:要及时关闭
Adapter:没有使用缓存的convertView
4、内存泄漏调试:
(1).内存监测工具 DDMS --> Heap
不管怎么当心,想彻底避免bad code是不可能的,此时就须要一些工具来帮助咱们检查代码中是否存在会形成内存泄漏的地方。Android tools中的DDMS就带有一个很不错的内存监测工具Heap(这里我使用eclipse的ADT插件,并以真机为例,在模拟器中的状况相似)。用 Heap监测应用进程使用内存状况的步骤以下:
1. 启动eclipse后,切换到DDMS透视图,并确认Devices视图、Heap视图都是打开的;
2. 将手机经过USB连接至电脑,连接时须要确认手机是处于“USB调试”模式,而不是做为“Mass Storage”;
3. 连接成功后,在DDMS的Devices视图中将会显示手机设备的序列号,以及设备中正在运行的部分进程信息;
4. 点击选中想要监测的进程,好比system_process进程;
5. 点击选中Devices视图界面中最上方一排图标中的“Update Heap”图标;
6. 点击Heap视图中的“Cause GC”按钮;
7. 此时在Heap视图中就会看到当前选中的进程的内存使用量的详细状况。
说明:
a) 点击“Cause GC”按钮至关于向虚拟机请求了一次gc操做;
b) 当内存使用信息第一次显示之后,无须再不断的点击“Cause GC”,Heap视图界面会定时刷新,在对应用的不断的操做过程当中就能够看到内存使用的变化;
c) 内存使用信息的各项参数根据名称便可知道其意思,在此再也不赘述。
如何才能知道咱们的程序是否有内存泄漏的可能性呢。这里须要注意一个值:Heap视图中部有一个Type叫作data object,即数据对象,也就是咱们的程序中大量存在的类类型的对象。在data object一行中有一列是“Total Size”,其值就是当前进程中全部Java数据对象的内存总量,通常状况下,这个值的大小决定了是否会有内存泄漏。能够这样判断:
a) 不断的操做当前应用,同时注意观察data object的Total Size值;
b) 正常状况下Total Size值都会稳定在一个有限的范围内,也就是说因为程序中的的代码良好,没有形成对象不被垃圾回收的状况,因此说虽然咱们不断的操做会不断的生成不少对 象,而在虚拟机不断的进行GC的过程当中,这些对象都被回收了,内存占用量会会落到一个稳定的水平;
c) 反之若是代码中存在没有释放对象引用的状况,则data object的Total Size值在每次GC后不会有明显的回落,随着操做次数的增多Total Size的值会愈来愈大,
直到到达一个上限后致使进程被kill掉。
d) 此处已system_process进程为例,在个人测试环境中system_process进程所占用的内存的data object的Total Size正常状况下会稳定在2.2~2.8之间,而当其值超过3.55后进程就会被kill。
总之,使用DDMS的Heap视图工具能够很方便的确认咱们的程序是否存在内存泄漏的可能性。
(2).内存分析工具 MAT(Memory Analyzer Tool)
(一) 生成.hprof文件
(二) 使用MAT导入.hprof文件
(三) 使用MAT的视图工具分析内存 android