developer tip

Android에서 내 애플리케이션의 메모리 사용량을 어떻게 확인합니까?

optionbox 2020. 9. 29. 07:38
반응형

Android에서 내 애플리케이션의 메모리 사용량을 어떻게 확인합니까?


프로그래밍 방식으로 Android 애플리케이션에서 사용 된 메모리를 어떻게 찾을 수 있습니까?

할 수있는 방법이 있기를 바랍니다. 또한 휴대 전화의 무료 메모리도 어떻게 얻나요?


Linux와 같은 최신 운영 체제에서 메모리 사용은 매우 복잡하고 이해하기 어려운 영역입니다. 사실 당신이 얻는 숫자를 정확하게 해석 할 가능성은 극히 낮습니다. (다른 엔지니어와 함께 메모리 사용량 수치를 볼 때마다 거의 항상 그들이 실제로 의미하는 바에 대한 긴 논의가있어 모호한 결론을 내릴뿐입니다.)

참고 : 이제 여기에있는 자료의 대부분을 다루고 Android 상태에 대한 최신 정보 인 앱의 메모리 관리에 대한 훨씬 더 광범위한 문서 가 있습니다.

먼저 Android에서 메모리를 관리하는 방법에 대한 논의가있는이 기사의 마지막 부분을 읽는 것입니다.

Android 2.0부터 서비스 API 변경

이제 ActivityManager.getMemoryInfo()전체 메모리 사용량을 살펴볼 수있는 최고 수준의 API입니다. 이것은 주로 시스템이 백그라운드 프로세스에 대한 메모리가 더 이상 없어지는 정도를 측정하는 데 도움이되므로 서비스와 같은 필요한 프로세스를 종료해야합니다. 순수 자바 애플리케이션의 경우, 이는 하나의 앱이이 지점까지 시스템에 스트레스를주지 않도록 부분적으로 자바 힙 제한이 있기 때문에 거의 사용되지 않습니다.

하위 수준에서는 Debug API를 사용하여 메모리 사용량에 대한 원시 커널 수준 정보를 가져올 수 있습니다. android.os.Debug.MemoryInfo

2.0부터 ActivityManager.getProcessMemoryInfo다른 프로세스에 대한이 정보를 가져 오는 API도 있습니다 . ActivityManager.getProcessMemoryInfo (int [])

이 모든 데이터가 포함 된 저수준 MemoryInfo 구조를 반환합니다.

    /** The proportional set size for dalvik. */
    public int dalvikPss;
    /** The private dirty pages used by dalvik. */
    public int dalvikPrivateDirty;
    /** The shared dirty pages used by dalvik. */
    public int dalvikSharedDirty;

    /** The proportional set size for the native heap. */
    public int nativePss;
    /** The private dirty pages used by the native heap. */
    public int nativePrivateDirty;
    /** The shared dirty pages used by the native heap. */
    public int nativeSharedDirty;

    /** The proportional set size for everything else. */
    public int otherPss;
    /** The private dirty pages used by everything else. */
    public int otherPrivateDirty;
    /** The shared dirty pages used by everything else. */
    public int otherSharedDirty;

그러나 차이가 사이에 무엇으로 Pss, PrivateDirty그리고 SharedDirty... 음 이제 재미가 시작됩니다.

Android (및 일반적으로 Linux 시스템)의 많은 메모리는 실제로 여러 프로세스에서 공유됩니다. 따라서 프로세스가 사용하는 메모리 양은 실제로 명확하지 않습니다. 디스크에 페이징을 추가하면 (Android에서 사용하지 않는 스왑은 말할 것도없고) 명확하지 않습니다.

따라서 실제로 각 프로세스에 매핑 된 모든 물리적 RAM을 가져 와서 모든 프로세스를 합산하면 실제 총 RAM보다 훨씬 많은 수를 얻게 될 것입니다.

Pss또한 해당 페이지를 사용하여 다른 프로세스의 수의 비율에 의해 조정되는 과정에서 RAM의 기본적으로 각 페이지 - 수 계정 메모리 공유에 소요되는 커널 계산하는 통계이다. 이 방법으로 (이론적으로) 모든 프로세스에서 pss를 더하여 사용중인 총 RAM을 확인하고 프로세스 간의 pss를 비교하여 상대적 가중치에 대한 대략적인 아이디어를 얻을 수 있습니다.

