文档章节

防范不良代码

蜀山下的鱼
 蜀山下的鱼
发布于 2015/04/29 00:44
字数 2691
阅读 2
收藏 0

1查询数据库没有关闭游标


    程序中经常会进行查询数据库的操作,但是经常会有使用完毕Cursor后没有关闭的情况。如果我们的查询结果集比较小,对内存

的消耗不容易被发现,只有在常时间大量操作的情况下才会复现内存问题,这样就会给以后的测试和问题排查带来困难和风险。

示例代码:


Cursor cursor=getContentResolver().query(uri...);
if(cursor.moveToNext()){
......
}
修正示例代码:
Cursor cursor = null;
try{
cursor=getContentResolver().query(uri...);
if(cursor!=null && cursor.moveToNext()){
......
}
}finally{
if(cursor != null){
try{
cursor.close();
}catch(Exception e){

}
}
}



2.缓存 convertView


以构造ListViewBaseAdapter为例,在BaseAdapter中提高了方法:

public View getView(int position,Viewconvert View, ViewGroup parent)

来向ListView提供每一个item所需要的view对象。初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化一定数量的view

象,同时ListView会将这些view对象缓存起来。当向上滚动ListView时,原先位于最上面的listitemview对象会被回收,然后被用来

构造新出现的最下面的listitem。这个构造过程就是由getView()方法完成的,getView()的第二个形参View convertView就是被缓存起

来的listitemview对象(初始化时缓存中没有view对象则convertViewnull)


    由此可以看出,如果我们不去使用convertView,而是每次都在getView()中重新实例化一个View对象的话,即浪费资源也浪费时

间,也会使得内存占用越来越大。ListView回收listitemview对象的过程可以查看:


android.widget.AbsListView.java-->void addScrapView(Viewscrap)方法。
示例代码:
public View getView(int position,Viewconvert View,ViewGroup parent){
View view = new Xxx(...);
......
return view;
}
修正示例代码:
public View getView(int position,Viewconvert View,ViewGroup parent){
View view = null;
if(convertView != null){
view = convertView;
populate(view , getItem(position));
...
}else{
view = new Xxx(...);
...
}
return view;
}



3Bitmap对象释放内存


    有时我们会手工的操作Bitmap对象,如果一个Bitmap对象比较占内存,当它不在被使用的时候,可以调用Bitmap.recycle()方法回

收此对象的像素所占用的内存,但这不是必须的,视情况而定。



4释放对象的引用


这种情况描述起来比较麻烦,举两个例子进行说明。


示例A
假设有如下操作
public class DemoActivity extends Activity{
......
private Handler mHandler=...
private Object obj;
public void operation(){
obj = init Obj();
...
[Mark]
mHandler.post(new Runnable(){
public void run(){
use Obj(obj);
}
});
}
}


我们有一个成员变量obj,在operation()中我们希望能够将处理obj实例的操作post到某个线程的MessageQueue中。在以上的代码

中,即便是mHandler所在的线程使用完了obj所引用的对象,但这个对象仍然不会被垃圾回收掉,因为DemoActivity.obj还保有这个对

