2019独角兽企业重金招聘Python工程师标准>>>

前言

不少人认为JAVA程序,因为有垃圾回收机制,应该没有内存泄露。

其实如果我们一个程序中,已经不再使用某个对象,但是因为仍然有引用指向它,垃圾回收器就无法回收它,当然该对象占用的内存就无法被使用,这就造成了内存泄露。如果我们的java运行很久,而这种内存泄露不断的发生,最后就没内存可用了。当然java的,内存泄漏和C/C++是不一样的。如果java程序完全结束后,它所有的对象就都不可达了,系统就可以对他们进行垃圾回收,它的内存泄露仅仅限于它本身,而不会影响整个系统的。C/C++的内存泄露就比较糟糕了,它的内存泄露是系统级,即使该C/C++程序退出,它的泄露的内存也无法被系统回收,永远不可用了,除非重启机器。

Android的一个应用程序的内存泄露对别的应用程序影响不大。为了能够使得Android应用程序安全且快速的运行,Android的每个应用程序都会使用一个专有的Dalvik虚拟机实例来运行,它是由Zygote服务进程孵化出来的,也就是说每个应用程序都是在属于自己的进程中运行的。Android为不同类型的进程分配了不同的内存使用上限,如果程序在运行过程中出现了内存泄漏的而造成应用进程使用的内存超过了这个上限,则会被系统视为内存泄漏,从而被kill掉,这使得仅仅自己的进程被kill掉,而不会影响其他进程(如果是system_process等系统进程出问题的话,则会引起系统重启)。

一、引用没释放造成的内存泄露

1.1、注册没取消造成的内存泄露

这种Android的内存泄露比纯java的内存泄露还要严重,因为其他一些Android程序可能引用我们的Anroid程序的对象(比如注册机制)。即使我们的Android程序已经结束了,但是别的引用程序仍然还有对我们的Android程序的某个对象的引用,泄露的内存依然不能被垃圾回收。

比如

假设我们希望在锁屏界面(LockScreen)中,监听系统中的电话服务以获取一些信息(如信号强度等),则可以在LockScreen中定义一个PhoneStateListener的对象,同时将它注册到TelephonyManager服务中。对于LockScreen对象,当需要显示锁屏界面的时候就会创建一个LockScreen对象,而当锁屏界面消失的时候LockScreen对象就会被释放掉。

但是如果在释放LockScreen对象的时候忘记取消我们之前注册的PhoneStateListener对象,则会导致LockScreen无法被垃圾回收。如果不断的使锁屏界面显示和消失,则最终会由于大量的LockScreen对象没有办法被回收而引起OutOfMemory,使得system_process进程挂掉。

虽然有些系统程序,它本身好像是可以自动取消注册的(当然不及时),但是我们还是应该在我们的程序中明确的取消注册,程序结束时应该把所有的注册都取消掉。

1.2、集合容器对象没清理造成的内存泄露

我们通常把一些对象的引用加入到了集合容器(比如ArrayList)中,当我们不需要该对象时,并没有把它的引用从集合中清理掉,这样这个集合就会越来越大。如果这个集合是static的话,那情况就更严重了。

1.3、Context泄漏
所谓的 Context泄漏,其实更多的是指 Activity泄露,这是一个很隐晦的OutOfMemoryError的情况。
先看一个Android官网提供的例子: 
private static Drawable sBackground ;    
@Override  
protected void onCreate ( Bundle state ) {  
  super . onCreate ( state );  
  TextView label = new TextView ( this );  
  label . setText ( "Leaks are bad" );  
  if ( sBackground == null ) {  
    sBackground = getDrawable ( R . drawable . large_bitmap );  
  }  
  label . setBackgroundDrawable ( sBackground );  
  setContentView ( label );  
}  

这段代码效率很快,但同时又是极其错误的; 在第一次屏幕方向切换时它泄露了一开始创建的Activity。当一个Drawable附加到一个 View上时, View会将其作为一个 callback设定到Drawable上。上述的代码片段,意味着这个静态的Drawable拥有一个TextView的引用,
而TextView又拥有Activity(Context类型)的引用,换句话说,Drawable拥有了更多的对象引用。即使Activity被 销毁,内存仍然不会被释放。 
另外,对Context的引用超过它本身的生命周期,也会导致该Context无法回收,从而导致内存泄漏。所以尽量使用Application这种Context类型。 
这种Context拥有和应用程序一样长的生命周期,并且不依赖Activity的生命周期。如果你打算保存一个长时间的对象, 
并且其需要一个 Context,记得使用Application对象。你可以通过调用Context.getApplicationContext()或 Activity.getApplication()轻松得到Application对象。 
最近遇到一种情况引起了Context泄漏,就是在Activity销毁时,里面有其他线程没有停。 
总结一下避免Context泄漏应该注意的问题: 
1.尽量使用Application这种Context类型。 
2.注意对Context的引用不要超过它本身的生命周期。 
3.慎重的对Context使用“static”关键字。 
4.Context里如果有线程,一定要在onDestroy()里及时停掉。 
1.4、static关键字的滥用
当类的成员变量声明成static后,它是属于类的而不是属于对象的,如果我们将很大的资源对象(Bitmap,context等)声明成static,那么这些资源不会随着对象的回收而回收, 会一直存在,所以在使用static关键字定义成员变量的时候要慎重。
1.5、WebView对象没有销毁
当我们不要使用WebView对象时,应该调用它的destory()函数来销毁它,并释放其占用的内存,否则其占用的内存长期也不能被回收,从而造成内存泄露

