目录

  1. 内存泄漏
  • 什么是内存泄漏
  • 了解内存分配的几种策略
  • 防止内存泄漏
  • 内存泄漏的例子
  1. 如何找到项目中存在的内存泄露
  • 确定是否存在内存泄露
  • 哪些对象属于泄漏的(找出来)
  • 定位内存泄露的原因所在
  1. 如何在应用里面避免内存泄露

  2. 内存泄漏经常出现的例子

  • 静态变量引起的内存泄露
  • 非静态内部类引起内存泄露(包括匿名内部类)
  • 未移除的监听引起的内存泄漏
  • 资源未关闭引起的内存泄露情况
  • 无限循环动画

1.内存泄漏

C/C++ 自己去分配内存和释放内存——手动管理malloc和free

1.1.什么是内存泄露

内存不在GC掌控之内了。

当一个对象已经不需要再使用了,本该被回收时,而有另外一个正在使用的对象持有它的引用从而就导致对象不能被回收。这种导致了本该被回收的对象不能被回收而停留在堆内存中,就产生了内存泄漏

了解java的GC内存回收机制:某对象不再有任何的引用的时候才会进行回收。

ArrayList<String> list = new Arraylist<String>();
复制代码

(回到顶部)

1.2.了解内存分配的几种策略

1.静态的

静态的存储区:内存在程序编译的时候就已经分配好,这块的内存在程序整个运行期间都一直存在。 它主要存放静态数据、全局的static数据和一些常量。

2.栈式的

在执行函数(方法)时,函数一些内部变量的存储都可以放在栈上面创建,函数执行结束的时候这些存储单元就会自动被释放掉。 栈内存包括分配的运算速度很快,因为内置在处理器的里面的。当然容量有限。

3.堆式的

也叫做动态内存分配。有时候可以用malloc或者new来申请分配一个内存。在C/C++可能需要自己负责释放(java里面直接依赖GC机制)。

在C/C++这里是可以自己掌控内存的,需要有很高的素养来解决内存的问题。java在这一块貌似程序员没有很好的方法自己去解决垃圾内存,需要的是编程的时候就要注意自己良好的编程习惯。

区别:

1.空间和大小: 堆是不连续的内存区域,堆空间比较灵活也特别大。 栈式一块连续的内存区域,大小是有操作系统觉决定的。

2.效率: 堆管理很麻烦,频繁地new/remove会造成大量的内存碎片,这样就会慢慢导致效率低下。 对于栈的话,他先进后出,进出完全不会产生碎片,运行效率高且稳定。

public class Main{int a = 1;Student s = new Student();public void XXX(){int b = 1;//栈里面Student s2 = new Student();}
}
复制代码

1.成员变量全部存储在堆中(包括基本数据类型,引用及引用的对象实体)---因为他们属于类,类对象最终还是要被new出来的。

2.局部变量的基本数据类型和引用存储于栈当中,引用的对象实体存储在堆中。-----因为他们属于方法当中的变量,生命周期会随着方法一起结束。

我们所讨论内存泄露,主要讨论堆内存,他存放的就是引用指向的对象实体。

(回到顶部)

1.3.防止内存溢出

有时候确实会有一种情况:当需要的时候可以访问,当不需要的时候可以被回收也可以被暂时保存以备重复使用。比如:ListView或者GridView、RecyclerView.

加载大量数据或者图片的时候,图片非常占用内存,一定要管理好内存,不然很容易内存溢出。滑出去的图片就回收,节省内存。看ListView的源码——回收对象,还会重用ConvertView。如果用户反复滑动或者下面还有同样的图片,就会造成多次重复IO(很耗时),那么需要缓存---平衡好内存大小和IO,算法和一些特殊的java类。

算法:lrucache(最近最少使用先回收)

特殊的java类:利于回收,StrongReference,SoftReference,WeakReference,PhatomReference

类型 回收时机 使用 生命周期
StrongReference 强引用 从不回收 对象的一般保存 JVM停止的时候才会终止
SoftReference 软引用 当内存不足的时候 SoftReference结合
ReferenceQueue构造有效期短
内存不足时终止
WeakReference 弱引用 在垃圾回收的时候 同软引用 GC后终止
PhatomReference 虚引用 在垃圾回收的时候 合ReferenceQueue来跟踪对象被
垃圾回收期回收的活动
GC后终止

开发时,为了防止内存溢出,处理一些比较占用内存大并且生命周期长的对象的时候,可以尽量使用软引用和弱引用。 软引用比LRU算法更加任性,回收量是比较大的,你无法控制回收哪些对象。

比如使用场景:默认头像、默认图标。 ListView或者GridView、RecyclerView要使用内存缓存+外部缓存(SD卡)

(回到顶部)

1.4.内存泄露例子

单例模式导致内存对象无法释放而导致内存泄露

MainActivity在内存当中泄露了。

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);CommUtil commUtil = CommUtil.getInstance(this);}
}public class CommUtil {private static CommUtil instance;private Context context;private CommUtil(Context context){this.context = context;}public static CommUtil getInstance(Context context){if(instance == null){instance = new CommUtil(context);}return instance;}}
复制代码

