因为我们的应用程序能够使用的内存有限,所以在编写代码的时候需要特别注意内存
使用问题。如下是一些常见的内存使用不当的情况。
⑴查询数据库没有关闭游标
描述:
程序中经常会进行查询数据库的操作,但是经常会有使用完毕 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生命周期的介绍,以明确何时应
该释放哪些资源。
分享到:
相关推荐
#### 五、常见内存使用不当情况及解决方案 - **查询数据库没有关闭游标**:当使用SQLiteDatabase进行查询时,必须确保在完成后关闭Cursor对象,以释放相关的资源。 - **构造Adapter时未使用convertView**:在...
**1.7 Android内存分配小结** Android的内存分配机制相对复杂,涉及到多种内存管理策略和技术。从硬件层面上看,Android使用了基于Linux的内存管理系统;而在应用层面上,则采用了Java的垃圾回收机制。为了充分利用...
本文通过介绍`static`关键字的基本概念及其在内存管理中的作用,揭示了不当使用`static`可能导致内存溢出的问题。为了有效地避免这些问题,开发人员需要注意以下几点: - 合理规划静态变量的使用,尽量减少其数量和...
5. 小结 理解内存管理是编写高效、安全的C++程序的关键。熟练掌握不同内存分配方式,识别并处理内存错误,以及正确使用指针和数组,能帮助开发者避免许多潜在问题。在Windows环境中,内存管理不仅涉及程序内部,还...
小结: 虚拟内存是计算机系统中的一种重要概念,它可以避免系统崩溃的情况。但是,虚拟内存需要合理配置,否则可能会导致虚拟内存太低、虚拟内存不足的情况。了解虚拟内存的优点和缺点,合理配置虚拟内存,可以提高...
此外,还有其他可能引发内存泄漏的情况,如未取消的定时器、订阅、或者第三方库的不当使用等。对于Vue开发者,以下是一些防止内存泄漏的最佳实践: - 避免在生命周期钩子中使用全局变量,尤其是与DOM相关的。 - 在...
#### 小结 当遇到系统中或自检时识别的内存容量与标称不符的情况时,首先要判断是否是由于操作系统类型限制(如32位系统)导致的正常现象;其次检查内存条本身是否存在问题,例如兼容性或物理损坏等;最后,通过...
* 使用不当可能导致死锁 * 实现复杂 五、实践例题 某车站售票厅,任何时刻最多可容纳2020名购票者。若把一个购票者看作一个进程,请回答以下问题: 1. 用P、V操作管理这些并发进程时,应怎样定义信号量? 2. 如何...
在实际项目中,"Android异步加载图像小结 (含线程池,缓存方法).doc"文档可能详细介绍了这些技术的实现细节和案例,而"项目说明.zip"可能包含了具体实现的代码示例,供开发者参考学习。 总的来说,高效地在...
**小结**:根据数据访问的方式合理设计数据模型,必要时可以采用数据冗余来提高读取性能。 #### 索引优化 **背景**:索引是提高查询性能的重要手段之一。合适的索引设计能够显著减少查询时间。 **案例**:电话簿...
5.7 本章小结 第6章 数据库配置参数调整 6.1 数据库配置参数 6.2 监控和调优实例(DBM)配置参数 6.3 监控和调优DB配置参数 6.4 调整DB2概要注册变量 6.5 内存自动调优 6.6 总结 第7章 锁和并发 7.1 锁等待及调整案例 ...
#### 小结 调试是软件开发过程中不可或缺的一部分。Delphi 提供了强大的集成调试器和其他辅助工具,帮助开发者高效地定位和解决程序中的问题。通过合理运用这些工具和技术,可以显著提高软件质量和开发效率。无论是...
### Redis实践与总结 ...然而,在实际部署和使用过程中,需要注意合理配置和管理,避免因不当使用而导致性能瓶颈或安全问题。通过对上述案例的学习,我们可以更好地理解如何在一线开发中有效地运用Redis。
如果使用不当,闭包很容易造成内存泄露,因为闭包中的外部变量会持续存在于内存中,直到没有任何闭包引用它们。 在创建闭包时,常见的方法是在一个函数内创建另一个函数,并通过这个内部函数访问外部函数的变量。...
### C语言常见易错点小结 C语言作为一种强大的编程语言,在软件开发领域有着广泛的应用。然而,由于其编译器对于语法的检查不如其他一些高级语言那样严格,因此在编写过程中很容易出现一些不易察觉的错误。对于初学...
但是,如果使用不当,可能会导致以下问题: - **性能问题**:虽然生成器可以节省大量内存,但如果生成器过于复杂或包含大量计算密集型操作,可能会影响程序的整体性能。 - **状态管理错误**:如果生成器中包含...