编辑推荐:

本文来自于csdn,本文主要从java的内存模型讲起,最终举出几个内存泄露的例子和解决方案。

java运行时内存模型

具体信息:http://gityuan.com/2016/01/09/java-memory/

回收算法

JVM回收算法主要有两种

1.引用计数法:每个对象有一个引用计数器,当对象被引用一次时计数器加一,引用失效计数器减一。当计数器为0时表示对象可以被回收。(由于无法解决相互引用问题而被废弃)

2.可达性算法:从GC ROOT节点开始遍历,可以连通的对象都是活对象。无法到达的对象可以被回收。

可以作为GC ROOT节点的对象

虚拟机栈的栈帧的局部变量表引用的对象

本地方法栈JNI引用的对象

方法区的静态变量和常量所引用的对象

例子

public

class GCDemo {

public static void main(String[] args) {

GcObject obj1 = new GcObject();

GcObject obj2 = new GcObject();

obj1.instance = obj2;

obj2.instance = obj1;

obj1 = null;

obj2 = null;

}

}

class GcObject {

public Object instance = null;

}

引用计数法内存图

1.step1:GcObject实例1的引用计数+1,目前为1

2.step2:GcObject实例2的引用计数+1,目前为1

3.step3:GcObject实例2的引用计数+1,目前为2

4.step4:GcObject实例1的引用计数+1,目前为2

5.obj1 = null:GcObject实例1引用计数-1,目前为1

6.obj2 = null:GcObject实例2引用计数-1,目前为1

到此为止,GcObject实例1和2都无法使用,但是引用计数不为0,发生内存泄露。

可达性算法内存图

由可以被作为GC ROOT对象来看,虚拟机栈中obj1和obj2是两个GC ROOT起点,由于最终都将obj1与obj2设置为了null。因此GcObject1和2都无法可达,因此可以被回收。

在Java中,内存泄漏就是存在一些被分配的对象,这些对象有下面两个特点,首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;其次,这些对象是无用的,即程序以后不会再使用这些对象。如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。

Android中常见的内存泄露

Android中最常见的内存泄露是关于Activity的内存泄漏。其核心问题在于在Activity生命周期之外仍有其引用。从内存模型角度来讲,它在GC

ROOTING时可达,但是它的onDestroy已经被执行。既它从我们期待的行为上来说应该被标识为可以被回收。但它仍然可达。以下就列出最常见的几种情况,请记住核心矛盾:Activity生命周期之外仍有其引用

Handler导致的内存泄漏

public

class SampleActivity extends Activity {

private final Handler mLeakyHandler = new

Handler() {

@Override

public void handleMessage(Message msg) {

// ...

}

}

@Override

protected void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

// 延时10分钟发送一个消息

mLeakyHandler.postDelayed(new Runnable() {

@Override

public void run() { }

}, 60 * 10 * 1000);

// 返回前一个Activity

finish();

}

}

思考上面代码,它会发生严重的内存泄露。首先,mLeakyHandler被声明为了一个匿名内部类(自己思考,上述代码有几个匿名内部类),它隐式的持有了外部类Activity的强引用。之后,handler发出了一个10分钟的延时消息,接着Activity杀掉了自己。问题的关键在于handler发出的消息Message会在消息队列里存i在10分钟。它持有发出消息的handler的引用。而handler又持有Activity的强引用。这就导致Activity在其生命周期之外仍有强引用,发生了严重的内存泄露。

注意,问题的核心在于内部类以及匿名内部类都会隐式的持有外部类的强引用。这就导致一个类的生命不在由一个因素控制,变为了多个因素。在我们认为该结束的时候而没有产生正确的行为。解决的方案很简单,也很通用:

用静态内部类。

在静态内部类根据需求使用弱引用修饰需要引用的外部类资源。

public