这个故事告诉我们能用Application的context就用Application的CommonUtil生命周期跟MainActivity不一致,而是跟Application进程同生同死。

旋转3次:会在内存里面开辟三个MainActivity 实际上3次以上都只会有2个MainActivity。当GC回收的时候会将除了第0个和最后这一个留着其他的都会被回收。

优化两个情况: 1.主动;平时 2.被动,很卡的时候 出现问题的时候。

如果我们不知道代码内存泄露的情况,如何判断我们的项目里面有哪些是有内存泄露情况的?

1.凭借工具结合自己的经验来判断。 往往我们的app在某个时候或者某个操作以后会出现很卡的现象。

1)判断就是查看内存抖动情况

Android Monitor MAT (对Eclipse插件使用的,也有独立分析工具)

查找引用了该对象的外部对象有哪些, 然后一个一个去猜,查找可能内存泄露的嫌疑犯,依据:看(读代码和猜)他们的生命周期是否一致(可以通过快照对比),如果生命周期一致了肯定不是元凶。

排除一些容易被回收的(软引用、虚引用、弱引用)

设置监听很容易出现内存泄露

handler.post(callback)
onDestroy(){
handler.removeCallback();
}
复制代码

(回到顶部)

2.如何找到项目中存在的内存泄露

往往做项目的时候情况非常复杂,或者项目做得差不多了想起来要性能优化检查下内存泄露。

如何找到项目中存在的内存泄露的这些地方呢?

2.1.确定是否存在内存泄露

1.Android Monitors的内存分析

最直观的看内存增长情况,知道该动作是否发生内存泄露。

动作发生之前:GC完后内存1.4M; 动作发生之后:GC完后内存1.6M

2.使用MAT内存分析工具

MAT分析heap的总内存占用大小来初步判断是否存在泄露。 Heap视图中有一个Type叫做data object,即数据对象,也就是我们的程序中大量存在的类类型的对象。在data object一行中有一列是“Total Size”,其值就是当前进程中所有Java数据对象的内存总量,一般情况下,这个值的大小决定了是否会有内存泄漏。

我们反复执行某一个操作并同时执行GC排除可以回收掉的内存,注意观察data object的Total Size值,正常情况下Total Size值都会稳定在一个有限的范围内,也就是说由于程序中的的代码良好,没有造成对象不被垃圾回收的情况。

反之如果代码中存在没有释放对象引用的情况,随着操作次数的增多Total Size的值会越来越大。 那么这里就已经初步判断这个操作导致了内存泄露的情况。

(回到顶部)

2.2.先找怀疑对象(哪些对象属于泄露的)

MAT对比操作前后的hprof来定位内存泄露是泄露了什么数据对象。(这样做可以排除一些对象,不用后面去查看所有被引用的对象是否是嫌疑)

快速定位到操作前后所持有的对象哪些是增加了(GC后还是比之前多出来的对象就可能是泄露对象嫌疑犯)

技巧:Histogram中还可以对对象进行Group,比如选择Group By Package更方便查看自己Package中的对象信息。

(回到顶部)

2.3. MAT分析hprof来定位内存泄露的原因所在。(哪个对象持有了上面怀疑出来的发生泄露的对象)

  • 1.Dump出内存泄露“当时”的内存镜像hprof,分析怀疑泄露的类;
  • 2.把上面得出的这些嫌疑犯一个一个排查个遍。步骤:

(1)进入Histogram,过滤出某一个嫌疑对象类

(2)然后分析持有此类对象引用的外部对象(在该类上面点击右键List Objects--->with incoming references)

(3)再过滤掉一些弱引用、软引用、虚引用,因为它们迟早可以被GC干掉不属于内存泄露(在类上面点击右键Merge Shortest Paths to GC Roots--->exclude all phantom/weak/soft etc.references)

(4)逐个分析每个对象的GC路径是否正常,此时就要进入代码分析此时这个对象的引用持有是否合理,这就要考经验和体力了!

(比如上面的例子中:旋转屏幕后MainActivity有两个,肯定MainActivity发生泄露了,那谁导致他泄露的呢?原来是我们的CommonUtils类持有了旋转之前的那个MainActivity,那是否合理?结合逻辑判断当然不合理,由此找到内存泄露根源是CommonUtils类持有了该MainActivity实例造成的。怎么解决?罪魁祸首找到了,怎么解决应该不难了,不同情况解决办法不一样,要靠你的智慧了。)