象的引用。所以如果在DemoActivity中不再使用这个对象了,可以在[Mark]的位置释放对象的引用,而代码可以修改为:
......
public void operation(){
obj = init Obj();
...
final Object o= obj;
obj = null;
mHandler.post(new Runnable(){
public void run(){
useObj(o);
}
}
}
......
示例B:
假设我们希望在锁屏界面(LockScreen)中,监听系统中的电话服务以获取一些信息(如信号强度等),则可以在LockScreen中定义一

PhoneStateListener的对象,同时将它注册到TelephonyManager服务中。对于LockScreen对象,当需要显示锁屏界面的时候就会

创建一个LockScreen对象,而当锁屏界面消失的时候LockScreen对象就会被释放掉。


但是如果在释放LockScreen对象的时候忘记取消我们之前注册的PhoneStateListener对象,则会导致LockScreen无法被垃圾回收。

如果不断的使锁屏界面显示和消失,则最终会由于大量的LockScreen对象没有办法被回收而引起OutOfMemory,使得system_process

进程挂掉。


总之当一个生命周期较短的对象A,被一个生命周期较长的对象B保有其引用的情况下,在A的生命周期结束时,要在B中清除掉对A

的引用。



5Context的使用


Android应用程序堆最大为16MB,至少在G1之前是这样即便没有将这些内存用完的打算,开发者也应尽量减少内存开销以便其他

应用能够在后台运行而不会被强制关闭。这样的话,Android在内存中保存的应用越多,用户在应用间的切换就越快。Android应用

程序的内存泄露问题,大部分时间里,这些问题都是源自同一个错误:对Context(上下文环境)的长时间引用。


Android上,Context用于多种操作,但最多的还是用来加载和访问资源。这也是为什么所有的Widges在其构造函数中都有一

Context参数。常规的Android应用中,有两类ContextActivityContextApplicationContext,通常前者被开发者传递给需要

Context的类和方法。

1. @Override

2. Protected void onCreate(Bundle state){

3. super.onCreate(state);

4. 

5. TextView label = new TextView(this);

6. label.setText("Leaks are bad");

7. 

8. setContentView(label);

9. }

这就意味着那些视图引用了整个Activity及其所拥有的一切:一般是整个视图层和所有资源。因此,如果泄露了这类Context(这里的

泄露指的是引用Context,从而阻止了GC(垃圾回收)操作),就泄露了很多内存空间。如果不小心,泄露整个Activity是非常容易

的事。

在进行屏幕方向改变的时候,系统默认做法是保持状态不变的情况下,销毁当前Activity并重新创建一个新的Activity。这样

做,Android会从资源文件中重新装载当前应用的UI。现在假设你写了一个带有很大一幅位图的应用,但你不想在每次屏幕旋转时都

装载一次位图,最简单的做法就是将其保存在一个静态区域中:


1. Private static Drawables Background;

2. 

3. @Override

4. Protected void onCreate(Bundle state){

5. super.onCreate(state);

6. 

7. TextView label = new TextView(this);

8. label.setText("Leaks are bad");

9. 

10. if(sBackground == null){

11. sBackground=getDrawable(R.drawable.large_bitmap);

12. }

13. label.setBackgroundDrawable(sBackground);

14. 

15. setContentView(label);

16. }

这段代码执行的快,同时也很有问题:在进行第一次屏幕方向改变的时候泄露了第一个Activity所占的内存空间。当一个Drawable

接到一个视图上时,视图被设置为Drawable上的一个回调,在上面的代码片段中,这就意味着Drawable引用了TextView

TextView又引用了ActivityContextActivityContext又进一步引用了更多的东西(依赖与你的代码)。


上面这段示例是最简单的泄露ActivityContext的情况,你可以到HomeScreen'sSourceCode查看unbindDrawables()方法中看看我们

是如何通过在Acitivity销毁时将存储Drawable的回调置为null来解决该问题的。如果再有兴趣的话,某些情况下会产生一个由泄露的

Context形成的链,这很糟糕,会很快使得内存耗尽。


有两种方法可以避免ActivityContext相关的内存泄露:最明显的一种是避免在ActivityContext自身的范围之外对其进行引用。上面这段示

例展示了静态引用的情况,但对内部类和外部类的隐式引用同样都是危险的。第二种解决方法是用ApplicationContext,因为该Context

与应用的生命周期一样长,并不依赖Activity的生命周期。如果想拥有一个生命期足够长的object(对象),但却需要给其一个必须的

Context的话,别忘了ApplicationObject。获取ApplicationContext的方法很简单:执行Context.getApplicationContext()

Activity.getApplication()

总之,为了避免ActivityContext相关的内存泄露,记住下面几条:

a) 不要长时间引用一个ActivityContext(引用周期应与Acitivity的生命周期一样长)

b) 尝试使用ApplicationContext代替AcitivityContext

c) Activity中,避免使用你无法控制其生命周期的非静态的内部类,使用静态的内部类,并对Activity内部进行弱引用。就是在静态的

内部类中对外部类进行弱引用,就如在ViewRoot及其W内部类中的做法那样

d) 垃圾回收(GC)无法保证内存泄露

e) 使用WeakReference代替强引用。比如可以使用WeakReference<Context> mContextRef;

该部分的详细内容也可以参考Android文档中Article部分。

 


6线程

线程也是造成内存泄露的一个重要的源头。线程产生内存泄露的主要原因在于线程生命周期的不可控。我们来考虑下面一段代码。

Public class MyActivity extends Activity{

Public void onCreate(Bundle savedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

newMyThread().start();

}

 

Private class MyThread extends Thread{

@Override

Public void run(){

super.run();

//dosomthing

}

}

}

这段代码很平常也很简单,是我们经常使用的形式。我们思考一个问题:假设MyThreadrun函数是一个很费时的操作,当我们开启

该线程后,将设备的横屏变为了竖屏,一般情况下当屏幕转换时会重新创建Activity,按照我们的想法,老的Activity应该会被销毁才

对,然而事实上并非如此。

由于我们的线程是Activity的内部类,所以MyThread中保存了Activity的一个引用,当MyThreadrun函数没有结束时,MyThread是不

会被销毁的,因此它所引用的老的Activity也不会被销毁,因此就出现了内存泄露的问题。

 

有些人喜欢用Android提供的AsyncTask,但事实上AsyncTask的问题更加严重,Thread只有在run函数不结束时才出现这种内存泄露

问题,然而AsyncTask内部的实现机制是运用了ThreadPoolExcutor,该类产生的Thread对象的生命周期是不确定的,是应用程序无法

控制的,因此如果AsyncTask作为Activity的内部类,就更容易出现内存泄露的问题。


这种线程导致的内存泄露问题应该如何解决呢?

