您的位置:首页 > 移动开发 > Android开发

Android内存泄露——全解析和处理办法

2016-08-11 18:15 309 查看

先说为什么要进行内存泄漏检查,也就是内存泄漏会造成什么后果?JAVA是垃圾回收语言的一种,开发者无需特意管理内存分配。但是JAVA中还是存在着许多内存泄露的可能性,如果不好好处理内存泄露,会导致APP内存单元无法释放被浪费掉,导致使用者感觉越用越卡,最终导致内存全部占据堆栈(heap)挤爆进而程序崩溃。先看几张巴枪上的内存泄漏的图片。

随便就是100多K,3百多个溢出,虽然很多是重复测试造成的,但确实不可小视。

    想要了解内存泄露,对内存的了解必不可少。

JAVA是在JVM所虚拟出的内存环境中运行的,JVM的内存可分为三个区:堆(heap)、栈(stack)和方法区(method)

 

栈(stack):是简单的数据结构,但在计算机中使用广泛。栈最显著的特征是:LIFO(Last In, First Out,
后进先出)。比如我们往箱子里面放衣服,先放入的在最下方,只有拿出后来放入的才能拿到下方的衣服。栈中只存放基本类型变量和对象的引用(不是对象),常量

 

堆(heap):堆内存用于存放由new创建的对象和数组。在堆中分配的内存,由java虚拟机自动垃圾回收器来管理。JVM只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身

 

方法区(method):又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量

 

内存的概念大概理解清楚后,要考虑的问题来了:

到底是哪里的内存会让我们造成内存泄露?

 

在JAVA中JVM的栈记录了方法的调用,每个线程拥有一个栈。在线程的运行过程当中,执行到一个新的方法调用,就在栈中增加一个内存单元,即帧(frame)。在frame中,保存有该方法调用的参数、局部变量和返回地址。然而JAVA中的局部变量只能是基本类型变量(int),或者对象的引用。所以在栈中只存放基本类型变量和对象的引用。引用的对象保存在堆中。

 

当某方法运行结束时,该方法对应的frame将会从栈中删除,frame中所有局部变量和参数所占有的空间也随之释放。线程回到原方法继续执行,当所有的栈都清空的时候,程序也就随之运行结束。

而对于堆内存,堆存放着普通变量。在JAVA中堆内存不会随着方法的结束而清空,所以在方法中定义了局部变量,在方法结束后变量依然存活在堆中。

 

综上所述,栈(stack)可以自行清除不用的内存空间。但是如果我们不停的创建新对象,堆(heap)的内存空间就会被消耗尽。所以JAVA引入了垃圾回收(garbage
collection,简称GC)去处理堆内存的回收,但如果对象一直被引用无法被回收,造成内存的浪费,无法再被使用。所以对象无法被GC回收就是造成内存泄露的原因!

 

垃圾回收机制

垃圾回收(garbage collection,简称GC)可以自动清空堆中不再使用的对象。在JAVA中对象是通过引用使用的。如果再没有引用指向该对象,那么该对象就无从处理或调用该对象,这样的对象称为不可到达(unreachable)。垃圾回收用于释放不可到达的对象所占据的内存。

 

实现思想:我们将栈定义为root,遍历栈中所有的对象的引用,再遍历一遍堆中的对象。因为栈中的对象的引用执行完毕就删除,所以我们就可以通过栈中的对象的引用,查找到堆中没有被指向的对象,这些对象即为不可到达对象,对其进行垃圾回收。

垃圾回收实现思想如果持有对象的强引用,垃圾回收器是无法在内存中回收这个对象。

 

引用类型

在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象。也就是说,只有对象处于可触及(reachable)状态,程序才能使用它。从JDK
1.2版本开始,把对象

 

的引用分为4种级别,从而使程序能更加灵活地控制对象的生命周期。这4种级别由高到低依次为:强引用、软引用、弱引用和虚引用。

Java/Android引用类型及其使用分析

 

1. 强引用(Strong reference)

实际编码中最常见的一种引用类型。常见形式如:A a = new A();等。强引用本身存储在栈内存中,其存储指向对内存中对象的地址。一般情况下,当对内存中的对象不再有任何强引用指向它时,垃圾回收机器开始考虑可能要对此内存进行的垃圾回收。如当进行编码:a = null,此时,刚刚在堆中分配地址并新建的a对象没有其他的任何引用,当系统进行垃圾回收时,堆内存将被垃圾回收。

 

2. 软引用(Soft Reference)