context.getapplictioncontext()可以吗? 可以!!只要让CommonUtils类不直接只有MainActivity的实例就可以了。

一般我是最笨的方法解决
new出来对象,用完后把它 = null;这样算不算优化
假如:方法里面定义的对象,要去管吗?一般不需要管。自己=null,要自己去控制所有对象的生命周期 判断各种空指针,有点麻烦。但是在很多时候去想到主动将对象置为null是很好的习惯。
复制代码

(回到顶部)

3.如何在应用里面避免内存泄露

判断一个应用里面内存泄露避免得很好,怎么看?
当app退出的时候,这个进程里面所有的对象应该就都被回收了,尤其是很容易被泄露的(View,Activity)是否还内存当中。
可以让app退出以后,查看系统该进程里面的所有的View、Activity对象是否为0.

工具:

使用AndroidStudio--AndroidMonitor--System Information--Memory Usage查看Objects里面的views和Activity的数量是否为0.
命令行模式:

(回到顶部)

4.内存泄露经常出现的例子

内存泄露(Memory Leak):

进程中某些对象已经没有使用价值了,但是他们却还可以直接或者间接地被引用到GC Root导致无法回收。
当内存泄露过多的时候,再加上应用本身占用的内存,日积月累最终就会导致内存溢出OOM.

内存溢出(OOM):

当应用占用的heap资源超过了Dalvik虚拟机分配的内存就会内存溢出。比如:加载大图片。

4.1.静态变量引起的内存泄露

当调用getInstance时,如果传入的context是Activity的context。只要这个单利没有被释放,那么这个 Activity也不会被释放一直到进程退出才会释放。

public class CommUtil {private static CommUtil instance;private Context context;private CommUtil(Context context){this.context = context;}public static CommUtil getInstance(Context mcontext){if(instance == null){instance = new CommUtil(mcontext);}//        else{//            instance.setContext(mcontext);//        }return instance;}
复制代码

(回到顶部)

4.2.非静态内部类引起内存泄露(包括匿名内部类)

错误的示范:

public void loadData(){//隐式持有MainActivity实例。MainActivity.this.anew Thread(new Runnable() {@Overridepublic void run() {while(true){try {//int b=a;Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}}}).start();
}
复制代码

解决方案: 将非静态内部类修改为静态内部类。(静态内部类不会隐式持有外部类)

当使用软引用或者弱引用的时候,MainActivity难道很容易或者可以被GC回收吗?
GC回收的机制是什么?
当MainActivity不被任何的对象引用。 虽然Handler里面用的是软引用/弱引用,但是并不意味着不存在其他的对象引用该MainActivity。
我连MainActivity都被回收了,那他里面的Handler还玩个屁。

(回到顶部)

4.3.不需要用的监听未移除会发生内存泄露

例子1:

// tv.setOnClickListener();//监听执行完回收对象
//add监听,放到集合里面
tv.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() {@Overridepublic void onWindowFocusChanged(boolean b) {//监听view的加载,view加载出来的时候,计算他的宽高等。//计算完后,一定要移除这个监听tv.getViewTreeObserver().removeOnWindowFocusChangeListener(this);}
});
复制代码

例子2:

SensorManager sensorManager = getSystemService(SENSOR_SERVICE);
Sensor sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ALL);
sensorManager.registerListener(this,sensor,SensorManager.SENSOR_DELAY_FASTEST);
//不需要用的时候记得移除监听
sensorManager.unregisterListener(listener);
复制代码

(回到顶部)

4.4.资源未关闭引起的内存泄露情况

比如:BroadCastReceiver、Cursor、Bitmap、IO流、自定义属性attribute attr.recycle()回收。
当不需要使用的时候,要记得及时释放资源。否则就会内存泄露。

4.5.无限循环动画

没有在onDestroy中停止动画,否则Activity就会变成泄露对象。 比如:轮播图效果。

(回到顶部)

转载于:https://juejin.im/post/5c6138e651882562260d1346

1.内存优化(一)内存泄漏相关推荐

  1. Android:最全面详细的性能优化攻略(含内存优化、内存泄漏、绘制优化、布局优化、图片优化、APK优化、多线程优化、列表优化等)

    前言:佛教中有一句话:初学者的心态,拥有初学者心态是件了不起的事情.真正的大师永远怀有一颗学徒的心. 一.概述 在Android中,性能优化是细分领域中最难且也是知识面涉及最深和最广的方向之一. 更快 ...

