IT이야기

Android에서 내 응용 프로그램의 메모리 사용량을 검색하는 방법

cyworld 2022. 5. 14. 22:32
반응형

Android에서 내 응용 프로그램의 메모리 사용량을 검색하는 방법

내 Android 응용 프로그램에 사용된 메모리를 프로그래밍 방식으로 어떻게 찾을 수 있는가?

나는 그것을 할 수 있는 방법이 있기를 바란다.게다가, 전화기의 무료 메모리도 어떻게 얻을 수 있을까?

Linux와 같은 최신 운영 체제의 메모리 사용은 매우 복잡하고 이해하기 어려운 영역이라는 점에 유의하십시오.사실 어떤 수를 받든 실제로 정확하게 해석할 확률은 극히 낮다.(다른 엔지니어들과 함께 메모리 사용량을 볼 때마다 그것들이 의미하는 실제 의미는 모호한 결론밖에 나지 않는다는 것에 대해서는 항상 긴 논의가 있다.)

참고: 이제 우리는 앱의 메모리 관리에 대한 훨씬 더 광범위한 문서를 보유하고 있으며, 이 문서에는 대부분의 자료가 포함되어 있으며 안드로이드의 상태를 보다 최신 상태로 유지하고 있다.

먼저 이 글의 마지막 부분을 읽고 Android에서 메모리를 관리하는 방법에 대해 논의하십시오.

Android 2.0부터 시작되는 서비스 API 변경 사항

지금ActivityManager.getMemoryInfo()전체 메모리 사용량을 살펴보기 위한 최고 수준의 API 입니다.이는 대부분 애플리케이션이 시스템이 백그라운드 프로세스에 대한 메모리를 더 이상 갖지 못하게 되어 서비스처럼 필요한 프로세스를 제거하기 시작해야 하는지를 측정하는 데 도움이 된다.순수 Java 응용프로그램의 경우, Java 힙 한도가 부분적으로 한 앱이 시스템에 스트레스를 주지 않도록 하기 위해 있기 때문에, 이것은 거의 유용하지 않아야 한다.

더 낮은 레벨로 이동하면 Debug API를 사용하여 메모리 사용에 대한 원시 커널 레벨 정보인 Android.os를 얻을 수 있다.디버그.메모리정보

2 2.0부 API도 ,ActivityManager.getProcessMemoryInfo, 다른 프로세스에 대한 정보를 얻으려면:ActivityManager.getProcessMemoryInfo(int[])