软引用的一般使用形式如下:

A a = new A();

SoftReference<A> srA = new SoftReference<A>(a);

软引用所指示的对象进行垃圾回收需要满足如下两个条件:

1.当其指示的对象没有任何强引用对象指向它;

2.当虚拟机内存不足时。

因此,SoftReference变相的延长了其指示对象占据堆内存的时间,直到虚拟机内存不足时垃圾回收器才回收此堆内存空间。

 

3. 弱引用(Weak Reference)

同样的,弱引用的一般使用形式如下:

A a = new A();

WeakReference<A> wrA = new WeakReference<A>(a);

WeakReference不改变原有强引用对象的垃圾回收时机,一旦其指示对象没有任何强引用对象时,此对象即进入正常的垃圾回收流程。

 

 

内存泄露原因如果持有对象的强引用,垃圾回收器是无法在内存中回收这个对象。

 

内存泄露的真因是:持有对象的强引用,且没有及时释放,进而造成内存单元一直被占用,浪费空间,甚至可能造成内存溢出!

 

其实在Android中会造成内存泄露的情景无外乎两种:

 

全局进程(process-global)的static变量。这个无视应用的状态,持有Activity的强引用的怪物。活在Activity生命周期之外的线程。没有清空对Activity的强引用。

检查一下你的项目中是否有以下几种情况:Static Activities,Static Views,inner Classes,Anonymous Classes,Sensor Manager,TimerTask,Threads,动态广播注册了要注销,context跟ApplicationContext选择(能用ApplicationContext就不用context),第3方SDK要关闭,Context,单例的使用,其中TimerTask,Threads也属于内部类或是匿名内部类。

关于leakCanary的集成很简单见https://github.com/square/leakcanary。原理的话见http://www.jianshu.com/p/5ee6b471970e,不是我们分析的重点,当然也可以使用MAT工具来分析,不过比较麻烦http://blog.csdn.net/aaa2832/article/details/19419679,下面是由leakCanary内存泄漏检测工具检测出来的项目当中的一些例子。

Static Activities