1.6、GridView的滥用
GridView和ListView的实现方式不太一样。GridView的View不是即时创建的,而是全部保存在内存中的。比如一个GridView有100项,虽然我们只能看到10项,但是其实整个100项都是在内存中的。

二、资源对象没关闭造成的内存泄露

资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及时关闭它们,以便它们的缓冲及时回收内存。它们的缓冲不仅存在于java虚拟机内,还存在于java虚拟机外。如果我们仅仅是把它的引用设置为null,而不关闭它们,往往会造成内存泄露。因为有些资源性对象,比如SQLiteCursor(在析构函数finalize(),如果我们没有关闭它,它自己会调close()关闭),如果我们没有关闭它,系统在回收它时也会关闭它,但是这样的效率太低了。因此对于资源性对象在不使用的时候,应该调用它的close()函数,将其关闭掉,然后才置为null.在我们的程序退出时一定要确保我们的资源性对象已经关闭。

程序中经常会进行查询数据库的操作,但是经常会有使用完毕Cursor后没有关闭的情况。如果我们的查询结果集比较小,对内存的消耗不容易被发现,只有在常时间大量操作的情况下才会复现内存问题,这样就会给以后的测试和问题排查带来困难和风险。

三、一些不良代码成内存压力

有些代码并不造成内存泄露,但是它们,或是对没使用的内存没进行有效及时的释放,或是没有有效的利用已有的对象而是频繁的申请新内存,对内存的回收和分配造成很大影响的,容易迫使虚拟机不得不给该应用进程分配更多的内存,造成不必要的内存开支。

3.1、Bitmap没调用recycle()

Bitmap对象在不使用时,我们应该先调用recycle(),然后才它设置为null.

虽然Bitmap在被回收时可以通过BitmapFinalizer来回收内存。但是调用recycle()是一个良好的习惯

在Android4.0之前,Bitmap的内存是分配在Native堆中,调用recycle()可以立即释放Native内存。

从Android4.0开始,Bitmap的内存就是分配在dalvik堆中,即JAVA堆中的,调用recycle()并不能立即释放Native内存。但是调用recycle()也是一个良好的习惯。

可以通过dumpsys meminfo命令查看一个进程的内存情况。

示例:adb shell "dumpsys meminfo com.lenovo.robin"

运行结果。

Applications Memory Usage (kB):

Uptime: 18696550 Realtime: 18696541

** MEMINFO in pid 7985 [com.lenovo.robin] **

native   dalvik    other    total

size:     4828     5379      N/A    10207

allocated:     4073     2852      N/A     6925

free:       10     2527      N/A     2537

(Pss):      608      317     1603     2528

(shared dirty):     2240     1896     6056    10192

(priv dirty):      548       36     1276     1860

Objects

Views:        0        ViewRoots:        0

AppContexts:        0       Activities:        0

Assets:        2    AssetManagers:        2

Local Binders:        5    Proxy Binders:       11

Death Recipients:        1

OpenSSL Sockets:        0

SQL

heap:        0         MEMORY_USED:        0

PAGECACHE_OVERFLOW:        0         MALLOC_SIZE:        0

关于内存统计的更多内容请参考《Android内存泄露利器(内存统计篇)》

3.2、构造Adapter时,没有使用缓存的 convertView

以构造ListView的BaseAdapter为例,在BaseAdapter中提共了方法:

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

来向ListView提供每一个item所需要的view对象。初始时ListView会从BaseAdapter中根据当前的屏幕布局实例化一定数量的view对象,同时ListView会将这些view对象缓存起来。当向上滚动ListView时,原先位于最上面的list item的view对象会被回收,然后被用来构造新出现的最下面的list item。这个构造过程就是由getView()方法完成的,getView()的第二个形参 View convertView就是被缓存起来的list item的view对象(初始化时缓存中没有view对象则convertView是null)。

由此可以看出,如果我们不去使用convertView,而是每次都在getView()中重新实例化一个View对象的话,即浪费时间,也造成内存垃圾,给垃圾回收增加压力,如果垃圾回收来不及的话,虚拟机将不得不给该应用进程分配更多的内存,造成不必要的内存开支。ListView回收list item的view对象的过程可以查看:

android.widget.AbsListView.java --> void addScrapView(View scrap) 方法。

示例代码

public View getView(int position, View convertView, ViewGroup parent) {

View view = new Xxx(...);

... ...

return view;

}

修正示例代码

public View getView(int position, View convertView, ViewGroup parent) {

View view = null;

if (convertView != null) {

view = convertView;

populate(view, getItem(position));

...

} else {

view = new Xxx(...);

...

}

return view;

}

3.3、ThreadLocal使用不当

