Android内存管理哪些事

标签: android 内存管理 | 发表时间:2013-12-16 17:26 | 作者:kingbo203
出处:http://www.iteye.com

上一篇文章总结了一下内存分析的方法,这次聊聊如何出现解决和避免内存相关的问题。

 上一篇文中我们提到,出现OOM通常是因为有内存泄漏或是内存使用不当(分配了过多的内存),在Android的早期时代,内存真是非常的珍贵啊,大部分是手机只有32或24M的heapsize,记得曾经有一个项目,有图片处理的逻辑,运行几分种就会OOM,很多人就说是有内存泄露,后来排查了半天,发现是处理的速度跟不上加载的速度,消耗了太多的内存导致,现在主流的手机heapsize基本上都有64M,应该来讲,只要没有内存泄漏,很少会发生OOM的,并且,从Android 3.0开始,引入了largeheap,就是说你在manifest的application TAG中加入 android:largeHeap="true" 之后就可以让你的应用申请更多的内存(每个手机定义的大小不尽相同),标准的heapsize和largeHeap都可以通过方法查询的到:

 

                ActivityManager mgr = (ActivityManager)  MainActivity.this
                        .getSystemService(Context.ACTIVITY_SERVICE);
                Log.d(TAG,  "MemoryClass is:"+mgr.getMemoryClass()+",large class is:"+mgr.getLargeMemoryClass());
 你的应用到底有多少的heapsize可以通过:

 

 final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
来获取(注意,这里的单位是K)
我测试了几个手机,Galaxy nexus 可以申请到256M的heapsize,256M啊,同学们,这是多么的奢侈啊,但是人家谷歌说的很清楚,用这个标签一定要慎重,除非你是真的需要这么多内存,不能把这玩意作为快速解决OOM的手段,如果你的内存使用过多,还是先慎重的分析一下你的内存到哪里去了,因为这会导致GC的时间较长并且会影响系统的性能,原文如下:
However, the ability to request a large heap is intended only for a small set of apps that can justify the need to consume more RAM (such as a large photo editing app). Never request a large heap simply because you've run out of memory and you need a quick fix—you should use it only when you know exactly where all your memory is being allocated and why it must be retained. Yet, even when you're confident your app can justify the large heap, you should avoid requesting it to whatever extent possible. Using the extra memory will increasingly be to the detriment of the overall user experience because garbage collection will take longer and system performance may be slower when task switching or performing other common operations
无论手机的内存有多大,请记住,这只是手机,一定要珍惜内存。
那怎么样能够减少内存的使用呢,我个人认为,可以从以下几个方面入手:
  • 避免内存泄漏
  • 能不创建的对象就不要创建
  • 合理管理对象生命周期
  • 科学设计应用背景图片(尽量使用.9.png图片)

避免内存泄漏

首先是一定要避免内存泄漏,如果存在内存泄漏,多少内存也不够用,关于Java为什么会有内存泄漏以及内存泄露的定义IBM网站上有一篇文章说的非常到位: http://www.ibm.com/developerworks/cn/java/l-JavaMemoryLeak/

这里摘录一下该文的重点:

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

图2

GC是如何工作的呢:

文章也很好的交代了这一点:

为了更好理解GC的工作原理,我们可以将对象考虑为有向图的顶点,将引用关系考虑为图的有向边,有向边从引用者指向被引对象。另外,每个线程对象可 以作为一个图的起始顶点,例如大多程序从main进程开始执行,那么该图就是以main进程顶点开始的一棵根树。在这个有向图中,根顶点可达的对象都是有 效对象,GC将不回收这些对象。如果某个对象 (连通子图)与这个根顶点不可达(注意,该图为有向图),那么我们认为这个(这些)对象不再被引用,可以被GC回收。

图1

这些问题阐述清楚之后我们来看看Android平台常见的导致内存泄漏的原因(基本上都是在以往的项目中碰到的):

1.registerContentObserver未解注册(观察者模式要小心,切记适时解注册);

2.没有关闭(一定要在finally中关闭)cursor,这里要多说一点的是,我们其实可以用AsyncQueryHandler或者 CursorLoader 等数据库异步加载框架避免在activity中对cursor的管理;

3.Adapter bindview实现不当,这个属于比较低级的,但是还是有人会这么干,就是在bindview里不去判断convertview直接创建,关于怎么实现这里不再多讲;

4.单例模式注册listener未解注册;

5.mTelePhonyMgr.listen(mListener,PhoneStateListener.LISTEN_NONE);貌似这么调用之后不能解注册,不知道4.0以后的版本有没有修改。