여기서 또 다른 흥미로운 측정 항목 PrivateDirty은 기본적으로 디스크로 페이징 할 수없고 (디스크의 동일한 데이터에 의해 지원되지 않음) 다른 프로세스와 공유되지 않는 프로세스 내부 RAM의 양입니다. 이를 확인하는 또 다른 방법은 해당 프로세스가 사라질 때 시스템에서 사용할 수있는 RAM입니다 (그리고 아마도 캐시 및 기타 용도로 빠르게 포함됨).

이것이 바로이를위한 SDK API입니다. 그러나 개발자로서 장치로 할 수있는 일이 더 많습니다.

를 사용 adb하면 실행중인 시스템의 메모리 사용에 대한 많은 정보를 얻을 수 있습니다. 일반적인 adb shell dumpsys meminfo것은 위의 정보와 기타 다양한 정보를 포함하는 각 Java 프로세스의 메모리 사용에 대한 정보를 뱉어내는 명령 입니다. 예를 들어 adb shell dumpsys meminfo system시스템 프로세스 제공 하는 것과 같이 단일 프로세스의 이름이나 pid를 볼 수도 있습니다.

** pid 890 [시스템]의 MEMINFO **
                    기본 dalvik 기타 합계
            크기 : 10940 7047 N / A 17987
       할당 됨 : 8943 5516 N / A 14459
            무료 : 336 1531 N / A 1867
           (시) : 4585 9282 11916 25783
  (더티 공유) : 2184 3596916 6696
    (프라이빗 더티) : 4504 5956 7456 17916

 사물
           조회수 : 149 ViewRoots : 4
     AppContexts : 13 활동 : 0
          자산 : 4 자산 관리자 : 4
   로컬 바인더 : 141 프록시 바인더 : 158
사망 수혜자 : 49 명
 OpenSSL 소켓 : 0

 SQL
            힙 : 205 dbFiles : 0
       numPagers : 0 inactivePageKB : 0
    activePageKB : 0

맨 위 섹션은 size특정 힙의 주소 공간에있는 총 크기이고 allocated힙이 보유하고 있다고 생각하는 실제 할당의 kb, free추가 할당을 위해 힙에 사용 가능한 나머지 kb pss이며 priv dirty동일합니다. 각 힙과 연관된 페이지에 대해 앞서 설명한대로.

모든 프로세스에서 메모리 사용량을 확인하려는 경우 명령을 사용할 수 있습니다 adb shell procrank. 동일한 시스템에 대한 출력은 다음과 같습니다.

  PID 대 Rss Pss Uss cmdline
  890 84456K 48668K 25850K 21284K 시스템 _ 서버
 1231 50748K 39088K 17587K 13792K com.android.launcher2
  947 34488K 28528K 10834K 9308K com.android.wallpaper
  987 26964K 26956K 8751K 7308K com.google.process.gapps
  954 24300K ​​24296K 6249K 4824K com.android.phone
  948 23020K 23016K 5864K 4748K com.android.inputmethod.latin
  888 25728K 25724K 5774K 3668K 접합자
  977 24100K 24096K 5667K 4340K android.process.acore
...
   59336K 332K 99K 92K / system / bin / 설치됨
   60 396K 392K 93K 84K / system / bin / keystore
   51 280K 276K 74K 68K / system / bin / servicemanager
   54256K 252K 69K 64K / system / bin / debuggerd

여기서 VssRss열은 기본적으로 노이즈입니다 (이는 프로세스의 직접적인 주소 공간과 RAM 사용량이며, 프로세스 전반에 걸쳐 RAM 사용량을 더하면 엄청나게 많은 수를 얻게됩니다).

Pss우리가 전에 본, 그리고 것 같다 Uss입니다 Priv Dirty.

흥미로운 점은 여기에서주의해야 할 : Pss그리고 Uss약간 (이상보다 약간) 우리가 본 것을 다릅니다 meminfo. 왜 그런 겁니까? Well procrank는 데이터를 수집하기 위해 다른 커널 메커니즘을 사용하며 meminfo약간 다른 결과를 제공합니다. 왜 그런 겁니까? 솔직히 단서가 없습니다. 나는 이것이 procrank더 정확한 것일 수 있다고 생각 한다. 그러나 이것은 단지 요점을 남긴다 : "소금으로 얻은 모든 메모리 정보를 가져라; 종종 매우 큰 입자."

