• Android 内存优化&内存泄漏处理


    一:匿名内部类/非静态内部类

    匿名内部类的泄漏原因:匿名内部类会隐式地持有外部类的引用.当外部类被销毁时,内部类并不会自动销毁,因为内部类并不是外部类的成员变量,
    它们只是在外部类的作用域内创建的对象,所以内部类的销毁时机和外部类的销毁时机是不同的,
    所以会不会取决与对应对象是否存在被持有的引用.

     案例一:有名内部类导致内存泄漏

    匿名内部类/非静态内部类,Android开发经常会继承实现 Activity 或者 Fragment 或者 View。
    如果你使用了匿名类,而又被异步线程所引用,那得小心,如果没有任何措施同样会导致内存泄漏的.

    runnable1 和 runnable2 的区别就是,runnable2 使用了匿名内部类,我们看看引用时的引用内存:

    可以看到,runnable1 是没有什么特别的。但 runnable2 多出了一个 MainActivity 的引用,若是这个引用再传入到一个异步线程,此线程在和Activity生命周期不一致的时候,也就造成了Activity的泄露。

    在 MainActivity 内部创建了一个非静态内部类的单例TestInnerBad,不过这种写法却会造成内存泄漏,非静态内部类默认会持有外部类MainActivity 的引用,导致 MainActivity的内存资源不能正常回收.

    解决方案:

     将该内部类TestInnerBad设为静态内部类或将该内部类抽取出来封装成一个单例,

    案例二:匿名内部类导致内存泄漏

    1. public class TestActivity {
    2. public static void main(String[] args) {
    3. //java的匿名内部类
    4. new Thread(new Runnable() {
    5. @Override
    6. public void run() {
    7. }
    8. }).start();
    9. }
    10. }

    匿名内部类Runnable持有外部类TestActivity 的引用, 当外部类TestActivity销毁时,匿名内部类Runnable 会导致内存泄漏,

    解决方案:

    创建一个静态内部类实现Runnable接口或者使用lambda表达式

    1. public class TestActivity {
    2. Runnable runnable1
    3. public static void main(String[] args) {
    4. runnable1 = new MyRunnable()
    5. new Thread(runnable1).start();
    6. }
    7. //创建一个静态内部类
    8. private static class MyRunnable implements Runnable{
    9. @Override
    10. public void run(){
    11. }
    12. }
    13. @overRide
    14. onDestroy(){
    15. runnable1=null
    16. }
    17. }
    1. java: 把Runnable转为lambda写法 避免了内存泄漏
    2. new Thread(() -> { }).start();

     

    1. kotlin: 把Runnable转为lambda写法 避免了内存泄漏
    2. Thread({}).start()
    3. 或者
    4. Thread {}.start()

    案例三:匿名内部类导致内存泄漏

    1. public class MyActivity extends Activity {
    2. private Button button;
    3. @Override
    4. protected void onCreate(Bundle savedInstanceState) {
    5. super.onCreate(savedInstanceState);
    6. button = new Button(this);
    7. button.setOnClickListener(new View.OnClickListener() {
    8. @Override
    9. public void onClick(View v) {
    10. // do something
    11. }
    12. });
    13. setContentView(button);
    14. }
    15. }

    匿名内部类OnClickListener持有了外部类MyActivity的引用,如果MyActivity被销毁之前,button没有被清除,
     就会导致MyActivity无法被垃圾回收。(此处可以将Button 看作是自己定义的一个对象,
     一般解法是将button对象置为空)

     注意事项:在Activity销毁时,应该将所有持有Activity引用的对象设置为null。

    二:静态变量

    当我们的成员变量是 static 的时候,那么它的生命周期将和整个app的生命周期一致。这必然会导致一系列问题,

     解决方案:

    不要在类初始时初始化静态成员。可以考虑 lazy初始化(延迟加载)

    三:单例Singleton

    单例的静态特性,使得它的生命周期和应用的生命周期会一样长,所以一旦使用有误,小心无限制的持有Activity的引用而导致内存泄漏。

    1. public class MySingleton {
    2. private static MySingleton instance;
    3. private Context context;
    4. private MySingleton(Context context){
    5. this.context=context
    6. }
    7. public static MySingleton getInstance(Context context) {
    8. if (instance == null) {
    9. instance = new MySingleton(Context context);
    10. }
    11. return instance;
    12. }
    13. // ...
    14. }

      如果我们传入的是 Activity 的 Context,当这个 Context 所对应的 Activity 退出的时候,由于该 Context 的引用被单例对象所持有,其生命周期等于整个应用程序的生命周期,所以当前 Activity 退出时它的内存并不会回收,这造成的内存泄漏

    解决方案: 使用 Application 的 Context 避免内存泄漏

    1. public class MySingleton {
    2. private static MySingleton instance;
    3. private Context context;
    4. private MySingleton(Context context){
    5. // this.context=context
    6. // 使用 Application 的 Context 避免内存泄漏
    7. this.context=context.getApplicationContext()
    8. }
    9. public static MySingleton getInstance(Context context) {
    10. if (instance == null) {
    11. synchronized (MySingleton.class) {
    12. if (instance == null) {
    13. instance = new MySingleton(Context context);
    14. }
    15. }
    16. }
    17. return instance;
    18. }
    19. public static void releaseInstance() {
    20. instance = null;
    21. }
    22. // ...
    23. }

     

    四:Context

    如果需要使用 Context,推荐的使用 Application 的 Context。当然,Application 的 context  不是万能的,所以也不能随便乱用,对于有些地方则必须使用 Activity 的 Context,对于Application,Service,Activity三者的 Context 的应用场景如下:

  • 相关阅读:
    互联网、政务外网、政务专网、政务内网的区别
    Alexa染料标记RNA核糖核酸|RNA-Alexa 514|RNA-Alexa 488|RNA-Alexa 430
    bash和dash的区别(及示例)
    #力扣:LCP 06. 拿硬币@FDDL
    一米ip流量池系统
    10046 trace 产生方法
    java基于springboot+Vue社区居民医疗健康网站
    内存与IO访问原理
    3.3.2JavaScript网页编程——WebAPI(JS之DOM网页特效篇)
    【后端】经典面试题——GET和POST的区别
  • 原文地址:https://blog.csdn.net/qq_33552379/article/details/136273033