`

Android的内存机制和常见泄漏情形

阅读更多
一、 Android的内存机制

Android的程序由Java语言编写,所以Android的内存管理与Java的内存管理相似。程序员通过new为对象分配内存,所有对象在java堆内分配空间;然而对象的释放是由垃圾回收器来完成的。

那么GC怎么能够确认某一个对象是不是已经被废弃了呢?Java采用了有向图的原理。Java将引用关系考虑为图的有向边,有向边从引用者指向引用对象。线程对象可以作为有向图的起始顶点,该图就是从起始顶点开始的一棵树,根顶点可以到达的对象都是有效对象,GC不会回收这些对象。如果某个对象 (连通子图)与这个根顶点不可达(注意,该图为有向图),那么我们认为这个(这些)对象不再被引用,可以被GC回收。

二、Android的内存溢出

Android的内存溢出是如何发生的?

Android的虚拟机是基于寄存器的Dalvik,它的最大堆大小一般是16M,有的机器为24M。因此我们所能利用的内存空间是有限的。如果我们的内存占用超过了一定的水平就会出现OutOfMemory的错误。

为什么会出现内存不够用的情况呢?我想原因主要有两个:

由于我们程序的失误,长期保持某些资源(如Context)的引用,造成内存泄露,资源造成得不到释放。

保存了多个耗用内存过大的对象(如Bitmap),造成内存超出限制。



三、常见的内存泄漏

1.万恶的static
static是Java中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。所以用static修饰的变量,它的生命周期是很长的,如果用它来引用一些资源耗费过多的实例(Context的情况最多),这时就要谨慎对待了。
public class ClassName {       
    private static Context mContext;       //省略  
} 

以上的代码是很危险的,如果将Activity赋值到么mContext的话。那么即使该Activity已经onDestroy,但是由于仍有对象保存它的引用,因此该Activity依然不会被释放.

如何才能有效的避免这种引用的发生呢?

第一,应该尽量避免static成员变量引用资源耗费过多的实例,比如Context。

第二、Context尽量使用Application Context,因为Application的Context的生命周期比较长,引用它不会出现内存泄露的问题。

第三、使用WeakReference代替强引用。比如可以使用WeakReference<Context> mContextRef;




2.线程惹的祸
线程也是造成内存泄露的一个重要的源头。线程产生内存泄露的主要原因在于线程生命周期的不可控。我们来考虑下面一段代码。
    public class MyActivity extends Activity {      
    @Override      
    public void onCreate(Bundle savedInstanceState) {          
      super.onCreate(savedInstanceState);          
      setContentView(R.layout.main);          
      new MyThread().start();      
    }        
    private class MyThread extends Thread{          
    @Override          
      public void run() {              
      super.run();              
      //do somthing          
    }      
    }  
    }    


这段代码很平常也很简单,是我们经常使用的形式。我们思考一个问题:假设MyThread的run函数是一个很费时的操作,当我们开启该线程后,将设备的横 屏变为了竖屏,一般情况下当屏幕转换时会重新创建Activity,按照我们的想法,老的Activity应该会被销毁才对,然而事实上并非如此。

由于我们的线程是Activity的内部类,所以MyThread中保存了Activity的一个引用,当MyThread的run函数没有结束时,MyThread是不会被销毁的,因此它所引用的老的Activity也不会被销毁,因此就出现了内存泄露的问题。

这种线程导致的内存泄露问题应该如何解决呢?

第一、将线程的内部类,改为静态内部类。

第二、在线程内部采用弱引用保存Context引用。

另外,我们都知道Hanlder是线程与Activity通信的桥梁,我们在开发好多应用中会用到线程,有些人处理不当,会导致当程序结束时,线程并没有被销毁,而是一直在后台运行着,当我们重新启动应用时,又会重新启动一个线程,周而复始,你启动应用次数越多,开启的线程数就越多,你的机器就会变得越慢。
    package com.tutor.thread;   
    import android.app.Activity;   
    import android.os.Bundle;   
    import android.os.Handler;   
    import android.util.Log;   
    public class ThreadDemo extends Activity {   
        private static final String TAG = "ThreadDemo";   
        private int count = 0;   
        private Handler mHandler =  new Handler();   
           
        private Runnable mRunnable = new Runnable() {   
               
            public void run() {   
                //为了方便 查看,我们用Log打印出来    
                Log.e(TAG, Thread.currentThread().getName() + " " +count);   
                count++;   
                setTitle("" +count);   
                //每2秒执行一次    
                mHandler.postDelayed(mRunnable, 2000);   
            }   
               
        };   
        @Override   
        public void onCreate(Bundle savedInstanceState) {   
            super.onCreate(savedInstanceState);   
            setContentView(R.layout.main);    
            //通过Handler启动线程    
            mHandler.post(mRunnable);   
        }   
           
    }   

所以我们在应用退出时,要将线程销毁,我们只要在Activity中的,onDestory()方法处理一下就OK了,如下代码所示:
    @Override   
      protected void onDestroy() {   
        mHandler.removeCallbacks(mRunnable);   
        super.onDestroy();   
      }   





3.超级大胖子Bitmap
可以说出现OutOfMemory问题的绝大多数人,都是因为Bitmap的问题。因为Bitmap占用的内存实在是太多了,它是一个“超级大胖子”,特别是分辨率大的图片,如果要显示多张那问题就更显著了。

如何解决Bitmap带给我们的内存问题?

第一、及时的销毁。

虽然,系统能够确认Bitmap分配的内存最终会被销毁,但是由于它占用的内存过多,所以很可能会超过java堆的限制。因此,在用完Bitmap时,要 及时的recycle掉。recycle并不能确定立即就会将Bitmap释放掉,但是会给虚拟机一个暗示:“该图片可以释放了”。

第二、设置一定的采样率。

有时候,我们要显示的区域很小,没有必要将整个图片都加载出来,而只需要记载一个缩小过的图片,这时候可以设置一定的采样率,那么就可以大大减小占用的内存。






4.行踪诡异的Cursor
Cursor是Android查询数据后得到的一个管理数据集合的类,正常情况下,如果查询得到的数据量较小时不会有内存问题,而且虚拟机能够保证Cusor最终会被释放掉。
然而如果Cursor的数据量特表大,特别是如果里面有Blob信息时,应该保证Cursor占用的内存被及时的释放掉,而不是等待GC来处理。并且Android明显是倾向于编程者手动的将Cursor close掉




5.构造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对象的话,即浪费资源也浪费时间,也会使得内存占用越来越大。




分享到:
评论

相关推荐

    Android的内存机制和溢出说明

    理解并掌握Android的内存管理机制,及时释放不再使用的资源,以及正确处理上下文和位图,对于开发高效、稳定的应用至关重要。开发者应时刻警惕内存泄露,通过有效的内存优化策略,确保应用在有限的内存环境下也能...

    Android内存泄漏简介

    对于Android内存泄漏的检测和解决,开发者可以使用多种工具和方法。Android Studio中就集成有Profiler工具,可以帮助开发者监控应用程序的内存使用情况,并识别内存泄漏。此外,还有一些第三方的内存泄漏检测工具,...

    使用Android Studio检测内存泄露(LeakCanary)

    理解Android的内存管理机制和熟练使用这些工具,是提高应用性能和稳定性的重要一环。定期进行性能测试和内存分析,可以帮助开发者发现并修复潜在的问题,提升用户体验。记住,良好的编程习惯和对内存管理的深入理解...

    Android处理内存泄漏的代码例子

    在ExmLeak这个压缩包文件中,你可以找到具体的代码实现,通过分析和实践这些例子,你将更好地理解和掌握如何避免Android应用中的内存泄漏问题。记住,良好的内存管理是提升应用性能和稳定性的重要因素。

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

    总结,Android内存优化是一个系统性的工作,涉及到多个层面,开发者需要深入理解内存管理机制,熟练掌握各种优化技巧,才能打造出运行流畅、占用资源合理的应用。通过持续学习和实践,开发者可以不断提升自己在内存...

    Android 内存泄露 Mat工具分析

    Android 内存泄露是一个常见的问题,它会导致应用程序的性能下降,甚至崩溃。Memory Analyzer Tool(MAT)是一个功能强大且广泛使用的内存泄露分析工具。本文将详细介绍如何使用 MAT 分析 Android 应用程序中的内存...

    Android webview 内存泄露的解决方法

    Android webview 内存泄露的解决方法 最近在activity嵌套webview显示大量图文发现APP内存一直在涨,没法释放内存,查了很多资料,大概是webview的一个BUG,引用了activity导致内存泄漏,所以就尝试传递...

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

    常见的Android内存泄露类型包括: 1. **静态变量引用**:当一个静态变量引用了一个Activity或Context,这个对象就无法被GC回收,因为静态变量在整个应用生命周期内都存在。解决方案是避免使用静态变量持有Activity...

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

    总结来说,Android内存优化的核心在于避免内存泄露和合理分配内存资源。通过理解内存泄露的原因,运用恰当的设计模式和编程技巧,同时借助工具来监控和分析内存使用情况,可以有效地提升应用的性能,延长应用的生命...

    Android Native 内存泄漏系统化解决方案

    在 Android 平台上,C++ 内存泄漏问题的分析和定位一直是困扰开发人员的难题,因为地图渲染、导航等核心功能对性能要求很高,高德地图 APP 中存在大量的 C++ 代码。解决这个问题对于产品质量尤为重要和关键,高德...

    android内存泄露测试

    ### Android内存泄露测试详解 在Android开发中,内存管理是一项至关重要的任务,不当的内存管理会导致应用运行缓慢、崩溃甚至耗尽系统资源。内存泄露是内存管理中的一个常见问题,它指的是应用程序分配了内存但未能...

    Android App定位和规避内存泄露方法研究

    总结来说,Android应用内存泄露是一个复杂的问题,涉及到Android内存管理机制、应用设计和代码实现等多个方面。通过上述方法,我们可以有效地检测、定位和规避内存泄露问题,从而提升Android应用的性能和稳定性。

    Android 内存溢出和内存泄漏的问题

    Android 内存溢出和内存泄漏的问题 在面试中,经常有面试官会问“你知道什么是内存溢出?什么是内存泄漏?怎么避免?”通过这篇文章,你可以回答出来了。 内存溢出 (OOM)是指程序在申请内存时,没有足够的内存空间...

    Android应用程序内存泄漏的分析.pdf

    以上知识点涉及了Android内存泄漏分析的多个方面,包括理论知识、工具使用、操作实践和问题诊断技巧。理解和掌握这些知识点对Android开发人员来说是非常重要的,它可以帮助他们在日常开发中有效地避免和解决内存泄漏...

    android性能优化之内存泄露

    #### 二、Android内存管理基础知识 在讨论内存泄露之前,我们需要了解一些基本概念。Android应用进程通常被分配一定量的RAM(例如:2MB到由`getMemoryClass()`或`getLargeMemoryClass()`返回的值),当内存不足时,...

    androidStudio内存泄漏分析

    在Android应用开发中,内存泄漏...通过以上步骤和方法,开发者可以有效地在Android Studio中进行内存泄漏分析,提高应用的性能和稳定性。实践中,结合代码审查和持续的测试,可以确保应用在内存管理方面达到最佳状态。

    android应用内存监控工具

    理解Android内存机制,掌握内存监控工具的使用,结合实际项目不断优化代码,是每个Android开发者必备的技能。定期进行内存分析,对应用进行压力测试,及时发现和修复问题,可以有效提升应用的质量和用户体验。

    android内存泄露的检测和排查

    本文将深入探讨Android内存泄漏的检测和排查方法,并通过一个锁屏内存泄漏的例子进行具体分析。 首先,我们需要理解什么是内存泄漏。在计算机科学中,内存泄漏是指程序在申请内存后,无法释放已不再使用的内存空间...

    Android 内存泄露原因分析

    Android 内存泄露原因分析。通过原因分析,再接合代码可查明具体原因,发现对策

Global site tag (gtag.js) - Google Analytics