Android 应用程序的内存问题-尝试了一切,仍然在失去

我花了整整4天的时间,想尽办法找出我正在开发的一个应用程序中的内存泄漏问题,但很久以前就没什么用了。

我正在开发的应用程序是社会性质的,所以想想配置文件活动(P)和列出活动与数据-例如徽章(B)。您可以从配置文件跳转到徽章列表到其他配置文件,其他列表等。

所以想象一个像这样的流程: P1-> B1-> P2-> B2-> P3-> B3,等等。为了保持一致性,我正在加载同一个用户的配置文件和徽章,所以每个 P 页面是相同的,每个 B 页面也是相同的。

这个问题的要点是: 在浏览了一段时间之后,根据每个页面的大小,我会在随机的地方得到一个内存不足的异常——位图、字符串等等——它似乎不是一致的。

在做了所有可以想象到的事情来找出内存不足的原因之后,我还是一无所获。我不明白的是,为什么 Android 不杀死 P1、 B1等等,如果它在加载时耗尽内存,而是崩溃。如果我曾经通过 onCreate ()和 onRestoreInstanceState ()返回到这些早期活动,我希望这些活动死亡并且复活。

更不用说这个了——即使我做 P1-> B1-> 后退-> B1-> 后退-> B1,我还是会崩溃。这表明存在某种内存泄漏,但即使在转储 hprof 并使用 MAT 和 JProfiler 之后,我也无法确定它的位置。

我已经禁用了从网络加载图像(并增加了加载的测试数据,以弥补它,使测试公平) ,并确保图像缓存使用 SoftReferences。Android 实际上试图释放它所拥有的为数不多的 SoftReferences,但就在它内存崩溃之前。

标签页面从网页获取数据,从 BaseAdapter 将数据加载到 EntityData 数组中,然后将数据提供给 ListView (我实际上使用的是 CommonsWare 的 优秀的合并适配器,但是在这个标签活动中,实际上只有一个适配器,但是无论如何我想提到这个事实)。

我已经看过代码了,找不到任何可能泄露的信息。我清空了所有能找到的内容,甚至 System.gc ()也左右删除了,但应用程序还是崩溃了。

我仍然不明白为什么堆栈中的非活动活动没有被收获,我真的很想弄清楚这一点。

此时此刻,我正在寻找任何线索,建议,解决方案... 任何有用的东西。

谢谢你。

43381 次浏览

so the only thing i can really think of is if you have a static variable that references directly or indirectly to the context. Even something so much as a reference to part of the application. I'm sure you have already tried it but i will suggest it just in case, try just nulling out ALL of your static variables in the onDestroy() just to make sure the garbage collector gets it

Are you holding some references to each Activity? AFAIK this is a reason which keeps Android from deleting activities from the stack.

We're you able to reproduce this error on other devices as well? I've experienced some strange behaviour of some android devices depending on the ROM and/or hardware manufacturer.

The biggest source of memory leak I have found was caused by some global, high level or long-standing reference to the context. If you are keeping "context" stored in a variable anywhere, you may encounter unpredictable memory leaks.

I think the problem maybe a combination of many factors stated here in the answers are what is giving you problems. Like @Tim said, a (static) reference to an activity or an element in that activity can cause the GC to skip the Activity. Here is the article discussing this facet. I would think the likely issue comes from something keeping the Activity in an "Visible Process" state or higher, which will pretty much guaranty that the Activity and its associated resources never get reclaimed.

I went through the opposite problem a while back with a Service, so that's what got me going on this thought: there is something keeping your Activity high on the process priority list so that it won't be subject to the system GC, such as a reference (@Tim) or a loop (@Alvaro). The loop doesn't need to be an endless or long running item, just something that runs a lot like a recursive method or cascaded loop (or something along those lines).

EDIT: As I understand this, onPause and onStop are called as needed automatically by Android. The methods are there mainly for you to overide so that you can take care of what you need to before the hosting process is stopped (saving variables, manually saving state, etc.); but note that it is clearly stated that onStop (along with onDestroy) may not be called in every case. Additionally, if the hosting process is also hosting an Activity, Service, etc. that has a "Forground" or "Visible" status, the OS might not even look at stopping the process/thread. For example: an Activity and a Service are both luanched in the same process and the Service returns START_STICKY from onStartCommand() the process automatically takes at least a visible status. That might be the key here, try declaring a new proc for the Activity and see if that changes anything. Try adding this line to the declaration of your Activity in the Manifest as: android:process=":proc2" and then run the tests again if your Activity shares a process with anything else. The thought here is that if you've cleaned up your Activity and are pretty sure that the problem is not your Activity then something else is the problem and its time to hunter for that.

