android内存泄露:1、非静态的内部类错误使用_情形1、LeakCanary内存泄漏检测库、内存泄露_内存溢出_内存抖动

目录

一、前言

二、内存泄露_内存溢出_内存抖动

三、LeakCanary 内存泄漏检测库-使用介绍

1、github上搜索 LeakCanary 工具

2、集成方式

3、案例1:非静态的内部类错误使用,导致 Activity内存泄露 

情形一:在Activity中启动子线程,子线程执行耗时操作

步骤1:新建一个 Module(模块/组件)

步骤2:写主界面 MainActivity、布局activity_main

步骤3:写业务逻辑:InnerClassActivity

4、效果展示

四、分析小结、解决方式

1、分析小结

2、解决方式

(1)方式1:自定义一个静态的类继承Thread

(2)方式2:当Activity销毁的时候,将线程的任务退出

(3)方式3:使用线程池

五、其他

六、参考资料


一、前言

今天来复习一下有关内存泄露的知识,一个是把之前学习笔记整理一下,如有一些工具版本更新会写一些当下的使用介绍。另外会参考网上比较好的博文进行修正改进一些知识点

二、内存泄露_内存溢出_内存抖动

首先我们必须对内存泄露有一个最简单的了解,就是一些对象有着有限的生命周期,当这些对象所要做的事情完成了,我们希望他们会被回收掉。但是如果有一系列对象对这个对象的引用,那么在我们期望这个对象生命周期结束的时候被GC回收的时候,它是不会被回收的,它还会占用内存,导致堆内存直升不降,这就造成了内存泄露

内存泄漏: 指对象不再使用,本该被回收,却因为有其他正在使用的对象持有该对象的引用,而无法被GC回收,它占用内存,导致堆内存直升不降的情况。

内存溢出:我们可以把app的堆内存空间想成了一个杯子,内存就是里面的水。当用户重复这个操作或者有多个不同Activity内存泄漏时,app运行一段时间堆内存超过系统规定的最大值 heapSize,杯子满了就会发生内存溢出(OOM)

内存抖动:是因为短时间内大量的对象被创建然后又被马上释放,瞬间产生大量的对象会严重占用内存区域,这块内存区域就是Young Generiation内存区域。当达到它的阀值,剩余空间不够的时候,就会触发GC(垃圾回收机制),这样刚产生的对象很快就被回收,每一次分配对象占用很少内存,但是它们叠加在一起就会造成Heap(堆内存)的压力,从而触发更多其他类型的GC,这个操作有可能会影响到帧率,并使得用户感知到性能问题。

内存溢出,内存抖动,内存泄漏这三者中这里面最严重的现象还是OOM也就是内存溢出,在开发过程中提到内存首先会想到内存溢出OOM,内存泄漏,然后才是内存抖动,内存抖动三者中严重程度比较轻。

内存溢出就是分配的内存不足以让你做有些操作,就是堆内存上有些内存没有被释放从而它会失去控制,造成程序使用的内存越来越少,导致系统运行速度减慢,严重程度下OOM,会造成整个程序的崩溃,所以为了提高APP的质量提高用户体验,我们必须避免与解决OOM。

三、LeakCanary 内存泄漏检测库-使用介绍

内存泄露分析工具:使用 square公司的 LeakCanary 分析activity的内存泄露

1、github上搜索 LeakCanary 工具

可在 github上搜索 LeakCanary 工具,可以看到 leakcanary 的源码:GitHub - square/leakcanary: A memory leak detection library for Android.

LeakCanary is a memory leak detection library for Android.    LeakCanary是一个用于Android的内存泄漏检测库。

2、集成方式

集成方式,点击链接:LeakCanary  ,如下图,

That’s it, there is no code change needed!   就这样,不需要修改代码!

集成,目前最新版本 2.2 ,只需在 build.gradle 添加如下依赖:

    // debug Implementation because LeakCanary should only run in debug builds.
    // 调试实现,因为LeakCanary只应在调试生成中运行。
    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.2'

另外添加依赖以后,有可能报如下异常,很可能是网络问题,try again 即可

当然上面还有更新的提示:

To upgrade from LeakCanary 1.6, follow the upgrade guide.  要从LeakCanary 1.6升级,请遵循升级指南。

3、案例1:非静态的内部类错误使用,导致 Activity内存泄露 

情形一:在Activity中启动子线程,子线程执行耗时操作

下面我们来看一下案例:

步骤1:新建一个 Module(模块/组件)

我们可以在项目上,新建一个Module:File--->New--->New Module

然后一直默认。。。,完成如下:

步骤2:写主界面 MainActivity、布局activity_main

布局activity_main

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="innerClass"
        android:text="非静态内部类的错误使用" />

</LinearLayout>

复习一下快捷键:option + enter

option + enter,会在 MainActivity 里面,自动生成方法:  public void innerClass(View view) { }

主界面 MainActivity

package com.yyh.testleakdemo;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void innerClass(View view) {
        startActivity( new Intent(MainActivity.this, InnerClassActivity.class));
    }

}

步骤3:写业务逻辑:InnerClassActivity

子线程执行耗时操作

package com.yyh.testleakdemo;

import android.os.Bundle;
import android.os.SystemClock;

import androidx.appcompat.app.AppCompatActivity;