1.以前: public class LogUtils {

     .....

     public static Context sContext;

.....

public static void init(Context context) {

        weakRefCtx = new WeakReference<Context>(context);

         if (weakRefCtx.get()==null)

             return;

         sContext = weakRefCtx.get();

.....

        String dir = sContext.getCacheDir().toString() + "/Logs/";

.....

public class BaseActivity extends Activity implements CheckUpdateBack {

protected void onCreate(Bundle savedInstanceState) {

L ogUtils.init(this);

现在:1.public class BaseActivity extends Activity implements CheckUpdateBack {

        protected void onDestroy() {

super.onDestroy();

LogUtils.sContext = null;

以前:2.public class CustomProgressDialog extends Dialog {

private Context context = null;

private static CustomProgressDialog customProgressDialog = null;

现在:2.public class CustomProgressDialog extends Dialog {

private Context context = null;

private static CustomProgressDialog customProgressDialog = null;

public static void CancelCustomProgressDialog(){

customProgressDialog.cancel();

customProgressDialog = null ;

}

以前:3.public class ToastUtil {

     private static Toast toast = null;

     public static void toastText(Context context, CharSequence text, int duration){

         if (toast==null)

         //创建一个Toast提示消息

             toast = Toast.makeText(context, text, duration);

         else

             toast.setText(text);

         //设置Toast提示消息在屏幕上的位置

         toast.setGravity(Gravity.CENTER, 0, 0);

         //显示消息

         toast.show();

     }

     public static void closeToast(){

         if(toast!=null){

             toast.cancel();

         }

     }

 

现在:3.public class ToastUtil {

     private static Toast toast = null;

     public static void toastText(Context context, CharSequence text, int duration){

         if (toast==null)

         //创建一个Toast提示消息

             toast = Toast.makeText(context, text, duration);

         else

             toast.setText(text);

         //设置Toast提示消息在屏幕上的位置

         toast.setGravity(Gravity.CENTER, 0, 0);

         //显示消息

         toast.show();

     }

     public static void closeToast(){

         if(toast!=null){

             toast.cancel();

             toast = null;

         }

     }

 

 

Static Views

 

1.以前:

 

  现在:public class MyInfo_MoreSetting_V35 extends BaseActivity implements OnClickListener, CheckUpdateBack {

private static TextView cacheSize;

     private Bundle b;

     private static CustomProgressDialog dlg;

     private static TextView tv_update;

protected void onDestroy() {

         super.onDestroy();

         handler.removeCallbacksAndMessages(null);

         cacheSize = null;

         tv_update = null;

         longDlg.cancel();

         longDlg = null;

     }

2.以前:public class BillActivity extends ActivityWithTitleAndList_SpecFooter {

private ImageView img_null;

  现在:public class BillActivity extends ActivityWithTitleAndList_SpecFooter {

private static ImageView img_null;

protected void onDestroy() {

         super.onDestroy();

         img_null= null;

     }

Inner Classes

1.以前:public class ChooseCity extends BaseActivity implements Comparator {

final myThread mythread = new myThread();

class myThread extends Thread implements Runnable {

         public Handler handler;

 

         @Override

         public void run() {

             super.run();

             Looper.prepare();

             handler = new Handler() {

                 @Override

                 public void handleMessage(Message msg) {

                     super.handleMessage(msg);

                     if (0 == msg.arg2) {

                         JSONObject pjo = new JSONObject();

                         for (int i = 0; i < commonpro_lists.size(); i++) {

                             if (root_ob.containsKey(commonpro_lists.get(i))) {

                                 pjo.put(commonpro_lists.get(i), root_ob.get(commonpro_lists.get(i)));

                             }

                         }

  现在:public class ChooseCity extends BaseActivity implements Comparator {

final myThread mythread = new myThread(this,commonpro_lists, root_ob,

                commoncity_lists,area_ob,area_lists,city_id,MyHandler);

         mythread.start();

static class myThread extends Thread implements Runnable {

         public Handler handler;

        private WeakReference<ChooseCity> chooseCityWeakReference;

        private List<String> commonpro_lists;

        private JSONObject root_ob;

        private List<String> commoncity_lists;

        private JSONObject area_ob;

        private List<String> area_lists;

        private String city_id;

        private Handler MyHandler;

 

        public myThread(ChooseCity chooseCity, List<String> commonpro_lists, JSONObject root_ob,

                        List<String> commoncity_lists, JSONObject area_ob, List<String> area_lists,

                        String city_id, Handler MyHandler) {

            chooseCityWeakReference = new WeakReference<ChooseCity>(chooseCity);

            this.commonpro_lists = commonpro_lists;

            this.root_ob = root_ob;

            this.commoncity_lists = commoncity_lists;

            this.area_ob = area_ob;

            this.area_lists = area_lists;

            this.city_id = city_id;

            this.MyHandler = MyHandler;

 

        }

 

        @Override

        public void run() {

            super.run();

            Looper.prepare();

            handler = new Handler() {

                @Override

                public void handleMessage(Message msg) {

                    super.handleMessage(msg);

                    if (0 == msg.arg2) {

                        JSONObject pjo = new JSONObject();

                        for (int i = 0; i < commonpro_lists.size(); i++) {

Anonymous Classes

1.以前:mWebView.setWebChromeClient(new MyWebChromeClient(this,progressbar));

  现在:mWebView.setWebChromeClient(new MyWebChromeClient(this));

 

Handler:要用activity就要用弱引用指向的activity,并且在ondestroy里面要调用remove

  

1.以前:public class BaseActivity extends Activity implements CheckUpdateBack {

   protected static Handler myHandler = new Handler(Looper.getMainLooper()){

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

  现在:protected Handler myHandler = new MyHandler(BaseActivity.this);

static class MyHandler extends  Handler{

private WeakReference<Context> weakRefCtx ;

public MyHandler(Context context){

weakRefCtx = new WeakReference<Context>(context);

}

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

if (weakRefCtx.get()==null)

return;

protected void onDestroy() {

         super.onDestroy();

         payHandle.removeCallbacksAndMessages(null);

     }

Threads

1.以前:new Thread(new Runnable() {

@Override

public void run() {

  

  现在:MyRunnable runnable = new MyRunnable(mIRemoteService,payHandle,businessResult);

new Thread(runnable).start();

}

static class MyRunnable implements Runnable {

private IRemoteClientService mIRemoteService;

private Handler payHandle;

private String businessResult;

public MyRunnable(IRemoteClientService mIRemoteService,Handler payHandle,String businessResult){

this.mIRemoteService = mIRemoteService;

this.payHandle = payHandle;

this.businessResult = businessResult;

}

@Override

public void run() {

try {

String result = mIRemoteService.pay(businessResult);

Message message = new Message();

message.what = 10;

message.obj = result;

payHandle.sendMessage(message);

} catch (RemoteException e) {

e.printStackTrace();

}

}

}

TimerTask

 

Sensor Manager(还有各种通过getSystemService的到的,重现就是打开2个含有此服务的activity,详解http://www.tuicool.com/articles/yuiMbyy,https://github.com/pwittchen/NetworkEvents/issues/106)

1.以前:LoginActivity :protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        mLoginUserList = LoginUserDBManager.getInstance().getAllUser();

        mWifiManager = (WifiManager) getSystemService(WIFI_SERVICE);

1.现在:protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        mLoginUserList = LoginUserDBManager.getInstance().getAllUser();

        mWifiManager = (WifiManager)                                                           this.getApplicationContext().getSystemService(WIFI_SERVICE);

 

 

单例的使用

1.以前:UserInfo userInfo = AppContext.getAppContext().getmCurrentUserInfo();

  现在:UserInfo userInfo = ((AppContext)this.getApplication()).getmCurrentUserInfo();

 

2.以前:public static CrashHandler getInstance(Context context,

UncaughtExceptionHandler defaultHandler) {

if (mHandler == null)

mHandler = new CrashHandler(context, defaultHandler);

mContext = context ;

return mHandler;

}

  现在:public static CrashHandler getInstance(Context context,

UncaughtExceptionHandler defaultHandler) {

WeakReference<Context> weakRefCtx = new WeakReference<Context>(context);

if (mHandler == null){

if (weakRefCtx.get()!=null)

mHandler = new CrashHandler(context, defaultHandler);

}

if (weakRefCtx.get()!=null)

mContext = context ;

return mHandler;

}

 

Context:

1.以前:public class BocPay {

        public boolean aboutMapQuery(Context context) {

this.context = context;

dialogMessage = "您尚未安装中国银行移动支付插件,立即安装并完成支付!";

mFlag = inexistenceOrNeedUpdate(packageName);

if (!mFlag) {// 未安装或需要更新

new AlertDialog.Builder(context).setTitle("提示信息                           ").setMessage(dialogMessage)

  现在:public class BocPay {

     public boolean aboutMapQuery(Context context) {

weakRefCtx = new WeakReference<Context>(context);

// this.context = weakRefCtx.get();

dialogMessage = "您尚未安装中国银行移动支付插件,立即安装并完成支付! ";

if (weakRefCtx.get()==null){

Log.d("leak","context软引用被回收了");

return true;

}

mFlag = inexistenceOrNeedUpdate(packageName);

if (!mFlag) {// 未安装或需要更新

new AlertDialog.Builder(weakRefCtx.get()).setTitle("提示信息                  ").setMessage(dialogMessage)

.setPositiveButton("确定", new DialogInterface.OnClickListener()

2.以前:public class StringUtils {

    public static SpannableString setPhoneSpan(final Context context, String text, final int color){

         ......

         spannableString.setSpan(new ClickableSpan() {

@Override

public void onClick(View widget) {

Utils.call(context, phone);

}

......

  现在:public class StringUtils {

public static SpannableString setPhoneSpan(final Context context, String text, final int color){

         final WeakReference<Context> weakRef  =new                    WeakReference<Context>(context);

......

spannableString.setSpan(new ClickableSpan() {

@Override

public void onClick(View widget) {

                    if (weakRef.get()==null)

                        return;

Utils.call(weakRef.get(), phone);

}

......

 

 

第3方SDK要关闭:

1.  public class ShareDownLoad extends Activity implements OnClickListener,                           PlatformActionListener {          

     @Override

     protected void onDestroy() {

         super.onDestroy();

         ShareSDK.stopSDK(this);

     }

 

context跟ApplicationContext选择(能用ApplicationContext就不用context):

1.以前:ShareSDK.initSDK(this);

  现在:ShareSDK.initSDK(this.getApplicationContext());

2.以前:mgr = new AccountMgr(this);

  现在:mgr = new AccountMgr(this.getApplicationContext());

3.以前:public class Navi_Amap_new extends ActivityWithTitleBar implements                           OnDistrictSearchListener,OnClickListener,OnMapLoadedListener,OnLocationGetListener {

ttsManager =new TTSController(this);

3.现在:ttsManager =new TTSController(this.getApplicationContext());

 

 

动态广播注册了要注销

1.以前:BaseActivity:

protected void onResume() {

super.onResume();

registerMainReceiver();

sendBroadcaseUpdateCount();

}

1.现在:protected void onDestroy() {

super.onDestroy();

unregisterMainReceiver();

}

 

 

 

 

 

 

 

 

 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: