Android Memory Leaks OR Different Ways to Leak

本文详细介绍了作者在解决AnySoftKeyboard应用中出现的内存泄漏问题的过程,包括六种常见内存泄漏的原因及解决方案。

Android Memory Leaks OR Different Ways to Leak

转自:http://evendanan.net/2013/02/Android-Memory-Leaks-OR-Different-Ways-to-Leak/


 few weeks ago I released version 85 of AnySoftKeyboard and soon after, crash reports started to hit my email box. It usually happens - mostly because I forget to test a feature, or just because the specific device decided it is time to crash (it happens, really! The floor is crooked). But this time, all the crashes were of the same nature, and were many, about 5-10 a day.
The crashes were due to OutOfMemoryError (a.k.a, OOM), which usually means a memory leak in Android (why? Unlike a desktop, an Android device has very little heap to play with and leaking will fill that little space quickly), I started to look for them.

TL;DR: If I was able to recreate this crash on my devices, I would have find the cause immediately, and just fix the 6th cause in this list. But, I wasn't able to reproduce it (why? later on that), and it took me some time to understand that the reporting users want me to fix it, and are willing to help!

Cause 1: Context Leak

The most common leak in Android is the  Context  leak (here is a great  explanation ). Although all the examples are talking about Activity leaking, a Service (which AnySoftKeyboard is) has the same problems as Activity in that regard.

Solution

It's a no-brainer, but tedious: where ever I used a Context object, I made sure that instance is the Application's Context - getApplicationContext() .

Cause 2: The Handler Leak OR the Inner Class

The  inner-class  (a.k.a.  nested classes ) in Java has an implicit property where it keeps a reference to its outer class, this is why you can call outer-class's functions in the inner-class scope. As long as the Handler is alive, so is the outer-class, and in my case - the whole Input Method Service, which in turn holds the entire tree!
One thing to note, once there are no messages in the Handler's loop, it will go out of reference in the Main Looper, and if there are no other references to it, well, it will be collected, with its implicit outer-class.  So what is the problem?  you may ask, and you are right. The problem is that some of the messages are delayed (or just still in queue), and as long as there is a message in the queue, the Handler will not be collected, so even if the OS will decide to kill AnySoftKeyboard (say, orientation change) and create a new one, the old  one is still in memory , and we have two full trees at the same time. Maybe not for long, but enough time to cause OOM on some devices.

Solution