第一、将线程的内部类,改为静态内部类。

第二、在线程内部采用弱引用保存Context引用。

解决的模型如下:

Public abstract class WeakAsyncTask<Params,Progress,Result,WeakTarget> extends

AsyncTask<Params,Progress,Result>{

Protected WeakReference<WeakTarget> mTarget;

 

Public WeakAsyncTask(WeakTarget target){

mTarget = new WeakReference<WeakTarget>(target);

}

 

@Override

Protected final void onPreExecute(){

Final WeakTarget target=mTarget.get();

if(target != null){

this.onPreExecute(target);

}

}

 

@Override

Protected final Result doInBackground(Params...params){

Final WeakTarget target=mTarget.get();

if(target!=null){

return this.doInBackground(target,params);

}else{

Return null;

}

}

 

@Override

Protected final void onPostExecute(Result result){

Final WeakTarget target=mTarget.get();

if(target != null){

this.onPostExecute(target,result);

}

}

 

Protected void onPreExecute(WeakTarget target){

//Nodefaultaction

}

 

Protected abstract Result doInBackground(WeakTarget target,Params...params);

 

Protected void onPostExecute(WeakTarget target ,Result result){

//Nodefaultaction

}

}




7其他


Android应用程序中最典型的需要注意释放资源的情况是在Activity的生命周期中,在onPause()onStop()onDestroy()方法中需要适

当的释放资源的情况。

本文转载自:http://blog.csdn.net/caiwenfeng_for_23/article/details/23211085

蜀山下的鱼
粉丝 9
博文 405
码字总数 0
作品 0
广州
高级程序员
私信 提问
前端 input 输入框可能被攻击的几种方式及防范

前言 最近看到一篇文章,文章讲到输入框有被 注入代码攻击 的危险,自己做了一个小示例,发现确实有这样的情况。 示例 先来看小示例吧,一个最简单的留言功能,输入框输入信息,然后把信息插...

NingBo
03/22
0
0
不可忽视的前端安全问题——XSS攻击

XSS是什么 XSS是跨站脚本攻击(Cross-Site Scripting)的简称。 XSS是一种注入脚本式攻击,攻击者利用如提交表单、发布评论等方式将事先准备好的恶意脚本注入到那些良性可信的网站中,当其他...

王冲
2018/10/22
0
0
OWASP Top 10十大风险 – 10个最重大的Web应用风险与攻防

先来看几个出现安全问题的例子 OWASP TOP10 开发为什么要知道OWASP TOP10 TOP1-注入 TOP1-注入的示例 TOP1-注入的防范 TOP1-使用ESAPI(https://github.com/ESAPI/esapi-java-legacy) TOP2...

lifetragedy
2016/09/18
0
0
网络机器人的识别与攻防的经典案例(也即爬虫与反爬虫的经典案例)

本文我们介绍一个网络机器人的识别与攻防的经典案例(也即爬虫与反爬虫的经典案例)。使用到的代码见本人的superword项目: https://github.com/ysc/superword/blob/master/src/main/java/o...

杨尚川
2015/04/11
0
0
认识Linux病毒 做好操作系统防护工程

对使用Windows的人来说,病毒无处不在,各种各样的新型病毒层出不穷,近年来,一种类似Unix的操作系统也在发展壮大,开始走进我们的视野,并在各领域内得到应用,它就是Linux系统,对于受病毒...

learningloong
2010/09/24
0
0

没有更多内容

加载失败,请刷新页面

加载更多

cmd命令与dos指令

bat命令学习 基础部分: 一、基础语法: 1.批处理文件是一个“.bat”结尾的文本文件,这个文件的每一行都是一条DOS命令。可以使用任何文本文件编辑工具创建和修改。 2.批处理是一种...

WinkJie
8分钟前
0
0
折叠手机适配布局

CSS Grid 设备相关参数 媒体查询 Chrome进行调试,创建相应的模拟机

lilugirl
38分钟前
2
0
Knative Eventing 中如何实现 Registry 事件注册机制

摘要: 在最新的 Knative Eventing 0.6 版本中新增了 Registry 特性, 为什么要增加这个特性, 该特性是如何实现的。针对这些问题,希望通过本篇文章给出答案。 背景 作为事件消费者,之前是...

阿里云云栖社区
41分钟前
1
0
安装 jemalloc for mysql

前言: Jemalloc 是BSD的提供的内存分配管理 安装依赖 $ yum install -y gcc$ yum install autoconf -y 安装 jemalloc $ git clone https://github.com/jemalloc/jemalloc$ cd jema......

Linux_Anna
52分钟前
3
0
linux下ctrl+c中止不了

有一台centos7的服务器,ctrl+c无效,并且tail -f也无效,只能关掉终端或者crtl+z放入后台再删掉,但是ping的时候使用ctrl+c是有效果的。 出现这种情况的原因可能是因为有人要使用ruby安装r...

gaolongquan
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部