web-dev-qa-db-ja.com

Androidで自分のアプリケーションのメモリ使用量を確認する方法

プログラムで自分のAndroidアプリケーションで使用されているメモリを確認する方法を教えてください。

やり方があるといいのですが。さらに、携帯電話の空きメモリもどうやって入手できますか。

773
Andrea Baccega

はい、あなたはプログラム的にメモリ情報を取得し、メモリ集約的な作業をするかどうかを決めることができます。

以下を呼び出してVM Heap Sizeを取得します。

Runtime.getRuntime().totalMemory();

以下を呼び出して、割り当てられたVMメモリを取得します。

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

以下を呼び出してVM Heap Size Limitを取得します。

Runtime.getRuntime().maxMemory()

以下を呼び出して、ネイティブ割り当てメモリを取得します。

Debug.getNativeHeapAllocatedSize();

OutOfMemoryErrorの動作を把握し、メモリ使用量を監視するためのアプリを作成しました。

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

ソースコードは で入手できます。https://github.com/coocood/oom-research

75
coocood

これは進行中の作業ですが、これは私が理解できないことです:

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配列だけを取るようにするのですか?

50
Ryan Beesley

HackbodはStack Overflowのベストアンサーの1つです。非常に不明瞭な被写体に光を当てます。とても助かりました。

もう1つの本当に役立つリソースは、この必見のビデオです。 Google I/O 2011:Androidアプリのメモリ管理


UPDATE:

Process Stats、ブログ投稿で説明されているアプリがメモリを管理する方法を発見するサービスプロセス統計:アプリがRAMを使用する方法を理解するby Dianne Hackborn:

24
Xavi Gil

Android Studio 0.8.10以降では、 Memory Monitor という非常に便利なツールが導入されました。

enter image description here

何が良いのか

  • 利用可能なメモリと使用済みのメモリをグラフで表示し、ガベージコレクションイベントを時間の経過とともに表示します。
  • アプリのスローが過度のガベージコレクションイベントに関連している可能性があるかどうかをすばやくテストします。
  • アプリのクラッシュがメモリ不足に関連しているかどうかをすばやくテストします。

enter image description here

図1. Android Memory MonitorでGC(ガベージコレクション)イベントを強制する

あなたはそれを使うことであなたのアプリのRAMリアルタイム消費についてたくさんの良い情報を持つことができます。

19
Machado

1)少なくともJavaからではないと思います。
2)

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

現在のプロセスの総メモリを取得する標準的な方法にはいくつか問題があることがわかりました。

  • Runtime.getRuntime().totalMemory():JVMメモリのみを返す
  • ActivityManager.getMemoryInfo()Process.getFreeMemory()、および/proc/meminfoに基づくその他のすべての情報は、結合されたすべてのプロセスに関するメモリ情報を返します(例: Android_util_Process.cpp
  • Debug.getNativeHeapAllocatedSize() - mallinfo()を使用します。これはmalloc()および関連する関数によって実行されるメモリ割り当てについての情報を返します( Android_os_Debug.cpp を参照)
  • Debug.getMemoryInfo() - 仕事はしますが遅すぎます。 1回の呼び出しで、 200ms on 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 metricを返します。あなたはそれに関するより多くの詳細をここで見つけることができます: onetwo および three


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;
4

Android Studio 3.0では、アプリがCPU、メモリ、ネットワーク、およびバッテリリソースをどのように使用しているかを理解するのに役立つAndroidプロファイラが導入されました。

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

enter image description here

0
Akash Patel

上記の多くの回答は間違いなくあなたを助けますが、(adbメモリツールに関する2日間の余裕と研究の後)私はopinionも。

Hackbodによると:したがって、実際に各プロセスにマップされたすべての物理RAMを取得し、すべてのプロセスを合計すると、おそらく、実際の合計RAMよりもはるかに大きい数になります。 したがって、プロセスごとに正確なメモリ量を取得する方法はありません。

しかし、あなたは何らかのロジックによってそれに近づくことができます..そして私はその方法を教えます..

上記のAndroid.os.Debug.MemoryInfoActivityManager.getMemoryInfo()のようなAPIがありますが、これらについてはすでに読んで使用しているかもしれませんが、他の方法についても説明します

そのため、最初にルートユーザーになる必要があります。プロセスでsuを実行してルート権限でコンソールに入り、そのoutput and input streamを取得します。次に、id\n(enter)をouputstreamに渡し、プロセス出力に書き込みます。Ifがuid=0を含む入力ストリームを取得しますrootユーザーです

次に、上記のプロセスで使用するロジックを示します

プロセスの出力ストリームを取得すると、idの代わりに\nを使用してコマンド(procrank、dumpsys meminfoなど)を渡し、そのinputstreamを取得します。読み取り、ストリームをbytes []、char []などに保存します。rawデータを使用します。

許可:

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

Rootユーザーかどうかを確認します。

// 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();
 }
}

logcat: result

APIからのインスタンスではなく、コンソールから単一の文字列で生データを取得します。これは、手動で分離する必要があるため保存が複雑です

これは試してみて、何か見逃した場合は教えてください

0
Iamat8