Very simple: gone over all the inner-classes I had (including the Handlers) and made sure they are static classes, and if they required a reference to the the outer-class, I used  WeakReference :
  public class AnySoftKeyboard extends InputMethodService {
   
  private static final class KeyboardUIStateHanlder extends Handler {
  //This will NOT be counted as a reference!
  private final WeakReference<AnySoftKeyboard> mKeyboard;
   
  public KeyboardUIStateHanlder(AnySoftKeyboard keyboard) {
  mKeyboard = new WeakReference<AnySoftKeyboard>(keyboard);
  }
   
  @Override
  public void handleMessage(Message msg) {
  AnySoftKeyboard ask = mKeyboard.get();
  if (ask == null)//delayed posts and such may result in the reference gone
  return;
   
  switch (msg.what) {
  case MSG_UPDATE_SUGGESTIONS:
  ask.performUpdateSuggestions();
  break;
view raw InnerClassSnippet.java hosted with ❤ by  GitHub

In the code snippet above, I used a WeakReference field to keep a pointer to the main AnySoftKeyboard instance. By using this pattern I was able to call methods of AnySoftKeyboard, but also made sure I was not keeping AnySoftKeyboard from being garbage-collected if needed.
Do remember that if using this pattern it is required to check that the weak reference is still pointing to something (e.g., not null).

Cause 3: The Drawable Callback

Most of the Drawables I use are not attached to Views, but some are, and since I keep reference to the drawables objects, this was still a possible leak. Why? The drawable requires that its client (usually a View) will implement theDrawable.Callback interface, which is used by the Drawable to perform animation related tasks. So, if the View is removed from the window/activity, but Drawable is still referenced, then the View is still referenced too, and will not be garbage-collected.

Solution

I explicitly unbind the drawables when the keyboard's View is no longer needed:
  private static void unbindDrawable(Drawable d) {
  if (d != null)
  d.setCallback(null);
  }
view raw AnyKeyboardBaseView.java hosted with ❤ by  GitHub
This was, actually, not required in my case since the only reference to the Drawable was the View which is the Callback for the Drawable.
Commit  (look for changes in  AnyKeyboardBaseView.java ).

Cause 4: The External Context

This is special for AnySoftKeyboard, most apps do not reference to an external package's Context, but since AnySoftKeyboard supports external packages (i.e., language packs, themes, etc.), I was keeping a reference to that external Context always: let's say you use three languages (not very rare or the users of AnySoftKeyboard), and want to use the  Ice Cream Sandwich theme , you'll end up with  four  external Context objects sitting around in AnySoftKeyboard heap. This is quite a waste, since most of the time I use the external Context only once or twice!

Solution

I moved the external from an explicit reference, to a WeakReference, and added the package's name to class's fields so I'll be able to create the context if needed.
Commit .

Cause 5: The Too Large Objects

Key background, Shift, Control, Enter, Space, Tab, Cancel, Globe, Microphone, Settings, Arrows  and more. All these are Drawables which were loaded into the keyboard View, and were kept in memory. About 15 of them, and some with various states (like the Enter key, which has a Normal, Search, Done, Go, etc states, each a full drawable). That's not a leak, but it is still a lot of memory for some devices - HTC Desire will not allow more than 32MB of heap, for example.

Solution

I created a  DrawableBuilder  class which have all the information required to get the drawable. This way, if the layout does not use a specific icon, it will not be loaded into memory.
Commit .

Cause 6: The Database Connection

After I fixed all the issues above, the app was still crashing on some devices. I had no idea where to look anymore, I was sure there was a leak, and it very elusive, maybe even vendor specific (although the crashes came from many vendors, and various OS versions), so I added to the  UncaughtExceptionHandler  a code that check of the thrown Exception type, and if it is OutOfMemoryError, I asked the framework do create a  memory dump :
  File extFolder = Environment.getExternalStorageDirectory();
  File target = new File(extFolder, "ask_mem_dump.hprof");
  Debug.dumpHprofData(target.getAbsolutePath());
view raw AndroidDumpHprof.java hosted with ❤ by  GitHub
I found the leak easily using  Eclipse Memory Analyzer (a.k.a, MAT) : the leak was a Database Connection Transport, and a huge one, each such transport used 0.5MB (for that user - since he had a large user-dictionary), and it leaked every time the user switched language! It was leaking due to a race-condition.

The Race Condition

When a dictionary is created, it loads its words list using an  AsyncTask  (so it wont hold up the UI thread), but when the list is long and the device is slow, the dictionary's  close  method (which is called when the language changes) may be called before the loading is done, hence not closing anything!

So, if it happens always why haven't I recreated it on my devices? Some flavors (Samsung devices and CyanogenMod) could handle that release automatically (there is a finalizer in Java, you know), and they did. Some vendors did not. I had a Samsung, a Motorola and a device running CyanogenMod. Moreover, my devices are fast just enough, and used a small words list. Bummer, ah? Ya..

Solution

Just closed the database connection: by ensuring the related functions use monitors.
Commit  and  commit .

The End


内容概要:本文围绕“基于分布式模型预测控制的多个固定翼无人机一致性控制”展开,利用Matlab代码实现相关算法的仿真,旨在通过分布式控制策略实现多架固定翼无人机在复杂动态环境中的协同飞行与一致性控制。研究结合模型预测控制(MPC)方法,构建适用于多无人机系统的分布式优化框架,重点解决了通信受限、信息延迟及无中心化指挥条件下的协同稳定性问题。内容涵盖固定翼无人机的动力学建模、分布式MPC优化求解机制、一致性协议设计、通信拓扑结构分析以及仿真验证全过程,确保多机系统在保持队形一致的同时完成协同任务。; 适合人群:具备自动控制理论、无人机系统建模或多智能体协同控制基础,从事智能无人系统、集群控制、自动化与机器人等领域研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①应用于多无人机协同编队飞行、集群侦察、分布式任务执行等实际工程场景;②为分布式MPC算法在多智能体系统中的一致性控制提供可复现的Matlab仿真案例,推动先进控制理论向工程实践转化;③服务于科研论文复现、算法验证、控制系统课程设计与毕业课题参考。; 阅读建议:建议读者结合文中提供的Matlab代码逐模块运行与调试,重点关注分布式MPC在不同通信拓扑下对一致性收敛性能的影响,并可通过调整预测时域、权重矩阵与噪声参数等方式深化对算法鲁棒性与适应性的理解。
内容概要:本文提出了一种基于变分模态分解(VMD)、麻雀搜索算法(SSA)优化与长短期记忆网络(LSTM)相结合的光伏功率预测模型(VMD-SSA-LSTM),旨在提升光伏发电预测的精度与鲁棒性。该方法首先利用VMD对原始非平稳光伏功率序列进行自适应分解,获得一系列具有更稳定特征的本征模态分量(IMFs),有效降低数据复杂性与噪声干扰;随后引入麻雀搜索算法(SSA)对LSTM网络的关键超参数(如学习率、隐层节点数等)进行全局寻优,克服传统试凑法效率低、易陷入局部最优的问题,显著提升模型收敛速度与泛化能力;最后,构建多个LSTM子模型分别预测各模态分量,并将结果重构得到最终的光伏功率预测值。该混合模型充分融合了VMD在信号预处理中的优异分解性能、SSA在参数优化中的高效搜索能力以及LSTM在捕捉时间序列长期依赖关系上的强大建模优势,实现了对复杂气象因素影响下光伏出力波动的高精度拟合与预测。; 适合人群:具备一定电力系统、新能源发电或时间序列预测基础知识,熟悉MATLAB编程环境,从事光伏功率预测、智能电网调度、可再生能源集成、负荷预测等领域研究的科研人员、工程技术人员及高校研究生。; 使用场景及目标:①应用于光伏电站的短期与超短期功率预测,为电网安全调度、电力市场交易、储能系统配置及需求侧响应提供精准数据支撑;②解决传统单一预测模型(如ARIMA、BPNN、单一LSTM)在处理非平稳、强波动性光伏数据时存在的精度不足、稳定性差等问题;③为风电、负荷等其他非平稳时序预测问题提供一种有效的“分解-优化-预测”混合建模范式与技术实现路径。; 阅读建议:建议读者结合文中提供的完整MATLAB代码,深入理解VMD信号分解、SSA优化算法流程及LSTM网络构建的每一个技术环节,通过实际历史数据进行模型复现与对比实验(如与VMD-LSTM、SSA-LSTM等模型比较),掌握参数调优技巧与模型性能评估方法,从而真正掌握该先进混合预测模型的核心思想与应用精髓。
代码转载自:https://pan.quark.cn/s/a4b39357ea24 在当代信息技术行业,深度学习技术的应用已经全面渗透到众多实际情境中,其中生成对抗网络(GAN)以及深度卷积生成对抗网络(DCGAN)被视为促进人工智能领域发展的核心技术之一。接下来将具体阐述如何借助Pytorch框架与MNIST数据集来完成基础GAN和DCGAN的开发,并解析过程中涉及的关键概念。 ### Pytorch框架与MNIST数据集概述 **Pytorch** 被视为一种广受欢迎的开源机器学习平台,其具备动态计算图与高度适应性等特性,因此在学术研究领域备受推崇,能够支持多种类型的深度学习架构。 **MNIST数据集** 是一个包含手写数字的图像集合,常用于训练各类图像识别系统,涵盖从0至9的数字图像,每张图像的尺寸为28x28像素。由于其入门级难度,MNIST数据集特别适合用于教学演示和实验操作。 ### 基础生成对抗网络(GAN)核心概念 **基础GAN** 是一种由生成器(Generator)和判别器(Discriminator)构成的无监督学习框架。生成器的核心任务是创造足以乱真的图像数据,而判别器的关键职责在于精确辨别真实图像与生成图像之间的差异。 1. **生成器**:该模块以随机噪声为输入,通过深度神经网络进行转换,最终输出接近真实图像的数据。其根本目的在于迷惑判别器。 2. **判别器**:对输入数据(无论是真实图像还是生成图像)进行评估,并输出其属于真实样本的概率。判别器的训练重点在于提升识别的精确度。 3. **训练机制**:GAN的训练过程涉及两个网络之间的交替训练,即在锁定一个网络参数的情况下对另一个网络进行训练。首先优化判别器以增强其区分能力...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值