로우 레벨 메모리를 반환함이 모든 데이터가 포함된 정보 구조:

    /** 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;

하지만 그 차이점이 무엇인지에 대해서는PssPrivateDirty그리고SharedDirty된다... 이제 재미는 시작된다.

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

따라서 각 프로세스에 실제로 매핑된 물리적 RAM을 모두 가져다가 모든 프로세스를 합친다면 실제 전체 RAM보다 훨씬 큰 숫자로 끝날 수 있다.

Pss각는 다른 number는 이 한다. 기본적으로 프로세스의 각 RAM 페이지는 해당 페이지를 사용하는 다른 프로세스 수의 비율로 확장된다.이렇게 하면 (이론적으로) 모든 프로세스에 걸쳐 pss를 합산하여 그들이 사용하고 있는 총 RAM을 볼 수 있고, 프로세스 간 pss를 비교하여 상대적 무게를 대략적으로 파악할 수 있다.

여기서 또 다른 흥미로운 측정기준은PrivateDirty기본적으로 디스크에 페이징할 수 없는 프로세스 내부의 RAM 양(디스크의 동일한 데이터로 백업되지 않음)이며 다른 프로세스와 공유되지 않는다.이를 살펴보는 또 다른 방법은 프로세스가 사라졌을 때(아마도 캐시와 그것의 다른 용도에 빠르게 포함되었을 때) 시스템에 사용할 수 있는 RAM이다.

이것은 SDK APIs에 상당한다.그러나 당신의 장치로 개발자로서 할 수 있는 일이 더 많다.

사용.adb실행 중인 시스템의 메모리 사용에 대해 얻을 수 있는 많은 정보가 있다.흔한 것은 명령이다.adb shell dumpsys meminfo위의 정보뿐만 아니라 다양한 정보를 포함하는 각 자바 프로세스의 메모리 사용에 대한 많은 정보를 뱉어낼 것이다.예를 들어, 단일 프로세스의 이름이나 pid를 사용하여 확인할 수도 있다.adb shell dumpsys meminfo system시스템 프로세스 제공:

** MEMINFO in pid 890 [시스템] **토종 달빅 기타 총계크기: 10940 7047 N/A 17987할당: 8943 5516 N/A 14459무료: 336 1531 N/A 1867(Pss): 4585 9282 11916 25783(1999년 더러움): 2184 3596 916 6696(프라이빗 더티): 4504 5956 7456 17916
물건들보기: 149 ViewRoots: 4AppContexts: 13개 활동: 0자산: 자산 관리기 4대: 4로컬 바인더: 141개의 프록시 바인더: 158사망자 수: 49명OpenSSL 소켓: 0
SQL힙: 205dbFiles: 0NumPagers: 0 비활성PageKB: 0activePageKB: 0

윗부분이 메인 부분인데, 그 부분이 어디다.size 주의 크기allocated힙이 가지고 있다고 생각하는 실제 할당량 kb 입니다.free KB는 추가 수리는 KB이다.pss그리고priv dirty각 힙과 관련된 페이지에 대해 앞에서 논의한 것과 동일하다.

모든 프로세스의 메모리 사용량만 보려면 명령을 사용하십시오.adb shell procrank되는 이 값은 다음과 동일한 시스템에서 출력되는 이 값은 다음과 같다.

PID VSS Rss USS cmdline890 84456K 48668K 25850K 21284K system_server1231 50748K 39088K 1758K 13792K com.android.launcher2947 34488K 28528K 10834K 9308K com.android.벽지를 바르다987 26964K 26956K 8751K 7308K com.google.process.gapps954 24300K 24296K 6249K 4824K com.android.전화를 걸다948 23020K 23016K 5864K com.android.inputmethod.latin888 25728K 25724K 5774K 3668K zygote977 24100K 24096K 5667K 4340K Android.process.acore...59 336K 332K 99K 92K /system/bin/installd60 396K 392K 93K 84K /system/bin/keystore51 280K 276K 74K /system/bin/servicemanager54 256K 252K 69K /system/bin/debuggerd

여기 더Vss그리고Rss열은 기본적으로 노이즈(이것은 프로세스의 직진 주소 공간과 RAM 사용량이며, 프로세스 전체에서 RAM 사용량을 합산하면 터무니없이 많은 숫자를 얻을 수 있다.)

Pss우리가 전에 봤던 것처럼Uss이다Priv Dirty.

여기서 주목해야 할 흥미로운 사항:Pss그리고Uss에서 본 .meminfo왜 그럴까?Well procrank는 다른 커널 메커니즘을 사용하여 데이터를 수집한다.meminfo그래, 그리고 약간 다른 결과를 내놓는다.왜 그런 것일까요?솔직히 난 전혀 모르겠어.믿어요procrank 더 ... ... ... ... ... ... ... ... ... ... ... really really really really really really really really really하지만, 이것은 요점을 남긴다: "어떤 기억 정보라도 소금 알갱이로 가져간다; 종종 매우 큰 알갱이로 얻는다."

마지막으로 명령이 있다.adb shell cat /proc/meminfo시스템의 전체 메모리 사용량에 대한 요약을 제공한다.여기에는 많은 자료가 있고, 토론할 가치가 있는 첫 몇 개의 숫자만 있다(그리고 나머지 숫자들은 소수의 사람들에 의해 이해되고, 그들에 대해 내가 그 몇 명의 사람들에 대한 질문들은 종종 상충되는 설명을 낳는다).

MemTotal: 395144 kBMemFree: 184936 kB버퍼: 880kB캐시됨: 84104kB스왑 캐시: 0kB

MemTotal커널 및 사용자 공간에 사용할 수 있는 총 메모리 양(무선, DMA 버퍼 등에 필요한 RAM의 일부가 장치 실제 물리적 RAM보다 적음)

MemFree전혀 사용되지 않는 램의 양이다.여기에 보이는 숫자는 매우 많으며, 일반적으로 Android 시스템에서는 프로세스 실행을 위해 사용 가능한 메모리를 사용하려고 하기 때문에 몇 MB에 지나지 않는다.

CachedRAM은 파일 시스템 캐시와 기타 등에 사용된다.일반적인 시스템에는 20MB 정도의 용량이 있어야 호출 상태가 불량해질 수 있다. Android out memory killer는 캐시된 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");
    }
}

PID가 activityManager.getProcessMemoryInfo()의 결과에 매핑되지 않는 이유는?분명히 당신은 결과 데이터를 의미 있게 만들고자 하는데, 왜 구글은 결과의 상관관계를 분석하는 것을 그렇게 어렵게 만들었는가?반환된 결과가 안드로이드.os의 배열이기 때문에 전체 메모리 사용량을 처리하려면 현재 시스템이 제대로 작동하지도 않는다.디버그.메모리정보 오브젝트, 그러나 그 오브젝트들 중 어떤 것도 실제로 그들이 어떤 pids와 연관되어 있는지 알려주지 않는다.모든 pids의 배열을 간단히 통과하면 결과를 이해할 방법이 없을 것이다.용도가 있는 것으로 알고 있기 때문에 한 번에 하나 이상의 pid를 전달하는 것은 무의미하게 만들고, 그렇다면 activityManager.getProcessMemoryInfo()가 인트 어레이만 사용하도록 만드는 이유는 무엇인가?

Hackbod's는 Stack Overflow에 대한 최고의 대답 중 하나이다.그것은 매우 애매한 문제에 빛을 던진다.그것은 나에게 많은 도움을 주었다.

또 다른 유용한 자료로는 반드시 봐야 할 비디오:Google I/O 2011: Android 앱을 위한 메모리 관리


업데이트:

프로세스 통계(Process Stats) - Dianne Hackborn의 RAM 사용 방법 이해 블로그 게시물에 설명된 앱 메모리 관리 방법을 알아보는 서비스:

Android Studio 0.8.10+는 Memory Monitor라는 믿을 수 없을 정도로 유용한 도구를 선보였다.

여기에 이미지 설명을 입력하십시오.

이 기능이 제공하는 이점:

  • 그래프에 사용 가능한 메모리 및 사용된 메모리 및 시간 경과에 따른 가비지 수집 이벤트 표시
  • 앱 속도가 과도한 가비지 수집 이벤트와 관련이 있는지 신속하게 테스트
  • 애플리케이션 충돌이 메모리 부족과 관련이 있는지 여부를 신속하게 테스트

여기에 이미지 설명을 입력하십시오.

F igure 1.Android Memory Monitor에서 GC(배지 수집) 이벤트 강제 적용

앱을 사용하면 앱의 RAM 실시간 소비량에 대한 충분한 정보를 얻을 수 있다.

1) 아니겠지, 적어도 자바에서는 아니겠지.
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() 기타 기반 - 결합된 모든 프로세스에 대한 메모리 정보(예: Android_util_Process.cpp)
  • Debug.getNativeHeapAllocatedSize() - 사용mallinfo()다음에 의해 수행된 메모리 할당에 대한 정보를 반환하는 방법malloc()및 관련 기능만 해당(Android_os_Debug.cpp 참조
  • Debug.getMemoryInfo() - 일은 하지만 너무 느리다.넥서스6에서 한 통화에 약 200ms가 걸린다.성능 오버헤드는 우리가 규칙적으로 부르는 것처럼 이 기능을 무용지물로 만들고 모든 통화는 상당히 눈에 띈다(안드로이드_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 메트릭을 반환한다.당신은 여기서 그것에 대해 더 자세한 것을 찾을 수 있다: 하나, 둘, .


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

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, 메모리, 네트워크 및 배터리 리소스를 사용하는 방법을 이해하는 데 도움이 되는 안드로이드 프로파일러를 도입했다.

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

여기에 이미지 설명을 입력하십시오.

위의 답변은 분명히 당신에게 도움이 되겠지만 (2일 간의 여유와 adb 기억장치에 대한 연구 끝에)의견도 도와줄 수 있을 것 같다.

Hackbodd의 말처럼 : 따라서 프로세스에 실제로 매핑된 물리적 RAM을 모두 가져다가 모든 프로세스를 합친다면 실제 전체 RAM보다 훨씬 숫자로 끝날 것이다. 따라서 프로세스당 정확한 메모리 양을 얻을 수 있는 방법은 없다.

하지만 논리적으로 접근하면..그리고 내가 어떻게..

, 를 들면 API가 있다.android.os.Debug.MemoryInfo그리고ActivityManager.getMemoryInfo()위에서 언급한 바와 같이, 당신이 이미 읽혀지고 사용되어질지도 모르지만, 나는 다른 방법에 대해 말하겠다.

따라서 먼저 루트 사용자가 되어야 제대로 작동할 수 있다.실행을 통해 루트 권한으로 콘솔에 가져오기su에 그리고 의 진행을 .output and input stream. 그럼 통과.id\n (입력) ouputstream에 입력하여 출력을 처리할 때 if가 다음을 포함하는 입력 스트림을 가져올 경우uid=0, 루트 사용자.

이제 위의 프로세스에서 사용할 논리가 여기에 있다.

프로세스 패스의 upuputstream을 얻으면 명령어(프록, 덤프 메민포...)를 id 대신 받아라.inputstream그리고 스트림을 바이트[ ], char[ ] 등 단위로 저장한다.원시 데이터를 사용한다.그럼 넌 끝장이야!!!!!

권한:

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

루트 사용자인지 확인하십시오.

// 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
   } 
}

명령 실 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();
 }
}

로그캣 :

어떤 API의 인스턴스 대신 콘솔에서 단일 문자열로 Raw data를 얻을 수 있는데, 이는 수동으로 분리해야 하기 때문에 저장하기가 복잡하다.

이건 그냥 시도일 뿐인데, 혹시 놓친 게 있으면 추천 좀 해줘.

나는 답을 읽다가 혼란스러워서 문서를 읽기로 했다.좋아, 해보자:

네이티브 힙 메모리 사용량

디버그 개체를 사용하여 디바이스의 기본 힙 메모리 크기를 가져올 수 있음:

long nativeTotal = Debug.getNativeHeapSize();
long nativeFree = Debug.getNativeHeapFreeSize();
long nativeAllocated = Debug.getNativeHeapAllocatedSize();
long nativeUsed = nativeTotal - nativeFree;

참고:nativeUsed그리고nativeAllocated가치가 같다

런타임 메모리 사용량

애플리케이션의 Runtime 개체를 사용한 메모리 사용, 모든 애플리케이션에는 인터페이스에 액세스할 수 있는 런타임 개체가 있음:

Runtime runtime = Runtime.getRuntime();
long runtimeMax = runtime.maxMemory();
long runtimeTotal = runtime.totalMemory();
long runtimeFree = runtime.freeMemory();
long runtimeUsed = runtimeTotal - runtimeFree;

시스템 메모리 사용량

ActivityManager를 사용하여 시스템의 메모리 사용량을 얻을 수 있다.MemoryInfo 개체:

ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
activityManager.getMemoryInfo(memoryInfo);
long systemTotal = memoryInfo.totalMem;
long systemFree = memoryInfo.availMem;
long systemUsed = systemTotal - systemFree;

Proc 메모리 사용량

시스템의 메모리 사용량을 읽을 수 있는 또 다른 방법은 /proc/meminfo 파일의 내용을 리눅스 에서 구문 분석하는 것이다.

RandomAccessFile reader = new RandomAccessFile("/proc/meminfo", "r");
Pattern pattern = Pattern.compile("(\\d+)");
Matcher totalMatcher = pattern.matcher(reader.readLine());
totalMatcher.find();
long procTotal = Long.parseLong(totalMatcher.group(1)) * 1024L; // KB to B
Matcher freeMatcher = pattern.matcher(reader.readLine());
freeMatcher.find();
long procFree = Long.parseLong(freeMatcher.group(1)) * 1024L; // KB to B
long procUsed = procTotal - procFree;

그래, 난 아직도 혼란스러워.하지만 메모리 사용량을 얻을 수 있는 방법은 이것뿐이었습니다.응용프로그램을 디버깅하려면 모두 기록하십시오!!!

나는 또한 그들 각자가 그들의 사용법과 정보를 읽을 수 있는 링크를 제공한다.

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

반응형