class SampleActivity extends Activity {

/**

* 匿名类的静态实例不会隐式持有他们外部类的引用

*/

private static final Runnable sRunnable = new

Runnable() {

@Override

public void run() {

}

};

private final MyHandler mHandler = new MyHandler(this);

@Override

protected void onCreate(Bundle savedInstanceState)

{

super.onCreate(savedInstanceState);

// 延时10分钟发送一个消息.

mHandler.postDelayed(sRunnable, 60 * 10 * 1000);

// 返回前一个Activity

finish();

}

/**

* 静态内部类的实例不会隐式持有他们外部类的引用。

*/

private static class MyHandler extends Handler

{

private final WeakReference

mActivity;

public MyHandler(SampleActivity activity)

{

mActivity = new WeakReference(activity);

}

@Override

public void handleMessage(Message msg) {

SampleActivity activity = mActivity.get();

if (activity != null) {

// ...

}

}

}

}

static变量导致的内存泄漏

static变量在进程启动的时候被分配内存空间。在进程被杀死的时候释放内存空间。因此,它的生命周期是贯穿整个应用的生命周期的。也就是说,当你的Activity被静态变量染指到。那么又会导致Activity在其生命周期之外仍有强引用,发生内存泄露。

常见的场景如下:

static

Context

static View

两者的本质是一样的,因为View内部持有Context的引用。第一种最常见的就是单例模式的实现,将具体的Activity的Context传递进去构造单例对象,导致泄露。第二种是当View需要加载的资源比较大时,想一劳永逸。而产生内存泄露。

解决方法依然通用且简单,在Activity声明周期外使用Context尽量去使用Application的Context

Activiity内部声明的静态资源要及时释放, 在相应的声明周期方法中做好收尾工作

注册与解注册

在andorid开发中,我们经常会在Activity的onCreate中注册广播接受器、EventBus等,如果忘记成对的使用反注册,可能会引起内存泄漏。开发过程中应该养成良好的相关,在onCreate或onResume中注册,要记得相应的在onDestroy或onPause中反注册。

创建与关闭

在android中,资源性对象比如Cursor、File、Bitmap、视频等,系统都用了一些缓冲技术,在使用这些资源的时候,如果我们确保自己不再使用这些资源了,要及时关闭,否则可能引起内存泄漏。因为有些操作不仅仅只是涉及到Dalvik虚拟机,还涉及到底层C/C++等的内存管理,不能完全寄希望虚拟机帮我们完成内存管理。

在这些资源不使用的时候,记得调用相应的类似close()、destroy()、recycler()、release()等函数,这些函数往往会通过jni调用底层C/C++的相应函数,完成相关的内存释放。

Cursor对象及时关闭

IO对象及时关闭

集合造成的内存泄漏

Vector

v = new Vector(10);

for (int i = 1; i < 100; i++) {

Object o = new Object();

v.add(o);

o = null;

}

画个简单的内存图就可以看出在栈中分配的v指向了在堆中分配的十个空间,虽然每次置为null,但是将V作为GC

ROOT的起点仍然可达每一个在堆中的Object对象,而我们本意是将每一个Object对象置为了null。

解决方案很简单:v = null 。本质上是断掉GC ROOT的起点。

