造成OOM(内存溢出)的几种情况
数据库
Cursor
没关。
当我们操作完数据库后,一定要调用close()
释放资源。构造
Adapter
没有使用缓存ContentView
。@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder vHolder = null;
//如果convertView对象为空则创建新对象,不为空则复用
if (convertView == null) {
convertView = inflater.inflate(..., null);
// 创建 ViewHodler 对象
vHolder = new ViewHolder();
vHolder.img= (ImageView) convertView.findViewById(...);
vHolder.tv= (TextView) convertView
.findViewById(...);
// 将ViewHodler保存到Tag中
convertView.setTag(vHolder);
} else {
//当convertView不为空时,通过getTag()得到View
vHolder = (ViewHolder) convertView.getTag();
}
// 给对象赋值,修改显示的值
vHolder.img.setImageBitmap(...);
vHolder.tv.setText(...);
return convertView;
} static class ViewHolder {
TextView tv;
ImageView img;
}未取消注册广播接收者
registerReceiver()
和unregisterReceiver()
要成对出现,通常需要在Activity
的onDestory()
方法去取消注册广播接收者。IO
流未关闭 注意用完后及时关闭Bitmap
使用后未调用recycle()
。Context
泄漏 这是一个很隐晦的OutOfMemoryError
的情况。先看一个Android官网提供的例子:private static Drawable sBackground;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this);
label.setText("Leaks are bad");
if (sBackground == null) {
sBackground = getDrawable(R.drawable.large_bitmap);
}
label.setBackgroundDrawable(sBackground);
setContentView(label);
}这段代码效率很快,但同时又是极其错误的:
我们看一下setBackgroundDrawable(Drawable background)
的源码:public void setBackgroundDrawable(Drawable background) {
...
background.setCallback(this);
}有
background.setCallback(this);
方法,也就是说Drawable
拥有TextView
的引用,而TextView
又拥有Activity
(Context类型)的引用, 因为sBackground
为static
的,即使Activity
被销毁,但是sBackground
的生命周期还没走完,所以内存仍然不会被释放。这样就会有内存泄露了。 对,这样想是对的,但是我们看一下setCallback
的源码:public final void setCallback(Callback cb) {
mCallback = new WeakReference<Callback>(cb);
}我们会发现里面使用了
WeakReference
,所以不会存在内存泄露了,但是官网当时提供的例子明明说有泄露,这是因为在3.0之后, 修复了这个内存泄露的问题,在3.0之前setCallback
,方法是没有使用WeakReference
的,所以这种泄露的情况在3.0之前会发生,3.0之后已经被修复。线程 线程也是造成内存泄露的一个重要的源头。线程产生内存泄露的主要原因在于线程生命周期的不可控。我们来考虑下面一段代码。
public class MyActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new MyThread().start();
}
private class MyThread extends Thread{
@Override
public void run() {
super.run();
//耗时的操作
}
}
}假设
MyThread
的run
函数是一个很费时的操作,当调用finish
的时候Activity
会销毁掉吗?
事实上由于我们的线程是Activity
的内部类,所以MyThread
中保存了Activity
的一个引用,当MyThread
的run
函数没有结束时,MyThread
是不会被销毁的, 因此它所引用的Activity
也不会被销毁,因此就出现了内存泄露的问题。尽量使用
ApplicationContext
Context
引用的生命周期超过它本身的生命周期,也会导致Context
泄漏。 所以如果打算保存一个长时间的对象时尽量使用Application
这种Context
类型。 例如:mStorageManager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
改成:
mStorageManager = (StorageManager) getApplicationContext().getSystemService(Context.STORAGE_SERVICE);按道理来说这种系统服务是不会有问题,但是有些厂商在修改的时候,可能会导致
Context
无法被及时释放。Handler的使用,在Activity退出的时候注意移除(尤其是循环的时候)
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);
//每2秒执行一次
mHandler.postDelayed(mRunnable, 2000);
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//通过Handler启动线程
mHandler.post(mRunnable);
}
}这样在也会引发内存泄露。我们应该在
onDestory
方法中移除Handler
,代码如下:@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacks(mRunnable);
}
最新文章
- Web APP开发技巧总结(转)
- Spring学习总结(三)——Spring实现AOP的多种方式
- Python的sorted函数应用
- 在sap系统设置纸张打印格式(针式打印机)
- ViewPager滑动页面的实现方法
- iOS H5 容器的一些探究(一):UIWebView 和 WKWebView 的比较和选择
- django下载文件
- 什么是DCI
- linux sort 用法
- 字符串的模式匹配(Java实现)
- EF增删改查+使用Expression进行动态排序分页
- linux 查看ip地址
- 将docker镜像上传到docker hub
- composer修改中文镜像
- 结对编程--C语言子程序词法分析
- Amazon OA
- [UE4]使机器人受伤
- 以Settings.APPLICATION_DEVELOPMENT_SETTINGS打开开发人员面板出错总结
- PHP中php_sapi_name()与array_map()
- 【转】多线程Core Data