Android面试题之——内存泄露
来源:互联网 发布:嵌入式linux开发流程 编辑:程序博客网 时间:2024/06/05 04:26
内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光,即发生OOM;
static Vector v = new Vector(10);for (int i = 1; i < 100; i++) { Object o = new Object(); v.add(o); o = null; // 设置null也无法将其内存回收,因为Vector中仍保存着对该内存的引用}
class A {}class Test { private static Test instance = new Test(new A()); private A a; private Test(A a) { this.a = a; } public Test getInstance() { return instance; }}设计上出现的问题,因为静态变量存储在方法区,因此instance的生命周期与应用相同,而instance中又保存了对A对象的引用,使得即使A对象不再使用,也无法对其进行释放。
class Server{ private String msg; public void recvMsg(){ readMsg(msg); save(msg); // 这里msg被保存之后已经无用,但是其生命周期将与Server保持一致,不能被回收,如果msg太大,将会造成不可预测的意外,安全的做法是加一行代码:msg=null; }}1、变量的作用域需要合理设置,类的成员变量生命周期与类对象相同,对于一些只需要短生命周期的变量,则会造成内存空间的浪费;
而这个也需要与内存抖动之间进行平衡比较,避免短时间内创建大量的临时性变量而造成内存抖动。
2、同理对于static静态变量,也应当遵循谨慎的原则进行使用;
3、在Android中最突出的问题便是对Context的引用造成的内存泄露。因为Activity对象本身引用的资源较多,其无法进行释放造成的资源浪费效果将会很明显;因此应当注意避免让长生命周期的对象引用Activity,可以尝试引用Application Context;
5)内部类与外部类之间的引用造成的内存泄露;由于内部类保存着对外部类的引用,若未对其进行释放,外部类的内存空间也无法释放;
public class TestActivity extends Activity { private MyHandler myHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // MyHandler是内部类,初始化MyHandler,其内部会隐式地保存对其外部类即Activity的引用 myHandler = new MyHandler(); // postDelayed一个Runnable,该操作会send一个Message到MessageQueue中,而Message对象msg中的 // target变量将会指向myHandler,即msg保存有对myHandler的引用 // 又MessageQueue是针对线程存在,当msg入队列还未得到处理时,Activity被关闭,此时msg依然存在与MessageQueue中 // 又由前面得知msg保存着对myHandler的引用,而myHandler保存着对Activity的应用;因此Activity关闭后,其内存仍然无法得到回收,即造成内存泄露 myHandler.postDelayed(new Runnable() { @Override public void run() { } }, 100000); // 关闭Activity finish(); } private class MyHandler extends Handler { @Override public void handleMessage(Message msg) { } }}由于MyHandler是内部类,初始化MyHandler,其内部会隐式地保存对其外部类即Activity的引用;
使用MyHandler postDelayed一个Runnable,该操作会send一个Message到MessageQueue中,而Message对象msg中的target变量将会指向myHandler,即msg保存有对myHandler的引用
又MessageQueue是针对线程存在,当msg入队列还未得到处理时,Activity被关闭,此时msg依然存在与MessageQueue中;
又由前面得知msg保存着对myHandler的引用,而myHandler保存着对Activity的应用;因此Activity关闭后,其内存仍然无法得到回收,即造成内存泄露。
2、解决Handler内存泄露的方法:使用弱引用及静态内部类的方法;
public class TestActivity extends Activity { private MyHandler myHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); myHandler = new MyHandler(this); } private static class MyHandler extends Handler { // 与外部Activity建立弱引用关系 private WeakReference<TestActivity> mWeak; public MyHandler(TestActivity activity) { // 初始化 mWeak = new WeakReference<TestActivity>(activity); } @Override public void handleMessage(Message msg) { TestActivity activity = mWeak.get(); if (activity != null) { activity.handleMessage(msg); } } } // 处理事务逻辑 public void handleMessage (Message msg) { // ...... }}
6)涉及到ListView优化中的未使用convertView,过多使用自己创建的View;
1、错误用法:
@Overridepublic View getView(int position, View convertView, ViewGroup parent) { View view = inflater.inflate(R.layout.activity_main, parent, false); // ...... return view;}
2、由ListView的源码及工作原理,来看一下ListView的源码:
ListView填充及获取View的方法如下:
private View makeAndAddView(int position, int y, boolean flow, int childrenLeft, boolean selected) { View child; if (!mDataChanged) { // Try to use an exsiting view for this position child = mRecycler.getActiveView(position); if (child != null) { // Found it -- we're using an existing child // This just needs to be positioned setupChild(child, position, y, flow, childrenLeft, selected, true); return child; } } // Make a new view for this position, or convert an unused view if possible child = obtainView(position, mIsScrap); // This needs to be positioned and measured setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]); return child;}由RecycleBin机制,ListView会首先尝试获取ActiveView[]中存储的View;然后再通过obtainView来获取View;
View obtainView(int position, boolean[] isScrap) { isScrap[0] = false; View scrapView; scrapView = mRecycler.getScrapView(position); View child; if (scrapView != null) { child = mAdapter.getView(position, scrapView, this); if (child != scrapView) { mRecycler.addScrapView(scrapView); if (mCacheColorHint != 0) { child.setDrawingCacheBackgroundColor(mCacheColorHint); } } else { isScrap[0] = true; dispatchFinishTemporaryDetach(child); } } else { child = mAdapter.getView(position, null, this); if (mCacheColorHint != 0) { child.setDrawingCacheBackgroundColor(mCacheColorHint); } } return child;}obtainView方法中首先通过getScrapView获取ScrapView[]中存储的View,ScrapView中存储的View便是划出界面废弃的View存储起来,便于再次重复利用;
可以看到在child = mAdapter.getView(position, scrapView, this);方法中来获得View,而scrapView即对应重写BaseAdapter方法中的convertView变量;不使用convertView则会造成废弃的View无法重复利用,同时大量创建的View,当View移出界面之后,而无法得到及时回收,造成内存泄露。
3、正确的用法:
private ViewHolder viewHolder;@Overridepublic View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = inflater.inflate(R.layout.activity_main, parent); viewHolder = new ViewHolder(); viewHolder.imageView = (ImageView) convertView.findViewById(R.id.imageView); viewHolder.textView = (TextView) convertView.findViewById(R.id.textView); convertView.setTag(viewHolder); } else { viewHolder = (ViewHolder) convertView.getTag(); } // ........ viewHolder.imageView.setImageDrawable(null); return convertView;}static class ViewHolder { TextView textView; ImageView imageView;}
7)资源没有关闭造成的内存泄露:
1、Cursor、File等资源型对象:
资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。
它们的 缓冲不仅存在于java虚拟机内,还存在于java虚拟机外。
如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄露。
- Android面试题之——内存泄露
- Android开发之—内存泄露篇
- (面试题)Java中的内存泄露
- Android之AsyncTask异步任务——防止内存泄露
- 如何检测内存泄露,如何进行内存优化? 【面试题】
- 公司面试题——Android内存管理
- android面试题——内存管理(0三)
- 阿里面经之解答 by cmershen(5)——内存泄露,java.util.concurrent包
- android之Context内存泄露
- Android内存泄露之开篇
- Android 内存泄露之资源
- Android内存泄露之开篇
- Android 内存泄露之------Thread
- Android之Contenxt内存泄露
- Android内存泄露之Context
- Android内存泄露之Handler
- Android内存泄露之Handler
- Android技术之 内存泄露
- 设计模式六大原则(3)-依赖倒置原则
- MFC函数启动执行过程
- maven之Nexus的配置【setting.xml里配置<profile>】(六)
- 从输入网址到网页显示过程
- UE激活
- Android面试题之——内存泄露
- 使用js脚本的好处以及其本身的特点
- 一行打印一个单词核心代码
- Apriori algorithm---数据挖掘初学1
- R语言学习之数据的清理和转化
- AlertDialog中View中的控件设置监听
- RSA key 的转换
- AlertDialog 普通对话框
- 设计模式六大原则(4)-接口隔离原则