`
liu86th
  • 浏览: 117935 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

[转]内存管理 --- 内存使用不当小结

 
阅读更多

 

 

因为我们的应用程序能够使用的内存有限,所以在编写代码的时候需要特别注意内存

使用问题。如下是一些常见的内存使用不当的情况。 


 ⑴查询数据库没有关闭游标 

描述: 

    程序中经常会进行查询数据库的操作,但是经常会有使用完毕 Cursor后没有关闭的情

况。如果我们的查询结果集比较小,对内存的消耗不容易被发现,只有在常时间大量操

作的情况下才会复现内存问题,这样就会给以后的测试和问题排查带来困难和风险。 

  

示例代码: 

 

 

 

Cursor cursor = getContentResolver().query(uri ...); 
if (cursor.moveToNext()) { 
    ... ...  
} 

 

 

修正示例代码: 

 

 

Cursor cursor = null; 
try { 
    cursor = getContentResolver().query(uri ...); 
    if (cursor != null && cursor.moveToNext()) { 
        ... ...  
   } 
} finally { 
    if (cursor != null) { 
        try {  
            cursor.close(); 
        } catch (Exception e) { 
            //ignore this 
        } 
    } 
}  

 

 

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

回收list item的view对象的过程可以查看: 

android.widget.AbsListView.java --> void addScrapView(View scrap) 方法。 

 

示例代码:

 

 

 

public View getView(int position, View convertView, ViewGroup parent) { 
    View view = new Xxx(...); 
    ... ... 
    return view; 
} 

 

 

修正示例代码: 

 

 

public View getView(int position, View convertView, ViewGroup parent) { 
    View view = null; 
    if (convertView != null) { 
        view = convertView; 
        populate(view, getItem(position)); 
        ... 
    } else { 
        view = new Xxx(...); 
        ... 
    } 
    return view; 
}
 

 

⑶ Bitmap对象不在使用时调用 recycle()释放内存 

 

描述:

 

 

 有时我们会手工的操作Bitmap对象,如果一个Bitmap对象比较占内存,当它不在被

使用的时候,可以调用Bitmap.recycle()方法回收此对象的像素所占用的内存,但这不是

必须的,视情况而定。可以看一下代码中的注释: 

    /** 

     * Free up the memory associated with this bitmap's pixels, and mark the 

     * bitmap as "dead", meaning it will throw an exception if getPixels() or 

     * setPixels() is called, and will draw nothing. This operation cannot be 

     * reversed, so it should only be called if you are sure there are no 

     * further uses for the bitmap. This is an advanced call, and normally need 

     * not be called, since the normal GC process will free up this memory when 

     * there are no more references to this bitmap. 

     */ 

 

  private void releaseBitmap(){ 
       
         //在这,我们分别预存储了第一个和最后一个可见位置之外的3个
位置的bitmap 
       
         //即dataCache中始终只缓存了(M=6+Gallery当前可见view的个
数)M个bitmap 
       
                     int start = mGallery.getFirstVisiblePosition()-3; 
       
                     int end = mGallery.getLastVisiblePosition()+3; 
       
                     Log.v(TAG, "start:"+ start); 
       
                     Log.v(TAG, "end:"+ end); 
       
                     //释放position<start之外的bitmap资源 
       
                     Bitmap delBitmap; 
       
                     for(int del=0;del<start;del++){ 
       
                             delBitmap = dateCache.get(del); 
       
                             if(delBitmap != null){ 
       
                                      //如果非空则表示有缓存的
bitmap,需要清理 
       
                                     Log.v(TAG, "release position:"+ 
del); 
       
                                     //从缓存中移除该del->bitmap的
映射 
       
                                     dateCache.remove(del); 
       
                                      delBitmap.recycle(); 
       
                             }
 

 

⑷ 释放对象的引用 

 

 

描述: 

    这种情况描述起来比较麻烦,举两个例子进行说明。 

示例A: 

假设有如下操作

 

 

public class DemoActivity extends Activity { 
    ... ... 
    private Handler mHandler = ... 
    private Object obj; 
    public void operation() { 
    obj = initObj(); 
    ... 
    [Mark] 
    mHandler.post(new Runnable() { 
           public void run() { 
            useObj(obj); 
           } 
    }); 
    } 
}

 

 

我们有一个成员变量 obj,在operation()中我们希望能够将处理obj实例的操作post

到某个线程的MessageQueue 中。在以上的代码中,即便是 mHandler 所在的线程使用

完了obj所引用的对象,但这个对象仍然不会被垃圾回收掉,因为 DemoActivity.obj还保

有这个对象的引用。所以如果在DemoActivity中不再使用这个对象了,可以在[Mark]的

位置释放对象的引用,而代码可以修改为:

 

 

... ... 
public void operation() { 
    obj = initObj(); 
    ... 
    final Object o = obj; 
    obj = null; 
    mHandler.post(new Runnable() { 
        public void run() { 
            useObj(o); 
        } 
    } 
} 
... ... 

 示例B: 

    假设我们希望在锁屏界面(LockScreen)中,监听系统中的电话服务以获取一些信息(如

信号强度等),则可以在LockScreen 中定义一个PhoneStateListener 的对象,同时将它

注册到TelephonyManager 服务中。对于LockScreen 对象,当需要显示锁屏界面的时候

就会创建一个LockScreen 对象,而当锁屏界面消失的时候LockScreen 对象就会被释放

掉。 

    但是如果在释放LockScreen 对象的时候忘记取消我们之前注册的

PhoneStateListener 对象,则会导致LockScreen无法被垃圾回收。如果不断的使锁屏界

面显示和消失,则最终会由于大量的LockScreen 对象没有办法被回收而引起

OutOfMemory,使得system_process进程挂掉。 

    总之当一个生命周期较短的对象A,被一个生命周期较长的对象B保有其引用的情况

下,在A的生命周期结束时,要在 B中清除掉对A的引用。

 

⑸其他 

 

 

Android 应用程序中最典型的需要注意释放资源的情况是在Activity的生命周期中,在

nPause()、onStop()、onDestroy()方法中需要适当的释放资源的情况。由于此情况很基

础,在此不详细说明,具体可以查看官方文档对Activity生命周期的介绍,以明确何时应

该释放哪些资源。 

分享到:
评论

相关推荐

    Android内存管理小结

    #### 五、常见内存使用不当情况及解决方案 - **查询数据库没有关闭游标**:当使用SQLiteDatabase进行查询时,必须确保在完成后关闭Cursor对象,以释放相关的资源。 - **构造Adapter时未使用convertView**:在...

    Android内存管理

    **1.7 Android内存分配小结** Android的内存分配机制相对复杂,涉及到多种内存管理策略和技术。从硬件层面上看,Android使用了基于Linux的内存管理系统;而在应用层面上,则采用了Java的垃圾回收机制。为了充分利用...

    关于static的小结(Java内存溢出)

    本文通过介绍`static`关键字的基本概念及其在内存管理中的作用,揭示了不当使用`static`可能导致内存溢出的问题。为了有效地避免这些问题,开发人员需要注意以下几点: - 合理规划静态变量的使用,尽量减少其数量和...

    windows内存编程讲解

    5. 小结 理解内存管理是编写高效、安全的C++程序的关键。熟练掌握不同内存分配方式,识别并处理内存错误,以及正确使用指针和数组,能帮助开发者避免许多潜在问题。在Windows环境中,内存管理不仅涉及程序内部,还...

    虚拟内存太低怎么办,虚拟内存怎么设置最好.docx

    小结: 虚拟内存是计算机系统中的一种重要概念,它可以避免系统崩溃的情况。但是,虚拟内存需要合理配置,否则可能会导致虚拟内存太低、虚拟内存不足的情况。了解虚拟内存的优点和缺点,合理配置虚拟内存,可以提高...

    vue单页应用的内存泄露定位和修复问题小结

    此外,还有其他可能引发内存泄漏的情况,如未取消的定时器、订阅、或者第三方库的不当使用等。对于Vue开发者,以下是一些防止内存泄漏的最佳实践: - 避免在生命周期钩子中使用全局变量,尤其是与DOM相关的。 - 在...

    系统中或自检时识别的内存容量和标称不符的情况.docx

    #### 小结 当遇到系统中或自检时识别的内存容量与标称不符的情况时,首先要判断是否是由于操作系统类型限制(如32位系统)导致的正常现象;其次检查内存条本身是否存在问题,例如兼容性或物理损坏等;最后,通过...

    第2章 进程管理总结.pdf

    * 使用不当可能导致死锁 * 实现复杂 五、实践例题 某车站售票厅,任何时刻最多可容纳2020名购票者。若把一个购票者看作一个进程,请回答以下问题: 1. 用P、V操作管理这些并发进程时,应怎样定义信号量? 2. 如何...

    Android异步加载图像小结 (含线程池,缓存方法).zip

    在实际项目中,"Android异步加载图像小结 (含线程池,缓存方法).doc"文档可能详细介绍了这些技术的实现细节和案例,而"项目说明.zip"可能包含了具体实现的代码示例,供开发者参考学习。 总的来说,高效地在...

    MongoDB北京2014 - MongoDB性能扩展 - 唐建法

    **小结**:根据数据访问的方式合理设计数据模型,必要时可以采用数据冗余来提高读取性能。 #### 索引优化 **背景**:索引是提高查询性能的重要手段之一。合适的索引设计能够显著减少查询时间。 **案例**:电话簿...

    深入解析DB2--高级管理,内部体系结构与诊断案例.part1.rar

    5.7 本章小结 第6章 数据库配置参数调整 6.1 数据库配置参数 6.2 监控和调优实例(DBM)配置参数 6.3 监控和调优DB配置参数 6.4 调整DB2概要注册变量 6.5 内存自动调优 6.6 总结 第7章 锁和并发 7.1 锁等待及调整案例 ...

    DebuggingDelphiPrograms

    #### 小结 调试是软件开发过程中不可或缺的一部分。Delphi 提供了强大的集成调试器和其他辅助工具,帮助开发者高效地定位和解决程序中的问题。通过合理运用这些工具和技术,可以显著提高软件质量和开发效率。无论是...

    Redis实践与总结

    ### Redis实践与总结 ...然而,在实际部署和使用过程中,需要注意合理配置和管理,避免因不当使用而导致性能瓶颈或安全问题。通过对上述案例的学习,我们可以更好地理解如何在一线开发中有效地运用Redis。

    JS闭包、作用域链、垃圾回收、内存泄露相关知识小结

    如果使用不当,闭包很容易造成内存泄露,因为闭包中的外部变量会持续存在于内存中,直到没有任何闭包引用它们。 在创建闭包时,常见的方法是在一个函数内创建另一个函数,并通过这个内部函数访问外部函数的变量。...

    C语言常见易错点小结

    ### C语言常见易错点小结 C语言作为一种强大的编程语言,在软件开发领域有着广泛的应用。然而,由于其编译器对于语法的检查不如其他一些高级语言那样严格,因此在编写过程中很容易出现一些不易察觉的错误。对于初学...

    Python中的一些陷阱与技巧小结

    但是,如果使用不当,可能会导致以下问题: - **性能问题**:虽然生成器可以节省大量内存,但如果生成器过于复杂或包含大量计算密集型操作,可能会影响程序的整体性能。 - **状态管理错误**:如果生成器中包含...

Global site tag (gtag.js) - Google Analytics