마지막으로 adb shell cat /proc/meminfo시스템의 전체 메모리 사용량에 대한 요약을 제공하는 명령 이 있습니다. 여기에는 많은 데이터가 있습니다. 논의 할 가치가있는 처음 몇 개의 숫자 만 있습니다 (그리고 소수의 사람들이 이해하는 나머지 숫자와 그들에 대한 소수의 사람들에 대한 나의 질문은 종종 상충되는 설명을 초래합니다).

메모리 합계 : 395144 kB
메모리 없음 : 184936 kB
버퍼 : 880 kB
캐시 됨 : 84104 kB
SwapCached : 0KB

MemTotal 커널 및 사용자 공간에서 사용할 수있는 총 메모리 양입니다 (일반적으로 장치의 실제 실제 RAM보다 적습니다. 일부 RAM은 라디오, DMA 버퍼 등에 필요하기 때문입니다).

MemFree전혀 사용되지 않는 RAM의 양입니다. 여기 보이는 숫자는 매우 높습니다. 일반적으로 Android 시스템에서는 프로세스 실행을 유지하기 위해 사용 가능한 메모리를 사용하기 때문에 몇 MB 밖에되지 않습니다.

Cached파일 시스템 캐시 및 기타 다른 용도로 사용되는 RAM입니다. 일반적인 시스템은 불량 페이징 상태가되지 않도록 20MB 정도가 필요합니다. Android 메모리 부족 킬러는 특정 시스템에 맞게 조정되어 캐시 된 RAM이 너무 많이 소모되어 페이징이 발생하기 전에 백그라운드 프로세스가 종료되도록합니다.


예, 프로그래밍 방식으로 메모리 정보를 얻고 메모리 집약적 인 작업을 수행할지 여부를 결정할 수 있습니다.

다음을 호출하여 VM 힙 크기를 가져옵니다.

Runtime.getRuntime().totalMemory();

다음을 호출하여 할당 된 VM 메모리를 가져옵니다.

Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();

다음을 호출하여 VM 힙 크기 제한을 가져옵니다.

Runtime.getRuntime().maxMemory()

다음을 호출하여 기본 할당 메모리를 가져옵니다.

Debug.getNativeHeapAllocatedSize();

OutOfMemoryError 동작을 파악하고 메모리 사용량을 모니터링하는 앱을 만들었습니다.

https://play.google.com/store/apps/details?id=net.coocood.oomresearch

https://github.com/coocood/oom-research 에서 소스 코드를 얻을 수 있습니다.


이것은 진행중인 작업이지만 이해가 안되는 부분입니다.

ActivityManager activityManager = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);

Log.i(TAG, " memoryInfo.availMem " + memoryInfo.availMem + "\n" );
Log.i(TAG, " memoryInfo.lowMemory " + memoryInfo.lowMemory + "\n" );
Log.i(TAG, " memoryInfo.threshold " + memoryInfo.threshold + "\n" );

List<RunningAppProcessInfo> runningAppProcesses = activityManager.getRunningAppProcesses();

Map<Integer, String> pidMap = new TreeMap<Integer, String>();
for (RunningAppProcessInfo runningAppProcessInfo : runningAppProcesses)
{
    pidMap.put(runningAppProcessInfo.pid, runningAppProcessInfo.processName);
}

Collection<Integer> keys = pidMap.keySet();

for(int key : keys)
{
    int pids[] = new int[1];
    pids[0] = key;
    android.os.Debug.MemoryInfo[] memoryInfoArray = activityManager.getProcessMemoryInfo(pids);
    for(android.os.Debug.MemoryInfo pidMemoryInfo: memoryInfoArray)
    {
        Log.i(TAG, String.format("** MEMINFO in pid %d [%s] **\n",pids[0],pidMap.get(pids[0])));
        Log.i(TAG, " pidMemoryInfo.getTotalPrivateDirty(): " + pidMemoryInfo.getTotalPrivateDirty() + "\n");
        Log.i(TAG, " pidMemoryInfo.getTotalPss(): " + pidMemoryInfo.getTotalPss() + "\n");
        Log.i(TAG, " pidMemoryInfo.getTotalSharedDirty(): " + pidMemoryInfo.getTotalSharedDirty() + "\n");
    }
}

