`
阿尔萨斯
  • 浏览: 4110408 次
社区版块
存档分类
最新评论

android GC内存泄露问题

 
阅读更多

转自:http://www.cnblogs.com/devinzhang/archive/2012/01/28/2330738.html

1. android内存泄露概念

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

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

内存泄露示例:

复制代码
/*此时,所有的Object对象都没有被释放,因为变量v引用这些对象。实际上这些对象已经是无用的,但还被引用,GC就无能为力了(事实上GC认为它还有用),这一点是导致内存泄漏最重要的原因。*/
Vector v = new Vector(10);      
for (int i = 1; i < 100; i++)      {       
    Object o = new Object();       
    v.add(o);       
    o = null;     
 }
复制代码

  循环申请Object对象,并将所申请的对象放入一个Vector中,如果仅仅释放对象本身,但因为Vector仍然引用该对象,所以这个对象对GC来说是不可回收的。因此,如果对象加入到Vector后,还必须从Vector中删除,最简单的方法就是将Vector对象设置为null。

  总的来说,内存管理中的内存泄漏产生的主要原因:保留下来却永远不再使用的对象引用。

2.引起内存泄露的情况

1)资源对象没关闭造成的内存泄露

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

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

2)一些不良代码成内存压力

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

  a.Bitmap没调用recycle()

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

虽然recycle()从源码上看,调用它应该能立即释放Bitmap的主要内存,但是测试结果显示它并没能立即释放内存。但是我它应该还是能大大的加速Bitmap的主要内存的释放。

  b.构造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对象的话,即浪费时间,也造成内存垃圾,给垃圾回收增加压力,如果垃圾回收来不及的话,虚拟机将不得不给该应用进程分配更多的内存,造成不必要的内存开支。

layout.xml

 <?xml version="1.0" encoding="utf-8" ?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="60dip">
  <ImageView android:id="@+id/icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:paddingLeft="9px" /> 
  <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@id/icon" android:textAppearance="?android:attr/textAppearanceMedium" android:paddingLeft="9px" /> 
  </RelativeLayout>

能引起内存泄露的代码: BadAdapter.java

复制代码
public class BadAdapter extends BaseAdapter {
    ......

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Log.d("MyAdapter", "Position:" + position + "---"
                + String.valueOf(System.currentTimeMillis()));
        final LayoutInflater inflater = (LayoutInflater) mContext
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View v = inflater.inflate(R.layout.list_item_icon_text, null);
        ((ImageView) v.findViewById(R.id.icon)).setImageResource(R.drawable.icon);
        ((TextView) v.findViewById(R.id.text)).setText(mData[position]);
        return v;
    }
}
复制代码

修正优化示例代码示例代码:GoodAdapter.java

复制代码
public class GoodAdapter extends BaseAdapter {

    ......

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Log.d("MyAdapter", "Position:" + position + "---"
                + String.valueOf(System.currentTimeMillis()));
        ViewHolder holder;
        if (convertView == null) {
            final LayoutInflater inflater = (LayoutInflater) mContext
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(R.layout.list_item_icon_text, null);
            holder = new ViewHolder();
            holder.icon = (ImageView) convertView.findViewById(R.id.icon);
            holder.text = (TextView) convertView.findViewById(R.id.text);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }
        holder.icon.setImageResource(R.drawable.icon);
        holder.text.setText(mData[position]);
        return convertView;
    }

    static class ViewHolder {
        ImageView icon;
        TextView text;
    }
}
复制代码

MainActivity.java

复制代码
public class MainActivity extends ListActivity {
    private BadAdapter/GoodAdapter mAdapter;

    private String[] mArrData;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mArrData = new String[1000];
        for (int i = 0; i < 1000; i++) {
            mArrData[i] = "Google IO Adapter";
        }
        mAdapter = new BadAdapter/GoodAdapter(this, mArrData);
        setListAdapter(mAdapter);
    }
}
复制代码

3)ThreadLocal使用不当

  如果我们粗暴的把ThreadLocal设置null,而不调用remove()方法或set(null),那么就可能造成ThreadLocal绑定的对象长期也能被回收,因而产出内存泄露。

分享到:
评论

相关推荐

    android的GC内存泄露问题

    本篇文章主要介绍了android GC内存泄露问题,具有一定的参考价值,有需要的可以了解一下。

    解决Android使用Handler造成内存泄露问题

    一、什么是内存泄露?  Java使用有向图机制,通过GC自动检查内存中的对象(什么时候检查由虚拟机决定),如果... Android中使用Handler造成内存泄露的原因 private Handler handler = new Handler() { public voi

    Android内存泄漏信息介绍(英文)

    Android内存泄漏说明文件 英文 08-06 13:53:42.082: D/dalvikvm(12152): GC_CONCURRENT freed 1842K, 12% free 15412K/17399K, paused 1ms+3ms

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

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

    Android应用内存泄漏的定位、分析与解决策略

    对于不同的语言平台来说,进行标记回收内存的算法是不一样的,像Android(Java)则采用GC-Root的标记回收算法。下面这张图就展示了Android内存的回收管理策略(图来自Google2011的IO大会)图中的每个圆节点代表对象...

    详解Android性能优化之内存泄漏

    在应用中内出现一次两次的内存泄漏获取不会出现什么影响,但是在应用长时间使用以后,若是存在大量的Activity无法被GC回收的话,最终会导致OOM的出现。那么我们在这就来分析一下导致内存泄漏的常见因素并且如何去...

    Android内存泄露分析.pptx

    导致内存泄露的原因: 在android开发中,jvm具有自动回收的机制,会不定时不定期的去清理无用的被占用的内存,而在理论上不需要再被使用的内存,在实际中却还持有对这一块内存的引用,导致GC时,不会被回收释放掉,...

    Android 内存泄露

    Android内存泄漏指的是进程中某些对象(垃圾对象)已经没有使用价值了,但是它们却可以直接或间接地引用到gc roots导致无法被GC回收。无用的对象占据着内存空间,使得实际可使用内存变小,形象地说法就是内存泄漏了...

    Android内存泄漏的轻松解决方法

    内存管理的目的就是让我们在开发过程中有效避免我们的应用程序出现内存泄露的问题。内存泄露相信大家都不陌生,我们可以这样理解:「没有用的对象无法回收的现象就是内存泄露」。 如果程序发生了内存泄露,则会带来...

    Android 5.1 WebView内存泄漏问题及快速解决方法

    在排查项目内存泄漏过程中发现了一些由WebView引起的内存泄漏,经过测试发现该部分泄漏只会出现在android 5.1及以上的机型。虽然项目使用WebView的场景并不多,但秉承着一个泄漏都不放过的精神,我们肯定要把它给...

    Android Studio+MAT实战内存泄漏

    对于内存泄漏,在Android中如果不注意的话,还是很容易出现的,尤其是在Activity中,比较容易出现,下面我就说下自己是如何查找内存泄露的。 首先什么是内存泄漏? 内存泄漏就是一些已经不使用的对象还存在于内存之...

    android内存泄露:1、LeakCanarys内存泄漏检测库、非静态的内部类错误使用

    但是如果有一系列对象对这个对象的引用,那么在我们期望这个对象生命周期结束的时候被GC回收的时候,它是不会被回收的,它还会占用内存,导致堆内存直升不降,这就造成了内存泄露。 内存泄漏: 指对象不再使用,...

    android面试题

    View重绘和内存泄露的好像是面试经常问的问题1. View的刷新: 在需要刷新的地方,使用handle.sendmessage发送信息,然后在handle的getmessage里面执行invaliate或者postinvaliate. 2. GC内存泄露

    Android程序的内存泄漏与规避方法

    引言  Android应用程序中内存使用的问题经常容易被忽视,...因此很多程序员认为在Java中不必担心内存泄漏的问题,然而实际并非如此,Java中仍然存在着内存泄漏。Android应用程序运行在嵌入式系统中,而嵌入式系统中内

    Android中的内存泄漏

    什么是内存泄漏 长生命周期的对象持有了短生命周期的对象,从而导致短生命周期的对象不能被释放 垃圾回收机制 垃圾回收机制分为:引用计数法、可达性分析法 引用计数法(有循环引用的问题)...内存泄漏的问题 内存泄漏并

    Android内存优化杂谈

    Android内存优化是我们性能优化工作中比较重要的一环,这里其实主要包括两方面的工作: 1、优化RAM,即降低运行时内存。这里的目的是防止程序发生OOM异常,以及降低程序由于内存过大被LMK机制杀死的...内存泄露:简单

    android优化

    Android 的Dalvik VM 在基础方面和Sun JVM 没有什么大的区别仅仅是字节码的优化,我们要知道什么时候用gc 什么时候用以及到底用不用finalization,因为Java 对内存的分配只需要new 开发者不需要显示的释放内存,但是...

    谈一谈Android内存泄漏问题

    内存泄漏:是指内存得不到GC的及时回收,从而造成内存占用过多,从而导致程序Crash,也就是常说的OOM。 一、static 先来看下面一段代码 public class DBHelper { private static DBHelper db= null; private ...

    通信与网络中的Android程序的内存泄漏与规避方法

    引言  Android应用程序中内存使用的问题经常容易被忽视,...因此很多程序员认为在Java中不必担心内存泄漏的问题,然而实际并非如此,Java中仍然存在着内存泄漏。Android应用程序运行在嵌入式系统中,而嵌入式系统中内

Global site tag (gtag.js) - Google Analytics