  2. 【Android 内存优化】内存抖动 ( 垃圾回收算法总结 | 分代收集算法补充 | 内存抖动排查 | 内存抖动操作 | 集合选择 )

    文章目录 一. 垃圾回收算法总结 二. 分代收集算法补充 三. 查看 Java 虚拟机 四. 获取 Android 应用可使用最大内存 五. 内存抖动标志 六. 排查内存抖动 七. 常见的造成内存抖动 ...

  3. Android 内存优化——常见内存泄露及优化方案

    如果一个无用对象(不需要再使用的对象)仍然被其他对象持有引用,造成该对象无法被系统回 收,以致该对象在堆中所占用的内存单元无法被释放而造成内存空间浪费,这中情况就是内存泄 露. 在 Android 开 ...

  4. 内存优化之一——内存优化工具参数详解

    博客结构 1.背景 2.内存查看方式 (1).AS-Profiler (1).运行框-命令行 3.内存参数 (0)PSS (1)Java heap (2)Native Heap (3)Code (4) ...

  5. MySQL高级 - 内存优化 - InnoDB内存优化

    InnoDB 内存优化 innodb用一块内存区做IO缓存池,该缓存池不仅用来缓存innodb的索引块,而且也用来缓存innodb的数据块. innodb_buffer_pool_size 该变量决定 ...

  6. MySQL高级 - 内存优化 - MyISAM内存优化

    MyISAM 内存优化 myisam存储引擎使用 key_buffer 缓存索引块,加速myisam索引的读写速度.对于myisam表的数据块,mysql没有特别的缓存机制,完全依赖于操作系统的IO缓 ...

  7. Android内存优化之内存泄漏

    内存泄漏 内存泄漏一般有以下几种情况:单例.静态变量.Handler.匿名内部类.资源使用未关闭 单例导致的内存泄漏 单例的情况主要是因为单例的生命周期比较长,如果引用的一些资源(比如Context. ...

  8. Android内存优化11 内存泄漏常见情况2 线程持久化

    线程持久化 Java中的Thread有一个特点就是她们都是直接被GC Root所引用,也就是说Dalvik虚拟机对所有被激活状态的线程都是持有强引用,导致GC永远都无法回收掉这些线程对象,除非线程被手 ...

  9. 7 php 内存泄漏_APP内存优化之内存泄漏

    内存泄漏产生的原因 一个长生命周期的对象持有一个短生命周期对象的引用,通俗讲就是该回收的对象,因为引用问题没有被回收,最终会产生OOM. 内存分析工具 top/procrankmeinfoProcst ...

  10. android cne服务,Android内存优化-了解内存篇

    查看系统内存 文件 shell@aries:/ $ cat /proc/meminfo MemTotal: 1970216 kB MemFree: 83756 kB Buffers: 156020 k ...

最新文章

  1. python中的enumerate 函数(编号的实现方式)
  2. MapReduce-Shuffle机制运行解析
  3. git fetch和git pull
  4. YunYang1994/tensorflow-yolov3 Readme 翻译
  5. 二十八、电力窃漏电案例模型构建
  6. Acwing 271. 杨老师的照相排列
  7. 校园表白墙-带后台源码
  8. 红橙Darren视频笔记 view的invalidate调用draw方法的流程(源码分析基于api 29)
  9. Spring Boot 线程池
  10. 001 vagrant安装box
  11. ASP.NET与MVC架构区别总结
  12. web页面有哪三层构成?分别是什么?
  13. QTableView遍历
  14. 网络就好似一个个树洞
  15. 2022-05-08 Unity核心5——Tilemap
  16. html语言设置网页背景,HTML+CSS入门 设置网页中的背景图片的5个属性
  17. 【基础】Linux 常用操作
  18. c语言常考的笔试题1
  19. 九段刀客:express连接MySQL并实现增、删、改、查
  20. 阿里研究院发布《2020中国区块链发展报告》,毛球科技助力数字经济加速发展

热门文章

  1. 【第三组】用例+功能说明+技术说明
  2. (素材源码)猫猫学IOS(十六)UI之XIB自定义Cell实现团购UI
  3. THINKPHP 分页类
  4. 使用logminer进行审计 Audit by using logminer
  5. Makefile详解(一)-介绍及总述
  6. Loadrunner的Tuxedo脚本分析,基本流程和基本函数介绍
  7. Foxmail自动收取新邮件
  8. html 右边是iframe 左右结构_HTML布局之左右结构,左边固定右边跟据父元素自适应...
  9. JAVE SE 学习day_09:sleep线程阻塞方法、守护线程、join协调线程同步方法、synchronized关键字解决多线程并发安全问题
  10. 自加计数器c语言程序,计数器 加1 C语言 程序.doc