activityManager.getProcessMemoryInfo ()의 결과에 PID가 매핑되지 않는 이유는 무엇입니까? 분명히 결과 데이터를 의미있게 만들고 싶은데 Google이 결과를 연관시키는 것을 왜 그렇게 어렵게 만들었습니까? 반환 된 결과가 android.os.Debug.MemoryInfo 객체의 배열이기 때문에 전체 메모리 사용량을 처리하려는 경우 현재 시스템이 제대로 작동하지 않지만 이러한 객체는 실제로 연결된 pid를 알려주지 않습니다. 모든 pid의 배열을 단순히 전달하면 결과를 이해할 방법이 없습니다. 내가 사용한다는 것을 알기 때문에 한 번에 두 개 이상의 pid를 전달하는 것은 의미가 없습니다. 그렇다면 activityManager.getProcessMemoryInfo ()가 int 배열 만 취하도록 만드는 이유는 무엇입니까?


Hackbod는 Stack Overflow에 대한 최고의 답변 중 하나입니다. 매우 모호한 피사체에 빛을 비 춥니 다. 많은 도움이되었습니다.

또 다른 유용한 리소스는 꼭 봐야 할 동영상입니다. Google I / O 2011 : Android 앱을위한 메모리 관리


최신 정보:

프로세스 통계 : 앱이 메모리를 관리하는 방법을 발견하는 서비스 인 프로세스 통계 : Dianne Hackborn 의 블로그 게시물 Process Stats : Understanding How Your App Uses RAM by Dianne Hackborn :


Android Studio 0.8.10+에는 Memory Monitor 라는 매우 유용한 도구가 도입되었습니다 .

여기에 이미지 설명 입력

장점 :

  • 사용 가능 및 사용 된 메모리를 그래프로 표시하고 시간 경과에 따른 가비지 수집 이벤트를 표시합니다.
  • 앱 속도 저하가 과도한 가비지 수집 이벤트와 관련이 있는지 빠르게 테스트합니다.
  • 앱 충돌이 메모리 부족과 관련이 있는지 여부를 빠르게 테스트합니다.

여기에 이미지 설명 입력

그림 1. Android 메모리 모니터에서 GC (Garbage Collection) 이벤트 강제 실행

이를 사용하면 앱의 RAM 실시간 소비에 대한 많은 좋은 정보를 얻을 수 있습니다.


1) 적어도 Java에서는 그렇지 않은 것 같습니다.
2)

ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
MemoryInfo mi = new MemoryInfo();
activityManager.getMemoryInfo(mi);
Log.i("memory free", "" + mi.availMem);

우리는 현재 프로세스의 전체 메모리를 얻는 모든 표준 방법에 몇 가지 문제가 있음을 발견했습니다.

  • Runtime.getRuntime().totalMemory(): JVM 메모리 만 반환
  • ActivityManager.getMemoryInfo(), Process.getFreeMemory()및 그 밖의 모든 것 /proc/meminfo-결합 된 모든 프로세스에 대한 메모리 정보를 반환합니다 (예 : android_util_Process.cpp ).
  • Debug.getNativeHeapAllocatedSize()- mallinfo()수행 된 메모리 할당 malloc()및 관련 함수 에 대한 정보 만 반환하는 사용 ( android_os_Debug.cpp 참조 )
  • Debug.getMemoryInfo()-일을하지만 너무 느립니다. Nexus 6 에서 한 번의 통화 200ms 가 걸립니다 . 성능 오버 헤드로 인해이 함수를 정기적으로 호출하고 모든 호출이 상당히 눈에 띄기 때문에이 함수를 쓸모 없게 만듭니다 ( android_os_Debug.cpp 참조 ).
  • ActivityManager.getProcessMemoryInfo(int[])- Debug.getMemoryInfo()내부적으로 호출 ( ActivityManagerService.java 참조 )

마지막으로 다음 코드를 사용했습니다.

const long pageSize = 4 * 1024; //`sysconf(_SC_PAGESIZE)`
string stats = File.ReadAllText("/proc/self/statm");
var statsArr = stats.Split(new [] {' ', '\t', '\n'}, 3);