如果我们粗暴的把ThreadLocal设置null,而不调用remove()方法或set(null),那么就可能造成ThreadLocal绑定的对象长期也不能被回收,因而产出内存泄露。
关于此的更多内容请参考《ThreadLocal的内存泄露》
四、JNI代码的内存泄露
关于此的详细内容请参考《 JNI引用与垃圾回收》

转载于:https://my.oschina.net/u/924450/blog/179514

Android内存泄漏简介相关推荐

  1. Android内存泄漏leakcanary2.7

    一.内存泄漏 1.1 内存泄漏简介   内存泄漏,是指一些对象已经不再需要,但是无法成功被gc回收,导致这部分内存无法释放,造成资源的浪费.当大量的内存泄漏堆积时,严重时还容易间接引发OOM.   例 ...

  2. Android 内存泄漏检测开源库LeakCanary 研究

    1. Android 内存空间不足会引发的问题 1.1 异常 1.2 卡顿 1.3 从 Java 堆内存超限这个问题开始 2. 内存优化着手点 2.1 检测 RAM usage 2.2 进程 2.3 ...

  3. android释放acitity内存,Android 内存泄漏分析与解决方法

    在分析Android内存泄漏之前,先了解一下JAVA的一些知识 1. JAVA中的对象的创建 使用new指令生成对象时,堆内存将会为此开辟一份空间存放该对象 垃圾回收器回收非存活的对象,并释放对应的内 ...

  4. Android内存泄漏就这样产生了

    为什么80%的码农都做不了架构师?>>>    1.资源对象没关闭造成的内存泄漏 描述: 资源性对象比如(Cursor,File文件等)往往都用了一些缓冲,我们在不使用的时候,应该及 ...

  5. Android内存泄漏总结

    Android 内存泄漏总结 箫鉴哥 2016-01-19 13:44:26 浏览42979 评论10 android 性能优化 阿里技术协会 内存管理 内存泄漏 摘要: Android 内存泄漏总结 ...

  6. Android 内存泄漏总结文档

    2019独角兽企业重金招聘Python工程师标准>>> Android 内存泄漏总结 内存管理的目的就是让我们在开发中怎么有效的避免我们的应用出现内存泄漏的问题.内存泄漏大家都不陌生 ...

  7. Android内存泄漏分析及调试

    2019独角兽企业重金招聘Python工程师标准>>> Android内存泄漏分析及调试 分类: Android2013-10-25 11:31 5290人阅读 评论(5) 收藏 举 ...

  8. Android 内存泄漏分析指北

    android 内存泄漏分析指北 简单来说内存泄漏就是当对象不再被应用程序使用,但是垃圾回收器却不能移除它们,因为它们正在被引用 java 垃圾回收介绍: Java 虚拟机运行所管理的内存包括以下几个 ...

  9. Android内存泄漏的检测流程、捕捉以及分析

    https://blog.csdn.net/qq_20280683/article/details/77964208 Android内存泄漏的检测流程.捕捉以及分析 简述: 一个APP的性能,重度关乎 ...

最新文章

  1. 牛客 2018年长沙理工大学第十三届程序设计竞赛 G-逃离迷宫
  2. python交换函数_python a,b = b,一个实现?它与C ++交换函数有何不同?
  3. python列表切片口诀-Python-100例(7-8) 复制列表 打印乘法口诀
  4. 如何让LINUX程序运行在多CPU?
  5. 跨域请求,关于后端session会话丢失的解决办法
  6. reflow 和 repaint
  7. 五、【SAP-PM模块】服务采购业务流程
  8. 光端机使用时出现了故障怎样定位和处理
  9. jquery表单属性筛选元素
  10. Application Virtualization 4.5 部署之(二)
  11. 狼奔代码生成器使用说明
  12. 隐藏ribbon按钮
  13. 无法更新标识列 wechatid_宿州售楼处精神堡垒生产厂家-弈博标识标牌制造
  14. Java NIO之DatagramChannel
  15. Debian上如何打开关闭端口
  16. 【第一章】google guava 之 Joiner学习
  17. do.....while(0)让你虎躯一震的用法
  18. chrome设置微信ua_模拟UA实现访问只能在微信上打开的网页
  19. 初中使用计算机,初中生使用计算器的利和弊!!!急!!!!!!!!!!!!!!!!!!!!
  20. 安卓app开发工具_手机APP是怎么开发的,需要学习哪些知识?

热门文章

  1. windows server 2008虚拟化技术一览
  2. 迷你信使(MM)的软件设计界面
  3. Attention Model(注意力模型)思想初探
  4. [特征工程系列五]基于蒙特卡洛搜索树的半自动特征工程方案
  5. 基于php编写的新闻类爬虫,插入WordPress数据库
  6. VB.net版机房收费系统——结账功能实现(调错与优化)
  7. 在NAS设备上用NFS服务为RAC数据库和集群件存储oracle文件时的mount选项
  8. 怎样的中奖算法能让人信服(转)
  9. ora-01033:oracle initializationgorshutdown 处理方法
  10. sourcesafe管理phpproj文件的补充说明(downmoon)