프로그래밍 방식으로 Android 애플리케이션에 사용 된 메모리를 어떻게 찾을 수 있습니까?
그것을 할 수있는 방법이 있기를 바랍니다. 또한 전화의 무료 메모리도 어떻게 얻습니까?
Linux와 같은 최신 운영 체제의 메모리 사용량은매우복잡하고 이해하기 어려운 지역. 실제로 당신이 실제로 얻는 숫자를 정확하게 해석 할 확률은 매우 낮습니다. (다른 엔지니어와 함께 메모리 사용량을 볼 때마다 항상 모호한 결론 만내는 실제 의미에 대해 오랜 토론이 있습니다.)
참고 : 이제 훨씬 더 광범위한 문서가 있습니다.앱의 메모리 관리하기이 자료의 대부분은 여기에서 다루고 있으며 Android의 최신 정보를 제공합니다.
우선 Android에서 메모리를 관리하는 방법에 대한 몇 가지 논의가있는이 기사의 마지막 부분을 읽는 것이 좋습니다.
지금ActivityManager.getMemoryInfo()
는 전체적인 메모리 사용량을 살펴볼 수있는 가장 높은 수준의 API입니다. 이것은 시스템이 백그라운드 프로세스를위한 더 이상의 메모리를 갖지 않으므로 애플리케이션과 같은 필요한 프로세스를 죽이기 시작할 필요가 있다는 것을 애플리케이션이 판단하는 데 도움이됩니다. 순수한 Java 응용 프로그램의 경우이 기능은 거의 사용하지 않아야합니다. Java 힙 제한은 한 응용 프로그램이 시스템을이 시점까지 강조하지 못하도록하기위한 것입니다.
낮은 레벨로 가면 Debug API를 사용하여 메모리 사용에 대한 원시 커널 수준 정보를 얻을 수 있습니다.android.os.Debug.MemoryInfo
2.0부터는 API도 있습니다.ActivityManager.getProcessMemoryInfo
, 다른 프로세스에 대한이 정보를 얻으려면 :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 시스템 전반)의 많은 메모리는 실제로 여러 프로세스에서 공유됩니다. 따라서 프로세스가 사용하는 메모리의 양은 실제로 명확하지 않습니다. 그 페이징을 디스크에 추가하십시오 (우리가 안드로이드에서 사용하지 않는 것은 교환 할 필요가 없습니다).
따라서 실제로 각 프로세스에 매핑 된 실제 RAM을 모두 가져 와서 모든 프로세스를 추가하는 경우 실제로 총 RAM보다 훨씬 많은 수의 RAM이 생성됩니다.
그만큼Pss
숫자는 메모리 공유를 고려한 커널 계산의 기준입니다. 기본적으로 프로세스의 RAM 페이지는 해당 페이지를 사용하는 다른 프로세스 수의 비율에 따라 조정됩니다. 이렇게하면 이론적으로 모든 프로세스에서 pss를 추가하여 사용중인 총 RAM을 확인하고 프로세스 간 pss를 비교하여 상대적인 가중치를 대략적으로 파악할 수 있습니다.
다른 흥미로운 측정 항목은 다음과 같습니다.PrivateDirty
기본적으로 디스크에 페이징 할 수없는 프로세스 내부의 RAM 크기 (디스크의 동일한 데이터로 백업되지 않음)이며 다른 프로세스와 공유되지 않습니다. 이것을 살펴볼 또 다른 방법은 RAM이 프로세스가 사라지면 시스템에서 사용할 수있게 될 것입니다 (그리고 아마도 캐시와 다른 용도로 빠르게 포함될 것입니다).
이것이 SDK API입니다. 그러나 장치로 개발자로서 더 많은 일을 할 수 있습니다.
사용adb
실행중인 시스템의 메모리 사용에 관해 얻을 수있는 많은 정보가 있습니다. 공통적 인 것은 명령입니다.adb shell dumpsys meminfo
각 자바 프로세스의 메모리 사용에 관한 많은 정보를 뱉어 내고, 위의 정보와 다양한 것들을 포함 할 것이다. 예를 들어, 단일 프로세스의 이름이나 PID를 볼 수도 있습니다adb shell dumpsys meminfo system
나에게 시스템 프로세스를 알려줘.
** MEMINFO in pid 890 [system] ** native dalvik other total size: 10940 7047 N/A 17987 allocated: 8943 5516 N/A 14459 free: 336 1531 N/A 1867 (Pss): 4585 9282 11916 25783 (shared dirty): 2184 3596 916 6696 (priv dirty): 4504 5956 7456 17916 Objects Views: 149 ViewRoots: 4 AppContexts: 13 Activities: 0 Assets: 4 AssetManagers: 4 Local Binders: 141 Proxy Binders: 158 Death Recipients: 49 OpenSSL Sockets: 0 SQL heap: 205 dbFiles: 0 numPagers: 0 inactivePageKB: 0 activePageKB: 0
맨 위 섹션이 주요 섹션입니다.size
특정 힙의 주소 공간에있는 전체 크기입니다.allocated
힙이 가지고 있다고 생각하는 실제 할당의 kb입니다.free
힙이 추가 할당을 위해 남은 나머지 킬로 비트입니다.pss
과priv dirty
각각의 힙과 연관된 페이지에 대해 이전에 논의 된 것과 동일합니다.
모든 프로세스에서 메모리 사용량을보고 싶다면 다음 명령을 사용할 수 있습니다.adb shell procrank
. 같은 시스템에 출력되는 내용은 다음과 같습니다.
PID Vss Rss Pss Uss cmdline 890 84456K 48668K 25850K 21284K system_server 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 zygote 977 24100K 24096K 5667K 4340K android.process.acore ... 59 336K 332K 99K 92K /system/bin/installd 60 396K 392K 93K 84K /system/bin/keystore 51 280K 276K 74K 68K /system/bin/servicemanager 54 256K 252K 69K 64K /system/bin/debuggerd
여기에Vss
과Rss
열은 기본적으로 노이즈입니다 (프로세스의 RAM 사용량을 합산하면 엄청나게 큰 숫자를 얻습니다).
Pss
우리가 전에 보았던 것과 같습니다.Uss
~이다.Priv Dirty
.
여기서 주목할 흥미로운 점은 :Pss
과Uss
우리가 보았던 것과 약간 (또는 약간 이상) 다르다.meminfo
. 왜 그런가요? 좋은 procrank는 다른 커널 메커니즘을 사용하여 데이터를 수집합니다.meminfo
그렇습니다. 그리고 그들은 약간 다른 결과를줍니다. 왜 그런가요? 솔직히 나는 단서가 없다. 나는 믿는다procrank
더 정확한 것일 수 있습니다. 그러나 실제로 이것은 단지 한 가지 곡식으로 얻는 기억 정보를 취합니다. 흔히 매우 큰 곡식입니다.
마지막으로 명령이 있습니다.adb shell cat /proc/meminfo
시스템의 전체 메모리 사용량에 대한 요약을 제공합니다. 여기에는 많은 데이터가 있습니다. 토론 할 가치가있는 처음 몇 개의 숫자 만 있습니다. (나머지 사람들은 소수의 사람들 만 이해할 수 있으며, 소수의 사람들에 대한 나의 질문은 종종 상충되는 설명이됩니다).
MemTotal: 395144 kB MemFree: 184936 kB Buffers: 880 kB Cached: 84104 kB SwapCached: 0 kB
MemTotal
커널과 사용자 공간에서 사용할 수있는 총 메모리 크기입니다 (보통 RAM, DMA 버퍼 등에 해당 RAM이 필요하기 때문에 실제 실제 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 's는 스택 오버플로에 대한 최고의 해답 중 하나입니다. 그것은 아주 애매한 주제에 빛을 던집니다. 그것은 나를 많이 도왔다.
정말 도움이되는 또 다른 리소스는 꼭보아야 할 비디오입니다.Google I / O 2011 : Android 앱을위한 메모리 관리
최신 정보:
프로세스 통계, 앱이 블로그 게시물에서 설명한 메모리 관리 방법을 발견하는 서비스프로세스 통계 : 앱에서 RAM을 사용하는 방식 이해Dianne Hackborn 저 :
Android Studio 0.8.10 이상은 믿을 수 없을 정도로 유용한 도구를 선보였습니다.메모리 모니터.
그것은 무엇을 위해 좋은가 :
- 그래프에서 사용 가능하고 사용 된 메모리 및 가비지 수집 표시 시간 경과에 따른 사건.
- 앱의 느려지는 여부를 빠르게 테스트 과도한 가비지 수집 이벤트와 관련됩니다.
- 신속한 테스트 앱 충돌 여부는 메모리 부족과 관련이있을 수 있습니다.
그림 1. 안드로이드 메모리 모니터에서 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()
- 일을하지만 너무 느립니다. 그것은 대략 가지고 간다200ms...에Nexus 6하나의 통화. 성능 오버 헤드로 인해 우리는이 기능을 쓸모 없게 만들고 정기적으로 호출하므로 모든 호출이 상당히 눈에니다 (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미터법. 여기에서 자세한 내용을 확인할 수 있습니다.하나,두과세.
추신주제가 여전히 실제적이고 간단한 코드 스 니펫이 부족한 것으로 나타났습니다.견적성능이 중요한 요구 사항이 아닌 경우 프로세스의 개인 메모리 사용 :
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;
위의 답변은 분명히 도움이 될 것입니다.(2 일간의 여유와 adb 메모리 도구 연구 후)내가 도와 줄 수 있다고 생각해.의견너무.
같이Hackbod는 말합니다 :따라서 실제로 각 프로세스에 매핑 된 실제 RAM을 모두 가져 와서 모든 프로세스를 추가하는 경우 실제로 총 RAM보다 훨씬 많은 수의 RAM이 생성됩니다. 따라서 프로세스 당 정확한 메모리 양을 확보 할 수있는 방법은 없습니다.
하지만 몇 가지 논리로 그걸 가까이에서 볼 수 있습니다.
다음과 같은 API가 있습니다.
android.os.Debug.MemoryInfo
과ActivityManager.getMemoryInfo()
위에서 이미 언급 했었지만 이미 읽었을 지 모르지만 나는 다른 방법으로 이야기 할 것입니다.
따라서 먼저 root 사용자가되어야 작동하게됩니다. 실행으로 루트 권한으로 콘솔에 들어가십시오.su
진행 중이며output and input stream
. 그 다음 패스id\n
(들어가다)ouputstream에서 출력을 출력하고, 입력 스트림을 포함하는 경우uid=0
,당신은 루트 사용자입니다.
이제 여기에 위의 과정에서 사용할 논리가 있습니다.
프로세스의 출력을 얻을 때명령 (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가 아닌 콘솔에서 단일 문자열로 가져옵니다. 복잡한 데이터는 수동으로 분리해야하므로 저장해야합니다..
이것은 단지 시도입니다, 뭔가를 놓친다면 제게 제안 해주세요.