大部分的问题都跟static有关,一旦声明成static,除非我们主动的设置为null,否则是无法被回收的,像单例,观察者这些设计模式,通常都有生命周期较长的一方,使用的时候一定要小心。

能不创建的对象就不要创建

其实很多时候我们一个不小心的举动就会创建过多的对象,比如下面的这段代码:

public class DataBean {
    private String mData = "";
    private ArrayList<DataB> mDataBs = new ArrayList<DataB>();
}

 这就是很不好的编程习惯,如果这些字段为空,就无端的多创建了很多的对象,如果我们在内存中又加载了大量这样的对象,。。。。浪费啊。

再比如说,通常我们有一些比较复杂的界面,布局文件会比较庞大,常常会通过逻辑的需要来控制不同元素的可见性,这时候如果我们采用 viewstub,在需要的时候再加载相应的布局,就可以避免创建无用的对象,也可以加快activity的加载速度。:

写道
<ViewStub android:id="@+id/stub"
android:inflatedId="@+id/subTree"
android:layout="@layout/mySubTree"
android:layout_width="120dip"
android:layout_height="40dip" />

 

另外,有时候我们可以通过复用对象来达到少创建对象的目地,比如当我们有大量的数据需要插入到数据库时,

我们就可以这么做:

private ContentValues mValues = new ContentValues();

    public void inserVaules(String value1,int vaule2){
        mValues.clear();
        mValues.put(key, value1);
        mValues.put(key, vaule2);
        this.getContentResolver().insert(url, values);
       
    }

 

  合理管理对象生命周期:

这个通常在架构阶段就要考虑清楚,我们有哪些东西是要常驻内存的,有哪些是伴随界面存在的,尤其是那些缓存和业务逻辑层的manager,在做缓存的时候一定要平衡好内存和性能,关于一些缓存资源的释放推荐看一下Android新引入的 onTrimMemory() 方法,目前源码中已经有部分的逻辑实现了该方法

比如系统luncher中:

@Override
public void onTrimMemory(int level) {
 super.onTrimMemory(level);
 if (level >= ComponentCallbacks2.TRIM_MEMORY_MODERATE) {
 mAppsCustomizeTabHost.onTrimMemory();
 }
 }

 

    public void onTrimMemory() {
        mContent.setVisibility(GONE);
        // Clear the widget pages of all their subviews - this will trigger the widget previews
        // to delete their bitmaps
        mAppsCustomizePane.clearAllWidgetPages();
    }

  AppsCustomizePagedView.java

 

 

   public void clearAllWidgetPages() {
        cancelAllTasks();
        int count = getChildCount();
       for (int i = 0; i < count; i++) {
       View v = getPageAt(i);
            if (v instanceof PagedViewGridLayout) {
                ((PagedViewGridLayout) v).removeAllViewsOnPage();
                mDirtyPageContent.set(i, true);
            }
        }
    }

    private void cancelAllTasks() {
        // Clean up all the async tasks
        Iterator<AppsCustomizeAsyncTask> iter = mRunningTasks.iterator();
        while (iter.hasNext()) {
            AppsCustomizeAsyncTask task = (AppsCustomizeAsyncTask) iter.next();
           task.cancel(false);
            iter.remove();
            mDirtyPageContent.set(task.page, true);

            // We've already preallocated the views for the data to load into, so clear them as well
            View v = getPageAt(task.page);
            if (v instanceof PagedViewGridLayout) {
                ((PagedViewGridLayout) v).removeAllViewsOnPage();
            }
        }
        mDeferredSyncWidgetPageItems.clear();
        mDeferredPrepareLoadWidgetPreviewsTasks.clear();
    }

 

  科学设计应用背景图片

这个不用做过多的阐述,凡事都是有代价的,太炫太复杂太大太多的背景图片会导致应用耗用过多的内存。

另外,谷歌也给出了一些节省内存的 tips:

比如stringbuffer啊,尽量用int基本类型而不是Integer等。

 

 



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [android 内存管理] 推荐:

Android内存管理

- - CSDN博客推荐文章
首先Android内存管理机制相当复杂,想要讲清楚比较困难;其次对于绝大多数用户来说,只关心内存够不够用,至于内存如何管理的这种技术细节,不是用户需要去考虑的,写这样一个专题有没有意义. 毕竟我们是用手机,不是来研究手机的. 最后的顾虑是这个专题会不会太技术化了,绝大部分用户不会看或者说缺乏相应的背景.

Android内存管理之道

- - CSDN博客移动开发推荐文章
相信一步步走过来的Android从业者,每个人都会遇到OOM的情况. 如何避免和防范OOM的出现,对于每一个程序员来说确实是一门必不可少的能力. 今天我们就谈谈在Android平台下内存的管理之道,开始今天的主题之前,先再次回顾两个概念. 内存泄漏:对象在内存heap堆中中分配的空间,当不再使用或没有引用指向的情况下,仍不能被GC正常回收的情况.