/**
 * 在Activity中启动子线程,子线程执行耗时操作,
 * 但是当Activity退出的时候,如果线程的任务还没执行完,那么该线程对象就不会死掉,
 * 该线程由于是Activity的内部对象,因此对外部类(Activity)有一个隐式的强引用,
 * 从而导致Activity也无法被GC回收掉,最后导致Activity一定时间的泄露。
 */
public class InnerClassActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_inner_class);

        errorDemoMethod();

    }


    /**
     * 非静态内部类的错误使用
     */
    private void errorDemoMethod() {

        /**
         * 内部类对象,对外部类对象有一个隐式的强引用
         * 大家可复习一下:java 中的四种引用类型
         */
        new Thread(new Runnable() {
            @Override
            public void run() {
                //模拟耗时操作
                SystemClock.sleep(100*1000);
            }
        }).start();

    }



}

4、效果展示

在夜神模拟器上,运行Module模块:testleakdemo

点击 Button跳转到一个 Activity,执行耗时线程操作,然后点击返回按钮 Activity退出,

这个时候 InnerClassActivity 里面的耗时线程操作还没执行完,

然后我们可以看到 LeakCanary 工具有内存泄露的相关提示

四、分析小结、解决方式

1、分析小结

在Activity中启动子线程,子线程执行耗时操作,但是当Activity退出的时候,如果线程的任务还没执行完,那么该线程对象就不会死掉,该线程由于是Activity的内部对象,因此对外部类(Activity)有一个隐式的强引用,从而导致Activity也无法被GC回收掉,最后导致Activity一定时间的泄露。

2、解决方式

解决该泄露的方式:就是我们应该谨慎在Activity中开辟耗时的子线程,如果使用了那么也应该保证当Activity销毁的时候,将线程的任务退出。或者我们可以自定义一个静态的类继承Thread,然后再使用。因为只有非静态的内部类对象才会对外部类有隐式的强引用。

(1)方式1:自定义一个静态的类继承Thread

package com.yyh.testleakdemo;

import android.os.Bundle;
import android.os.SystemClock;
import androidx.appcompat.app.AppCompatActivity;

/**
 * 自定义一个静态的类继承Thread
 */
public class Correct1Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_correct1);

        MyThread myThread = new MyThread();
        myThread.start();

    }


    public static class MyThread extends Thread{
        @Override
        public void run() {
            //模拟耗时操作
            SystemClock.sleep(100*1000);
        }
    }


}

(2)方式2:当Activity销毁的时候,将线程的任务退出

package com.yyh.testleakdemo;

import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;

/**
 * 当Activity销毁的时候,将线程的任务退出
 */
public class Correct2Activity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_correct2);

        correctDemoMethod();

    }


    private Thread thread;

    private void correctDemoMethod() {
        thread = new Thread(new Runnable() {
            @Override
            public void run() {

                //模拟耗时操作
                //SystemClock.sleep(100*1000);

                //有关 SystemClock.sleep 和 thread.sleep 区别,
                //网上资料很多,这里就不多讲了

                try {
                    thread.sleep(100*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();

                    Log.v("InnerClassActivity@@@","InterruptedException");
                    return;

                }

            }
        });

        thread.start();

    }


    @Override
    protected void onDestroy() {
        super.onDestroy();

        //thread.stop();//Caused by: java.lang.UnsupportedOperationException
        thread.interrupt();

    }


}

有关 SystemClock.sleep 和 thread.sleep 区别,可参考博文:

SystemClock.sleep(long ms)与Thread.sleep(long millis)分析_wtu178的博客-CSDN博客

(3)方式3:使用线程池

其实,项目中会集成个性化的线程池,保证不开启过多的线程进行多线程操作(硬件层CPU 支持的核心数有限,过多的线程只能更加分割 cpu 时间片,无法达到更好的效果),也会有自己的新线程创建、管理和结束操作。在工作中我们将Android Thread分两种线程进行管理,即 UI Thread (最多 Thread 数由 cpu 核心数决定)和 Bkg Thread (最大Thread数为1)。

这块知识点有时间再整理一下。。。

五、其他

square公司,对我们 android来说,是一个大名鼎鼎的公司,它开源了相当多的开源框架。

比如说:LeakCanary(内存泄漏检测库)、OKHttp(网络请求库)、Picasso(图片库)、Retrofit(网络请求接口的封装)

square开源官网:Square Open Source

然后写博文的时候,偶然看到这篇博文,这哥们挺逗的😆

Android之超级开源公司square(picasso、okhttp、leakcanary、retrofit)简单介绍

这句话说的挺对:官方文档写的超级明白!所以一定要从官方的一手资料学习,注意二手资料容易带你到沟里!

然后我也顺便搜了搜,大牛JakeWharton的相关网址:

JakeWharton的github

JakeWharton (Jake Wharton) · GitHub

Butterknife(黄油刀)——View注入框架:将Android视图和回调绑定到字段和方法

GitHub - JakeWharton/butterknife: Bind Android views and callbacks to fields and methods.

JakeWharton的博客:

Jake Wharton

六、参考资料

android内存泄漏 OOM查找总结

Android异常和性能优化 - OOM异常

深入剖析Android内存泄露原理-视频

深入剖析Android内存泄露原理-视频个人笔记_simplebam-CSDN博客

深入剖析Android内存泄露原理

请别只做拿来主义者,如果觉得写的不错、对你有用,留下你的足迹:点赞 或 评论 支持下!

一直被模仿从未被超越,你们的支持是我们这些写博客博主们的动力!我们将继续分享干货!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

被开发耽误的大厨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值