android内存分配及泄露

Java 内存分配策略java

Java 程序运行时的内存分配策略有三种,分别是静态分配,栈式分配,和堆式分配,对应的,三种存储策略使用的内存空间主要分别是静态存储区(也称方法区)、栈区和堆区。程序员

•静态存储区(方法区):主要存放静态数据、全局 static 数据和常量。这块内存在程序编译时就已经分配好,而且在程序整个运行期间都存在,生命周期和整个应用相同。shell

•栈区 :当方法被执行时,方法体内的局部变量都在栈上建立,并在方法执行结束时这些局部变量所持有的内存将会自动被释放。由于栈内存分配运算内置于处理器的指令集中,效率很高,可是分配的内存容量有限。在栈区通常存储一些基本的数据类型(int, short, long, byte, float, double, boolean, char),类方法,对象地址,常量等等。数据库

•堆区 :又称动态内存分配,一般就是指在程序运行时直接 new 出来的内存。这部份内存在不使用时将会由 Java 垃圾回收器来负责回收,缺点是要在运行时分配内存,存取速度慢。         一般存储一些经过new来建立的对象,可是该对象的地址通常保存在栈中.编程

查看内存使用状况网络

可使用AS的monitor实时观察app运行是内存耗费状况。 使用adb shell meminfo $包名或 $进程号 来显示该应用的内存耗费状况app

GC的原理: 异步

Java的内存管理就是对象的分配和释放问题。在 Java 中,程序员须要经过关键字 new 为每一个对象申请内存空间 (基本类型除外),全部的对象都在堆 (Heap)中分配空间。另外,对象的释放是由 GC 决定和执行的。在 Java 中,内存的分配是由程序完成的,而内存的释放是由 GC 完成的,这种收支两条线的方法确实简化了程序员的工做。但同时,它也加剧了JVM的工做。这也是 Java 程序运行速度较慢的缘由之一。由于,GC 为了可以正确释放对象,GC 必须监控每个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC 都须要进行监控。 监视对象状态是为了更加准确地、及时地释放对象,而释放对象的根本原则就是该对象再也不被引用。 为了更好理解 GC 的工做原理,咱们能够将对象考虑为有向图的顶点,将引用关系考虑为图的有向边,有向边从引用者指向被引对象。另外,每一个线程对象能够做为一个图的起始顶点,例如大多程序从 main 进程开始执行,那么该图就是以 main 进程顶点开始的一棵根树。在这个有向图中,根顶点可达的对象都是有效对象,GC将不回收这些对象。若是某个对象 (连通子图)与这个根顶点不可达(注意,该图为有向图),那么咱们认为这个(这些)对象再也不被引用,能够被 GC 回收。socket

什么是Java中的内存泄露:async

在上面描述了GC的原理,只要从根节点出发便利有向图,只要不可达的节点都是GC对象。然而在现实的操做中,有些对象经过根节点可达,可是这些对象是无用的 ,即程序再也不会使用该对象。若是出现上述两种状况就能够断定内存泄露了。

检测内存泄露的工具:

leakcanary 一个开源的工具,在检测对象被destory后一一检查其内部的对象是否还有未释放的引用,若是有则提示泄露

常见的内存泄露:

一、静态集合类引发内存泄漏: 像HashMap、Vector等的使用最容易出现内存泄露,这些静态变量的生命周期和应用程序一致,他们所引用的全部的对象Object也不能被释放,由于他们也将一直被Vector等引用着。

Vector v = new Vector(10); 
for (int i = 1; i < 100; i++) {     
    Object o = new Object();     
    v.add(o);    
    o = null;   
}

 

二、当集合里面的对象属性被修改后,再调用remove()方法时不起做用。