if( statsArr.Length < 2 )
    throw new Exception("Parsing error of /proc/self/statm: " + stats);

return long.Parse(statsArr[1]) * pageSize;

VmRSS 메트릭을 반환합니다 . 여기에서 자세한 내용을 찾을 수 있습니다 : 1 , 2 , 3 .


추신 : 성능이 중요한 요구 사항이 아닌 경우 테마 에 프로세스의 개인 메모리 사용량 추정 하는 방법에 대한 실제적이고 간단한 코드 조각이 여전히 부족하다는 것을 알았습니다 .

Debug.MemoryInfo memInfo = new Debug.MemoryInfo();
Debug.getMemoryInfo(memInfo);
long res = memInfo.getTotalPrivateDirty();

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) 
    res += memInfo.getTotalPrivateClean(); 

return res * 1024L;

Android 스튜디오 3.0에서는 앱이 CPU, 메모리, 네트워크 및 배터리 리소스를 사용하는 방식을 이해하는 데 도움이되는 android-profiler를 도입했습니다.

https://developer.android.com/studio/profile/android-profiler

여기에 이미지 설명 입력


위에 분명히 도움이 될 많은 답변이 있지만 (adb 메모리 도구에 대한 2 일간의 여유와 연구 후) 저도 제 의견을 도울 수 있다고 생각 합니다.

As Hackbod says : Thus if you were to take all of the physical RAM actually mapped in to each process, and add up all of the processes, you would probably end up with a number much greater than the actual total RAM. so there is no way you can get exact amount of memory per process.

But you can get close to it by some logic..and I will tell how..

There are some API like android.os.Debug.MemoryInfo and ActivityManager.getMemoryInfo() mentioned above which you already might have being read about and used but I will talk about other way

So firstly you need to be a root user to get it work. Get into console with root privilege by executing su in process and get its output and input stream. Then pass id\n (enter) in ouputstream and write it to process output, If will get an inputstream containing uid=0, you are root user.

Now here is the logic which you will use in above process

When you get ouputstream of process pass you command (procrank, dumpsys meminfo etc...) with \n instead of id and get its inputstream and read, store the stream in bytes[ ] ,char[ ] etc.. use raw data..and you are done!!!!!

permission :

<uses-permission android:name="android.permission.FACTORY_TEST"/>

Check if you are root user :

// su command to get root access
Process process = Runtime.getRuntime().exec("su");         
DataOutputStream dataOutputStream = 
                           new DataOutputStream(process.getOutputStream());
DataInputStream dataInputStream = 
                           new DataInputStream(process.getInputStream());
if (dataInputStream != null && dataOutputStream != null) {
   // write id to console with enter
   dataOutputStream.writeBytes("id\n");                   
   dataOutputStream.flush();
   String Uid = dataInputStream.readLine();
   // read output and check if uid is there
   if (Uid.contains("uid=0")) {                           
      // you are root user
   } 
}

Execute your command with su

Process process = Runtime.getRuntime().exec("su");         
DataOutputStream dataOutputStream = 
                           new DataOutputStream(process.getOutputStream());
if (dataOutputStream != null) {
 // adb command
 dataOutputStream.writeBytes("procrank\n");             
 dataOutputStream.flush();
 BufferedInputStream bufferedInputStream = 
                     new BufferedInputStream(process.getInputStream());
 // this is important as it takes times to return to next line so wait
 // else you with get empty bytes in buffered stream 
 try {
       Thread.sleep(10000);
 } catch (InterruptedException e) {                     
       e.printStackTrace();
 }
 // read buffered stream into byte,char etc.
 byte[] bff = new byte[bufferedInputStream.available()];
 bufferedInputStream.read(bff);
 bufferedInputStream.close();
 }
}

logcat : 결과

API의 일부 인스턴스 대신 콘솔에서 단일 문자열로 원시 데이터를 가져옵니다 . 이는 수동으로 분리해야하므로 저장하기 복잡합니다 .

이것은 단지 시도입니다. 내가 뭔가를 놓친 경우 나에게 제안하십시오

참고 URL : https://stackoverflow.com/questions/2298208/how-do-i-discover-memory-usage-of-my-application-in-android

반응형