답변:
Linux와 같은 최신 운영 체제의 메모리 사용은 매우 복잡하고 이해하기 어려운 영역입니다. 실제로 당신이 얻는 숫자를 실제로 정확하게 해석 할 확률은 매우 낮습니다. (다른 엔지니어와의 메모리 사용량을 볼 때마다 매우 모호한 결론을 내릴 수 있다는 사실에 대한 논의가 항상 있습니다.)
참고 : 이제 여기에있는 많은 자료를 다루고 Android 상태에 대한 최신 정보를 제공하는 앱 메모리 관리에 대한 훨씬 더 광범위한 문서 가 있습니다.
우선이 기사의 마지막 부분을 읽어 보는 것이 좋습니다.이 기사에서는 Android에서 메모리를 관리하는 방법에 대해 설명합니다.
이제 ActivityManager.getMemoryInfo()
전체 메모리 사용량을 볼 수있는 최고 수준의 API입니다. 이것은 시스템이 백그라운드 프로세스를 위해 더 이상 메모리를 갖지 않는 정도를 응용 프로그램이 측정 할 수 있도록 도와 주므로 서비스와 같은 필요한 프로세스를 종료해야합니다. 순수 Java 애플리케이션의 경우 하나의 앱이이 시점에서 시스템에 스트레스를 줄 수 없도록 Java 힙 제한이 부분적으로 있기 때문에이 기능은 거의 사용하지 않아야합니다.
하위 수준으로 가면 디버그 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 페이지는 해당 페이지를 사용하는 다른 프로세스 수의 비율로 조정됩니다. 이 방법으로 (이론적으로) 모든 프로세스에서 ps를 합산하여 사용중인 총 RAM을 확인하고 프로세스 간 pss를 비교하여 상대적 가중치를 대략적으로 알 수 있습니다.
여기서 흥미로운 또 다른 지표 PrivateDirty
는 기본적으로 디스크에 페이징 할 수없는 (디스크의 동일한 데이터로 백업되지 않음) 프로세스 내부의 RAM 크기이며 다른 프로세스와 공유되지 않습니다. 이를 보는 또 다른 방법은 해당 프로세스가 사라질 때 시스템에서 사용할 수있는 RAM입니다 (캐시 및 기타 용도로 빠르게 포함됨).
그것은 이것에 대한 SDK API와 거의 같습니다. 그러나 장치 개발자라면 할 수있는 일이 더 많습니다.
를 사용 adb
하면 실행중인 시스템의 메모리 사용에 대해 얻을 수있는 많은 정보가 있습니다. 일반적인 adb shell dumpsys meminfo
것은 위의 정보와 다양한 다른 것들을 포함하는 각 Java 프로세스의 메모리 사용에 대한 많은 정보를 뱉어내는 명령 입니다. 단일 프로세스의 이름이나 pid를 사용 adb shell dumpsys meminfo system
하여 시스템 프로세스를 볼 수 있습니다.
** pid 890 [MEMORY]의 MEMINFO ** 네이티브 달빅 다른 총 크기 : 10940 7047 N / A 17987 할당 : 8943 5516 해당 없음 14459 무료 : 336 1531 해당 없음 1867 (Pss) : 4585 9282 11916 25783 (공유 더티) : 2184 3596916 6696 (개인 더러운) : 4504 5956 7456 17916 사물 조회수 : 149보기 루트 : 4 AppContexts : 13 활동 : 0 자산 : 4 자산 관리자 : 4 로컬 바인더 : 141 프록시 바인더 : 158 사망자 : 49 OpenSSL 소켓 : 0 SQL 힙 : 205 dbFiles : 0 numPagers : 0 비활성 페이지 KB : 0 activePageKB : 0
상부 섹션은 주 하나이며 size
, 특정 힙의 주소 공간의 전체 크기는 allocated
, 힙이 가지고 생각하는 것이 실제 할당의 KB이고 free
나머지 KB 힙 추가 할당을 갖는 자유롭게하고 pss
및은 priv dirty
동일하다 이전에 각 힙과 관련된 페이지에 대해 설명했듯이
모든 프로세스에서 메모리 사용량 만 보려면 명령을 사용할 수 있습니다 adb shell procrank
. 동일한 시스템에서이 결과는 다음과 같습니다.
PID Vss 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 / installd 60 396K 392K 93K 84K / system / bin / keystore 51280K 276K 74K 68K / system / bin / servicemanager 54256K 252K 69K 64K / system / bin / debuggerd
여기에서 Vss
및 Rss
열은 기본적으로 노이즈입니다 (이것은 프로세스의 간단한 주소 공간과 RAM 사용량입니다. 여러 프로세스에서 RAM 사용량을 합하면 엄청나게 많은 수를 얻습니다).
Pss
우리가 전에 본 것과 같고 Uss
입니다 Priv Dirty
.
흥미로운 점은 여기에서주의해야 할 : Pss
그리고 Uss
약간 (이상보다 약간) 우리가 본 것을 다릅니다 meminfo
. 왜 그런 겁니까? procrank는 다른 커널 메커니즘을 사용하여 데이터를 수집하고 meminfo
약간 다른 결과를 제공합니다. 왜 그런 겁니까? 솔직히 나는 단서가 없습니다. procrank
더 정확한 정보 라고 생각합니다 .하지만 실제로는 "소금 한 덩어리로 얻은 기억 정보를 가져 가십시오.
마지막으로 adb shell cat /proc/meminfo
시스템의 전체 메모리 사용량을 요약 한 명령 이 있습니다. 여기에는 많은 데이터가 있으며 토론 할 가치가있는 처음 몇 숫자 (그리고 나머지는 소수의 사람들이 이해하고 소수의 사람들에 대한 나의 질문은 종종 설명이 상충됩니다) :
총계 : 395144 kB MemFree : 184936 kB 버퍼 : 880 kB 캐시 : 84104 kB 스왑 캐시 : 0 kB
MemTotal
커널 및 사용자 공간에 사용 가능한 총 메모리 양입니다 (일부 RAM은 라디오, DMA 버퍼 등에 필요하기 때문에 장치의 실제 실제 RAM보다 적음).
MemFree
전혀 사용되지 않는 RAM의 양입니다. 여기 보이는 숫자는 매우 높습니다. 일반적으로 Android 시스템에서는 사용 가능한 메모리를 사용하여 프로세스를 계속 실행하기 때문에 몇 MB에 불과합니다.
Cached
파일 시스템 캐시 및 기타 것들에 사용되는 RAM입니다. 페이징 상태가 나 빠지지 않도록 일반적인 시스템에는 20MB 정도가 필요합니다. 안드로이드 메모리 부족 킬러는 캐시 된 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 ()의 결과에 매핑되지 않은 이유는 무엇입니까? 결과 데이터를 의미있게 만들고자하는데 왜 Google이 결과를 연관시키는 것이 어려운가? 반환 된 결과가 android.os.Debug.MemoryInfo 객체의 배열이기 때문에 전체 메모리 사용량을 처리하려는 경우 현재 시스템이 제대로 작동하지 않지만 실제로는 어떤 pid와 관련되어 있는지 알려주는 객체가 없습니다. 모든 pid 배열을 단순히 전달하면 결과를 이해할 방법이 없습니다. 내가 그것을 이해함에 따라 한 번에 둘 이상의 pid를 전달하는 것은 의미가 없습니다. 그런 경우 activityManager.getProcessMemoryInfo ()가 int 배열 만 가져 오도록 만드는 이유는 무엇입니까?
Hackbod는 Stack Overflow에 대한 최고의 답변 중 하나입니다. 매우 모호한 피사체에 빛을 비 춥니 다. 그것은 많은 도움이되었습니다.
또 다른 유용한 리소스는 꼭 봐야 할 비디오입니다. Google I / O 2011 : Android 앱용 메모리 관리
최신 정보:
프로세스 통계 : 앱에서 메모리를 관리하는 방법을 검색하는 서비스 인 블로그 통계 : Process Stats : Dianne Hackborn의 RAM 사용 방식 이해 :
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 메트릭을 반환합니다 . 여기에 대한 자세한 내용은 one , two 및 three 에서 찾을 수 있습니다 .
추신 : 성능이 중요한 요구 사항이 아닌 경우 테마 에 프로세스의 개인 메모리 사용 을 추정 하는 방법에 대한 실제적이고 간단한 코드 스 니펫이 여전히 부족하다는 것을 알았습니다 .
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 studio 3.0에서는 앱이 CPU, 메모리, 네트워크 및 배터리 리소스를 사용하는 방법을 이해하는 데 도움이되는 android-profiler를 도입했습니다.
https://developer.android.com/studio/profile/android-profiler
위의 많은 대답이 확실히 당신을 도울 것입니다 (그러나 2 일간의 여유 시간과 adb 메모리 도구에 대한 연구 후) 나는 내 의견으로 도 도울 수 있다고 생각 합니다.
으로 Hackbod는 말한다 : 당신이 실제로 각 프로세스에 매핑 된 모든 실제 RAM을, 그리고 모든 프로세스를 추가했다 따라서 경우에, 당신은 아마 실제 전체 RAM보다 수가 훨씬 더 끝낼 것입니다. 따라서 프로세스 당 정확한 메모리 양을 얻을 수있는 방법이 없습니다.
그러나 당신은 어떤 논리에 의해 그것에 가까워 질 수 있습니다.
이 몇 가지 같은 API입니다
android.os.Debug.MemoryInfo
그리고ActivityManager.getMemoryInfo()
당신이 이미에 대해 읽고 사용했을 수도있는 위에서 언급하지만 나는 다른 방법에 대해 이야기합니다
따라서 먼저 작동하려면 루트 사용자 여야합니다. su
프로세스에서 실행하여 루트 권한으로 콘솔에 들어가서 해당 권한을 가져옵니다 output and input stream
. 그런 다음 통과 id\n
(입력) ouputstream과 프로세스 출력에 기록, 만일이되는 InputStream가 포함 얻을 것이다 uid=0
, 당신은 루트 사용자입니다.
위의 프로세스에서 사용할 논리는 다음과 같습니다.
프로세스 의 출력 스트림을 얻을 때 id 대신 명령 (procrank, dumpsys meminfo 등 ...)을 전달\n
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 대신 콘솔에서 단일 문자열로 원시 데이터를 가져옵니다 . 수동으로 분리해야하므로 저장하기가 복잡합니다 .
이것은 단지 시도입니다, 내가 뭔가를 놓친 경우 제안하십시오