Android性能优化——之防止内存泄露

本文探讨了Android应用中常见的OOM问题及内存泄漏原因,包括静态变量、未注销的广播接收器、单例模式不当使用、属性动画管理不善及Handler内部类导致的内存泄漏。

又是好久没有写博客了,一直都比较忙,最近终于有时间沉淀和整理一下最近学到和解决的一些问题。

最近进行技术支持的时候,遇到了几个崩溃的问题,都是OOM异常,一般OOM异常给人的感觉应该是加载大图片造成的,但是经过看界面布局,并且分析加载图片的大小发现加载图片方面已经没有什么可以优化的了,但是依然崩溃,没办法了,又用的IDEA工具中的内存监视器,来判断到底是哪里造成内存激增,做哪些操作造成页面资源没有及时释放。最后发现原来是每次关闭这个界面,都没有及时的释放资源,每次开启,都会重新申请资源,造成内存泄露问题。下面我就说一下我们写代码的时候,那些地方容易会造成内存泄露。

首先是在Activity中声明静态变量或者静态方法或者静态控件

静态变量或者方法是放在静态数据区的,也就是在程序运行的过程中,只要加载过这个类之后,静态的变量或者方法,就一直会在静态数据区存在,不会释放资源。因为静态变量或者方法不是凭空存在的,所以需要Activity作为支撑,也就是说这个变量不仅占用了变量本身所存放数据的内存,还占据了这个Activity所占的内存。也就是这个Activity即使被用户关闭了,但是资源并没有被释放。

其次,一些读取数据库动态注册广播没有及时关闭注销也是容易造成内存泄露的

例如例如BroadcastReceiverContentObserverFileObserverActivityonDeatory或者某类的生命周期结束后,一定要unregister掉,否则这个Activity会一直被system强引用,不会被内存回收。

单例模式导致的内存泄露,

单例模式的特点就是他的生命周期和Application一样,如果某个Activity示例被一个单利所持有,也就是说单利里面引用了他,就会造成Activity对象会无法正常回收释放。

注:这里的原理和第一种是一样的,都是因为静态变量引用Activity对象,造成Activity无法正常释放资源造成的。

 

属性动画导致内存泄露

例如代码中设置了属性动画(ObjectAnimator),在Activity中启动了动画,但是在销毁的时候,没有调用cancle方法,虽然我们看不到动画了,但是这个动画依然会不断地播放下去,动画引用所在的控件,所在的控件引用Activity,这就造成Activity无法正常释放。

 

Handler内部类造成内存泄露

当使用内部类(包括匿名内部类)来创建Handler的时候,Handler对象会隐式的持有一个外部类对象(通常是Activity)的引用,而handler通常会伴随这一个耗时的后台线程(例如从网络拉取图片)一起出现,这个后台线程在任务执行完毕之后,通过消息机制,通知Handler然后Handler见图片更新到界面,如果用户在网络请求中关闭了Activity,正常情况下,Activity不再被使用,因该会被Gc回收掉,但是,由于该线程未执行完,而该线程持有的Handler的引用,就导致Activity无法被回收(直到网络请求结束),如果你执行了HandlerpostDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达之前,会有一条MessageQueue -> Message -> Handler -> Activity的链,导致你的Activity被持有引用而无法被回收。可以在Activity结束后,关闭线程,如果你的Handler是被delayMessage持有了引用,那么调用void removeCallbacksAndMessagesnull)方法来移除消息队列。

 

### Android 应用性能优化技巧 #### 一、解决内存泄漏方案 内存泄漏会使得应用程序占用过多的内存资源,进而影响到整个系统的运行效率甚至导致程序崩溃。对于未正确释放的静态引用情况,在Activity或Fragment生命周期结束时应确保解除所有不必要的关联[^2]。 针对未正确释放的后台任务问题,当组件不再需要执行异步操作的时候要及时取消这些任务并清理相关联的对象引用。如果存在长时间运行的任务,则应该考虑将其移至独立的服务进程中处理。 对于监听器和回调函数引起的泄露风险,在注册任何广播接收者或其他形式的通知机制之后务必记得注销它们;另外采用弱引用来保存对外部对象的引用也是一种有效的预防措施。 利用专门设计用于发现Java及Kotlin代码中的潜在内存泄漏问题的应用——LeakCanary可以帮助开发者快速定位具体位置以便及时修正错误。同时也可以借助于Android Studio内置的强大分析工具Profiler来监控应用实时内存消耗状况从而更好地理解哪里出现了异常增长趋势[^1]。 ```java // 注册广播接收器的例子 registerReceiver(myBroadcastReceiver, intentFilter); // 不要忘记在适当的地方反注册它 unregisterReceiver(myBroadcastReceiver); ``` #### 二、使用合适的数据结构 选择恰当的数据结构可以显著提高算法的时间复杂度与空间利用率。例如,在频繁插入删除元素的情况下LinkedList可能优于ArrayList; 而HashMap则适合做键值对映射存储容器因为它的查找速度非常快。合理选用数据结构有助于减少不必要的计算开销以及降低因不当实现而导致额外分配大量临时变量所带来的堆外溢隐患。 ### Native层内存管理注意事项 需要注意的是native层内存申请并不受到Dalvik虚拟机所设定的最大heap size约束而是由native process本身决定最大可用容量。因此即使是在Java层面看起来还有足够剩余空间可供支配也有可能因为在C/C++部分过度请求而触发OOM(out-of-memory)异常。所以在开发JNI接口调用或者直接编写原生代码期间应当格外小心谨慎对待每一次new出来的指针对象,并且遵循RAII(Resource Acquisition Is Initialization)原则即尽可能早地获取所需资源并在作用域结束前立即释放掉它们以防止意外发生[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值