Android内存管理哪些事

- - 移动开发 - ITeye博客
上一篇文章总结了一下内存分析的方法,这次聊聊如何出现解决和避免内存相关的问题.  你的应用到底有多少的heapsize可以通过:  . 来获取(注意,这里的单位是K). 我测试了几个手机,Galaxy nexus 可以申请到256M的heapsize,256M啊,同学们,这是多么的奢侈啊,但是人家谷歌说的很清楚,用这个标签一定要慎重,除非你是真的需要这么多内存,不能把这玩意作为快速解决OOM的手段,如果你的内存使用过多,还是先慎重的分析一下你的内存到哪里去了,因为这会导致GC的时间较长并且会影响系统的性能,原文如下:.

Sun JDK 1.6内存管理

- 小丑鱼 - 淘宝JAVA中间件团队博客
分为使用篇、调优篇和实现篇三个部分,使用篇为填鸭式,调优篇为pattern式,实现篇为启发式,三个PPT的目标为:. 1.掌握Sun JDK的内存区域的划分;. 2.掌握Sun JDK垃圾收集器的使用方法和触发时机;. 4.掌握一些基本的GC调优的方法;. 5.了解自动内存管理的常见实现方法,以及Sun JDK所做的优化.

c++之内存管理

- - CSDN博客推荐文章
c++使用3种不同解决方案存储数据,区别是数据保留在内存中的时间. 两种存储持续性为自动:自动变量和寄存器变量(register没有内存地址)(堆栈). 在函数外定义的变量和使用关键字static定义的变量的存储持续性都为静态.. 外部链接性,内部链接性和无链接性. 所有静态变量都有下面的两个初始化特征:.

[译] HotSpot JVM 内存管理

- - IT瘾-dev
HotSpot JVM 内存管理. 更新时间:2018-03-28. 关于 JVM 内存管理或者说垃圾收集,大家可能看过很多的文章了,笔者准备给大家总结下. 这算是系列的第一篇,接下来一段时间会持续更新. 本文主要是翻译《 Memory Management in the Java HotSpot Virtual Machine》白皮书的前四章内容,这是 2006 的老文章了,当年发布这篇文章的还是 Sun Microsystems,以后应该会越来越少人记得这家曾经无比伟大的公司了.

Memcached内存管理机制浅析

- 圣斌 - basic coder
Memcached的内存管理在网上也可以搜集到不少不错的文章,新浪的这篇《Memcached深度分析》讲得不错,读别人的文章还是不如自己直接去读源码分析源码来得直接,这里写一下我阅读Memcached源码时对于Memcached内存管理机制的理解. Memcached的代码结构很简单,从main()函数入口进去之后便是几个模块的初始化函数,和内存管理相关的主要有两个函数,一个是assoc_init(),这个是用来初始化哈希表的,关于这个哈希表的作用留在外面讨论,另一个是slabs_init(),该函数用来初始化slab,下面先来讨论一下slab机制.

JVM内存管理学习总结(一)

- - CSDN博客互联网推荐文章
I.JVM进程的生命周期. JVM实例的生命周期和java程序的生命周期保持一致,即一个新的程序启动则产生一个新的JVM进程实例,程序结束则JVM进程实例伴随着消失. 那么程序启动和程序终止就是JVM实例生命周期的两个边界,两个边界点可以这么理解:一个拥有程序入口(main函数)的class在执行main方法时,相应的JVM就被创建了(即JVM生命周期的起点),当由此main函数启动的所有非守护线程都终止时,JVM即退出(JVM实例生命周期的终点).

[原]GC与显式内存管理

- - Dev in Nightmare
    C++复兴的话题至今已被鼓吹两年有余,Herb Sutter和Bjarne Stroustrup等大牛们也为C++带来了大步伐的革新. 然而,从这两年的效果而言,C++的复兴并没有发生. 一方面随着世界经济的动荡,IT行业也出现了一定程度的衰退;另一方面这也是个新兴语言如雨后春笋的时代,尤其是web平台上,CoffeeScript、Dart、TypeScript等,新人阶前花更红.

java内存管理【转】权威

- - 互联网 - ITeye博客
  说起垃圾收集(Garbage Collection,GC),大部分人都把这项技术当做Java语言的伴生产物. 事实上,GC的历史远远比Java久远,1960年诞生于MIT的Lisp是第一门真正使用内存动态分配和垃圾收集技术的语言. 当Lisp还在胚胎时期时,人们就在思考:.    GC需要完成的三件事情:.