android中内存泄露,Android中的内存泄露相关推荐

  1. android如何避免内存泄露,Android开发中应该避免的内存泄露

    一.背景和目的: 目前许多开发人员在Android开发过程中,较少关注实现细节和内存使用,容易会造成内存泄露,导致程序OOM. 本文会通过代码向大家介绍在Android开发过程中常见的内存泄露. 二. ...

  2. android oom 检测工具,Android中UI检测、内存泄露、OOM、等优化处理

    对Android整个优化分析,非常棒,下面是自己的积累笔记.可直接看原文. 界面检测工具: Fps: GPU检测, 使用Lint进行资源及冗余UI布局等优化 (很强大,布局的冗余) Memory检测G ...

  3. android中momery检测,Android性能优化第(二)篇---Memory Monitor检测内存泄露

    版权声明:本文为LooperJing原创文章,转载请注明出处! 多练习多写代码.jpg 上篇说了一些性能优化的理论部分,主要是回顾一下,有了理论,小平同志又讲了,实践是检验真理的唯一标准,对于内存泄露 ...

  4. android的内存泄露有几种,Android中几种有可能会导致内存泄露的情况

    8种机械键盘轴体对比 本人程序员,要买一个写代码的键盘,请问红轴和茶轴怎么选? 1.Static静态成员导致的内存泄露 将占用大量内存空间的变量声明为static静态类型.当Activity被销毁的时 ...

  5. android 存储空间监控,浅谈 Android 内存监控(中)

    前言 在上篇 浅谈 Android 内存监控(上) 中,我们聊了 LeakCanary,微信的 Matirx 和美团的 Probe,它们各自有不同的应用场景,例如,在开发测试环境,我们会偏向用 Lea ...

  6. java内部类内存泄漏,Android中常见的内存泄漏和解决方案

    什么是内存泄漏? 简单点说,就是指一个对象不再使用,本应该被回收,但由于某些原因导致对象无法回收,仍然占用着内存,这就是内存泄漏. 为什么会产生内存泄漏,内存泄漏会导致什么问题? 相比C++需要手动去 ...

  7. java 单例 内存释放_周小抒 – 梦想仗剑走天涯 | Android中关于Context单例模式引起的内存泄漏以及解决方案...

    内存溢出与内存泄漏的区别: 内存溢出是由于应用所消耗的内存或者应用申请的内存超出了虚拟机分配的内存,也就是内存不够用了. 内存泄漏是某个不再使用对象由于被其他实例引用,导致不能被GC回收,而导致的内存 ...

  8. android 集合 内存泄漏,Android内存泄漏第二课--------(集合中对象没清理造成的内存泄漏 )...

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

  9. 【Android 逆向】修改运行中的 Android 进程的内存数据 ( 使用 IDA 分析要修改的内存特征 | 根据内存特征搜索修改点 | 修改进程内存 )

    文章目录 一.使用 IDA 分析要修改的内存特征 二.根据内存特征搜索修改点 三.修改进程内存 一.使用 IDA 分析要修改的内存特征 在前的博客 [Android 逆向]逆向修改游戏应用 ( 分析应 ...

最新文章

  1. EXTJS之Ext.util.Observable自定义事件
  2. 岗位内推 | 腾讯云小微自然语言技术中心招聘NLP研究型实习生
  3. 居然之家:核心业务系统全面上云,采用PolarDB替代传统商业数据库
  4. AbstractListView源码分析6
  5. Docker修改空间大小
  6. 开源 java CMS - FreeCMS2.2 系统配置
  7. pytorch学习笔记(三十四):MiniBatch-SGD
  8. 中断占据CPU时间的计算问题
  9. 3dmax如何建模(一)
  10. Crontab 在线生成器 - Linux计划任务
  11. 记录一次使用python来实现雷神加速器自动恢复时间和暂停时间的脚本
  12. word 图片导入不翻转_如何在Microsoft Word中翻转图片
  13. WannaCry爆发一年后,EternalBlue的威胁仍然存在
  14. 将精确到时分秒的日期截取到年月日
  15. 服务器丢包都有哪些原因?
  16. oracle数据库与实例的区别与联系
  17. 史上最小白之CNN 以及 TextCNN详解
  18. 【SSH框架】--Hibernate入门
  19. C#基于Emgucv的圆形识别定位方法
  20. navicat for mysql注册码分享

热门文章

  1. Matlab---傅里叶变换---通俗理解(二)
  2. AR# 58294 Zynq-7000 SoC: PS SPI 控制器文档升级
  3. python事件触发机制_Python3-事件驱动、IO模型和触发方式
  4. python随机取列表元素_python random从集合中随机选择元素
  5. 在线GUI编译分享|8ms模拟器的使用
  6. php 升序 排序字符串,PHP asort():对数组排序(升序),并保持索引关系
  7. MT76x8的多网口与GPIO复用配置
  8. 百度开发者中心全新升级 | 文末六一送福利
  9. vue elementui 切换语言
  10. webscoket绑定php uid,Think-Swoole之WebSocket客户端消息解析与使用SocketIO处理用户UID与fd关联...