public static void main(String[] args)
{
    Set set = new HashSet();
    Person p1 = new Person("唐僧","pwd1",25);
    Person p2 = new Person("孙悟空","pwd2",26);
    Person p3 = new Person("猪八戒","pwd3",27);
    set.add(p1);
    set.add(p2);
    set.add(p3);
    System.out.println("总共有:"+set.size()+" 个元素!"); 
    p3.setAge(2); 
    set.remove(p3); 
    set.add(p3); 
    System.out.println("总共有:"+set.size()+" 个元素!"); 
   for (Person person : set)
   {
        System.out.println(person);
   }
}

 

三、监听器 在java 编程中,咱们都须要和监听器打交道,一般一个应用当中会用到不少监听器,咱们会调用一个控件的诸如addXXXListener()等方法来增长监听器,但每每在释放对象的时候却没有记住去删除这些监听器,从而增长了内存泄漏的机会。

四、各类链接 好比数据库链接(dataSourse.getConnection()),网络链接(socket)和io链接,除非其显式的调用了其close()方法将其链接关闭,不然是不会自动被GC 回收的。对于Resultset 和Statement 对象能够不进行显式回收,但Connection 必定要显式回收,由于Connection 在任什么时候候都没法自动回收,而Connection一旦回收,Resultset 和Statement 对象就会当即为NULL。可是若是使用链接池,状况就不同了,除了要显式地关闭链接,还必须显式地关闭Resultset Statement 对象(关闭其中一个,另一个也会关闭),不然就会形成大量的Statement 对象没法释放,从而引发内存泄漏。这种状况下通常都会在try里面去的链接,在finally里面释放链接。

五、内部类和外部模块的引用

private static Object inner;
   void createInnerClass() {
    class InnerClass {
    }
    inner = new InnerClass();
}

 

六、单例模式 不正确使用单例模式是引发内存泄漏的一个常见问题,单例对象在初始化后将在JVM的整个生命周期中存在(以静态变量的方式),若是单例对象持有外部的引用,那么这个对象将不能被JVM正常回收,致使内存泄漏


七、一些静态的的变量持有局部对象
在程序中因为静态变量是和应用的生存周期保持一致的,可是在类中经过new一些局部对象赋值给这些静态变量时,当当前类销毁时这些局部对象也同样会
跟着销毁,可是因为静态变量持有这些对象的引用,跟应用的生存周期保持一致,因此这种状况下会致使内存泄漏。
常见的一些赋值给静态变量的对象:
经过findViewById获取的view赋值给static view;
new一个对象赋值给static 变量
static activity 的赋值

 8.还有一些异步操做致使的泄露

在Android中有不少种异步操做,handler操做;new一个线程Runnable,asyncTask等等,经过这些方法开启一个线程工做,若是有时候线程耗时很长而当前activity已经被destory了,因此这些线程还一直存在可是已是没什么必要了。

如何避免内存泄露:

一、对于一些静态的变量,能够经过弱引用赋予其对象,这样在进行GC的时候,弱引用的对象同样可以被回收。

 private static WeakReference<MainActivity> activityReference;
 void setStaticActivity() {
     activityReference = new WeakReference<MainActivity>(this);
}

这种状况一样适用于其余相似的static变量。

二、对于一些匿名类致使的泄露可使用静态内部类来代替,这样因为该类是静态的跟应用拥有相同的生命周期

private static class NimbleTask extends AsyncTask<Void, Void, Void> {
    @Override protected Void doInBackground(Void... params) {
        while(true);
    }
}

void startAsyncTask() {
    new NimbleTask().execute();
}

对于开启的线程也能够若是在activity被destory的时候,不须要进行执行能够在周期函数destory中将该thread进行中断。

private Thread thread;

@Override
public void onDestroy() {
    super.onDestroy();
    if (thread != null) {
        thread.interrupt();
    }
}

void spawnThread() {
    thread = new Thread() {
        @Override public void run() {
            while (!isInterrupted()) {
            }
        }
    }
    thread.start();
}

三、对于一些监听的操做,须要在destory中所有进行释放

 

参考文章:

http://www.jianshu.com/p/c5ac51d804fa

https://juejin.im/entry/5747d70fc26a38006c5744d5

相关文章
相关标签/搜索