Also, I can't remember where I saw it (if I even saw it in the Android docs) but I remember something about a PendingIntentreferencing an Activity may cause an Activity to behave this way.

Here is a link for the onStartCommand() page with some insights on the process non-killing front.

Bitmaps are often the culprit for memory errors on Android, so that would be a good area to double check.

I still don't understand why inactive activities that are on the stack don't get reaped, and I'd really love to figure that out.

This is not how things work. The only memory management that impacts activity lifecycle is the global memory across all processes, as Android decides that it is running low on memory and so need to kill background processes to get some back.

If your application is sitting in the foreground starting more and more activities, it is never going into the background, so it will always hit its local process memory limit before the system ever comes close to killing its process. (And when it does kill its process, it will kill the process hosting all the activities, including whatever is currently in the foreground.)

So it sounds to me like your basic problem is: you are letting too many activities run at the same time, and/or each of those activities is holding on to too many resources.

You just need to redesign your navigation to not rely on stacking up an arbitrary number of potentially heavy-weight activities. Unless you do a serious amount of stuff in onStop() (such as calling setContentView() to clear out the activity's view hierarchy and clear variables of whatever else it may be holding on to), you are just going to run out of memory.

You may want to consider using the new Fragment APIs to replace this arbitrary stack of activities with a single activity that more tightly manages its memory. For example if you use the back stack facilities of fragments, when a fragment goes on the back stack and is no longer visible, its onDestroyView() method is called to completely remove its view hierarchy, greatly reducing its footprint.

Now, as far as you crashing in the flow where you press back, go to an activity, press back, go to another activity, etc and never have a deep stack, then yes you just have a leak. This blog post describes how to debug leaks: http://android-developers.blogspot.com/2011/03/memory-analysis-for-android.html

Try passing getApplicationContext() to anything that needs a Context. You might have a global variable that is holding a reference to your Activities and preventing them from being garbage collected.

One of the things that really helped the memory issue in my case ended up being setting inPurgeable to true for my Bitmaps. See Why would I ever NOT use BitmapFactory's inPurgeable option? and the answer's discussion for more info.

Dianne Hackborn's answer and our subsequent discussion (also thanks, CommonsWare) helped clarify certain things I was confused about, so thank you for that.

Some tips:

  1. Make sure you are not leak activity context.

  2. Make sure you are don't keep references on bitmaps. Clean all of your ImageView's in Activity#onStop, something like this:

    Drawable d = imageView.getDrawable();
    if (d != null) d.setCallback(null);
    imageView.setImageDrawable(null);
    imageView.setBackgroundDrawable(null);
    
  3. Recycle bitmaps if you don't need them anymore.

  4. If you use memory cache, like memory-lru, make sure it is not using to much memory.

  5. Not only images take alot of memory, make sure you don't keep too much other data in memory. This easily can happens if you have infinite lists in your app. Try to cache data in DataBase.

  6. On android 4.2, there is a bug(stackoverflow#13754876) with hardware acceleration, so if you use hardwareAccelerated=true in your manifest it will leak memory. GLES20DisplayList - keep holding references, even if you did step (2) and no one else is referencing to this bitmap. Here you need:

    a) disable hardware acceleration for api 16/17;
    or
    b) detach view that holding bitmap

  7. For Android 3+ you can try to use android:largeHeap="true" in your AndroidManifest. But it will not solve your memory problems, just postpone them.

  8. If you need, like, infinite navigation, then Fragments - should be your choice. So you will have 1 activity, which will just switch between fragments. This way you will also solve some memory issues, like number 4.

  9. Use Memory Analyzer to find out the cause of your memory leak.
    Here is very good video from Google I/O 2011: Memory management for Android Apps
    If you dealing with bitmaps this should be a must read: Displaying Bitmaps Efficiently

I encountered the same problem with you. I was working on a instant messaging app, for the same contact, it is possible to start a ProfileActivity in a ChatActivity, and vice versa. I just add a string extra into the intent to start another activity, it takes the information of class type of starter activity, and the user id. For example, ProfileActivity starts a ChatActivity, then in ChatActivity.onCreate, I mark the invoker class type 'ProfileActivity' and user id, if it's going to start an Activity, I would check whether it is a 'ProfileActivity' for the user or not. If so, just call 'finish()' and go back to the former ProfileActivity instead of creating a new one. Memory leak is another thing.