code

Android 앱 메모리 부족 문제-모든 것을 시도했지만 여전히 손실

codestyles 2020. 9. 12. 10:04
반응형

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는 실제로 가지고있는 몇 가지 SoftReference를 해제하려고하지만 메모리가 부족해지기 직전입니다.

배지 페이지는 웹에서 데이터를 가져 와서 BaseAdapter에서 EntityData 배열로로드하고이를 ListView에 공급합니다 (실제로 CommonsWare의 우수한 MergeAdapter를 사용 하고 있지만이 배지 활동에는 어쨌든 어댑터가 1 개뿐입니다. 이 사실을 어느 쪽이든 언급하고 싶었습니다).

나는 코드를 살펴본 결과 누출되는 것을 찾을 수 없었습니다. 내가 찾을 수있는 모든 것을 지우고 무효화했고 심지어 System.gc () 왼쪽과 오른쪽까지도 앱이 충돌합니다.

나는 여전히 스택에있는 비활성 활동이 왜 수확되지 않는지 이해하지 못하며, 그것을 정말로 이해하고 싶습니다.

이 시점에서 나는 도움이 될 수있는 힌트, 조언, 해결책을 찾고 있습니다.

감사합니다.


나는 여전히 스택에있는 비활성 활동이 왜 수확되지 않는지 이해하지 못하며, 그것을 정말로 이해하고 싶습니다.

이것은 일이 작동하는 방식이 아닙니다. 활동 수명주기에 영향을 미치는 유일한 메모리 관리는 모든 프로세스 전역 메모리입니다. Android는 메모리가 부족하다고 판단하므로 일부를 되찾기 위해 백그라운드 프로세스를 종료해야합니다.

애플리케이션이 점점 더 많은 활동을 시작하는 포 그라운드에있는 경우 백그라운드로 이동하지 않으므로 시스템이 프로세스를 종료하기 전에 항상 로컬 프로세스 메모리 제한에 도달합니다. (그리고 프로세스를 종료하면 현재 포 그라운드에있는 모든 활동을 포함하여 모든 활동을 호스팅하는 프로세스 가 종료됩니다.)

그래서 당신의 기본적인 문제는 당신이 너무 많은 활동을 동시에 실행하게하고 / 또는 각 활동이 너무 많은 자원을 보유하고 있다는 것입니다.

잠재적으로 무거운 활동을 임의의 수에 의존하지 않도록 탐색을 다시 디자인하면됩니다. onStop ()에서 상당한 양의 작업을 수행하지 않는 한 (예 : setContentView ()를 호출하여 활동의 뷰 계층 구조를 지우고 다른 변수의 변수를 지우는 등) 메모리가 부족하게됩니다.

이 임의의 활동 스택을 메모리를보다 엄격하게 관리하는 단일 활동으로 대체하기 위해 새로운 Fragment API를 사용하는 것을 고려할 수 있습니다. 예를 들어 프래그먼트의 백 스택 기능을 사용하는 경우 프래그먼트가 백 스택으로 이동하여 더 이상 표시되지 않으면 onDestroyView () 메서드가 호출되어 뷰 계층을 완전히 제거하여 풋 프린트를 크게 줄입니다.

이제 뒤로 누르고, 활동으로 이동하고, 뒤로 누르고, 다른 활동으로 이동하는 등의 흐름에서 충돌이 발생하고 딥 스택이없는 한 누수가 있습니다. 이 블로그 게시물은 누출을 디버그하는 방법을 설명합니다. http://android-developers.blogspot.com/2011/03/memory-analysis-for-android.html


몇 가지 팁 :

  1. 활동 컨텍스트가 누출되지 않았는지 확인하십시오.

  2. 비트 맵에 대한 참조를 유지하지 않도록하십시오. Activity # onStop의 모든 ImageView를 다음과 같이 정리하십시오.

    Drawable d = imageView.getDrawable();  
    if (d != null) d.setCallback(null);  
    imageView.setImageDrawable(null);  
    imageView.setBackgroundDrawable(null);
    
  3. 더 이상 필요하지 않으면 비트 맵을 재활용하십시오.

  4. memory-lru와 같은 메모리 캐시를 사용하는 경우 많은 메모리를 사용하지 않는지 확인하십시오.

  5. 이미지는 많은 메모리를 차지할뿐만 아니라 메모리에 다른 데이터를 너무 많이 보관하지 않도록하십시오. 앱에 무한 목록이있는 경우 쉽게 발생할 수 있습니다. DataBase에서 데이터를 캐시 해보십시오.

  6. Android 4.2에는 하드웨어 가속에 버그 (stackoverflow # 13754876) 가 있으므로 hardwareAccelerated=true매니페스트에서 사용하면 메모리 누수가 발생합니다. GLES20DisplayList-(2) 단계를 수행하고 다른 사람이이 비트 맵을 참조하지 않는 경우에도 참조를 유지하십시오. 여기에 다음이 필요합니다.

    a) api 16/17에 대한 하드웨어 가속 비활성화
    또는
    b) 비트 맵을 보유한 뷰 분리

  7. Android 3 이상 android:largeHeap="true"에서는 AndroidManifest. 그러나 그것은 당신의 기억 문제를 해결하지 않고 단지 연기합니다.

  8. 무한 탐색과 같이 필요한 경우 Fragments를 선택해야합니다. 따라서 하나의 활동이 있으며 조각간에 전환됩니다. 이렇게하면 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


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


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.


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.


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


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.


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.


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.

참고URL : https://stackoverflow.com/questions/7536988/android-app-out-of-memory-issues-tried-everything-and-still-at-a-loss

반응형