Androidでアプリケーションのメモリ使用量を確認するにはどうすればよいですか?


800

プログラムでAndroidアプリケーションで使用されているメモリを見つけるにはどうすればよいですか?

それを行う方法があるといいのですが。さらに、どうすれば電話の空きメモリも取得できますか?


2
または、これについてもっと知りたい場合は、http:

1
あなたのアプリのメモリ管理developer.android.com/topic/performance/memory
聖武天皇

回答:


1008

Linuxなどの最新のオペレーティングシステムでのメモリ使用量は、非常に複雑で理解しにくい領域です。実際、実際にあなたが得た数字を正しく解釈する可能性は非常に低いです。(私が他のエンジニアとメモリ使用量の数値を見るときはいつも、それらが実際に何を意味するのかについての長い議論が常にあり、それは曖昧な結論をもたらすだけです。)

注:現在、アプリのメモリの管理に関するはるかに広範なドキュメントがあり、ここで資料の多くをカバーしており、Androidの状態についてはより最新です。

まず最初に、Androidでメモリがどのように管理されるかについての議論があるこの記事の最後の部分を読むことでしょう。

Android 2.0以降のサービスAPIの変更

現在ActivityManager.getMemoryInfo()、全体的なメモリ使用量を確認するための最高レベルのAPIです。これは主に、アプリケーションがシステムがバックグラウンドプロセス用のメモリがなくなって、サービスなどの必要なプロセスの強制終了を開始する必要があるかどうかを判断するのに役立ちます。純粋なJavaアプリケーションの場合、Javaヒープの制限は、1つのアプリがシステムにこの時点までストレスをかけることを回避するためにある程度存在するため、ほとんど役に立ちません。

下位レベルに進むと、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;

しかし、違いは間にあるものにとPssPrivateDirtySharedDirty...よく今の楽しみが始まります。

Android(およびLinuxシステム全般)の多くのメモリは、実際には複数のプロセス間で共有されます。したがって、プロセスが使用するメモリの量は実際には明確ではありません。ディスクへのページングアウトに加えて(Androidでは使用しないスワップはもちろんのこと)、さらに不明確になります。

したがって、実際にすべての物理RAMを各プロセスにマップし、すべてのプロセスを合計すると、実際のRAMの合計よりもはるかに多くなる可能性があります。

このPss数は、カーネルがメモリ共有を考慮に入れて計算するメトリックです。基本的に、プロセス内のRAMの各ページは、そのページを使用している他のプロセスの数の比率によってスケーリングされます。このようにして、(理論的には)すべてのプロセスのpssを合計して、それらが使用しているRAMの合計を確認し、プロセス間のpssを比較して、相対的な重みの概算を得ることができます。

ここでのもう1つの興味深いメトリックはですPrivateDirty。これは基本的に、ディスクにページングできないプロセス内のRAMの量であり(ディスク上の同じデータによってバッキングされません)、他のプロセスと共有されません。これを確認するもう1つの方法は、そのプロセスがなくなるとシステムで使用できるようになるRAMです(おそらく、すぐにキャッシュやその他の用途に組み込まれます)。

これがSDK APIです。ただし、デバイスを使用して開発者としてできることは他にもあります。

を使用adbすると、実行中のシステムのメモリ使用量について多くの情報を得ることができます。一般的なものは、adb shell dumpsys meminfo上記の情報やその他のさまざまな情報を含む、各Javaプロセスのメモリ使用量に関する一連の情報を吐き出すコマンドです。単一のプロセスの名前またはPIDを確認して確認することもできます。たとえばadb shell dumpsys meminfo system、システムプロセスを教えてください。

** pid 890のMEMINFO [システム] **
                    ネイティブダルビクその他合計
            サイズ:10940 7047 N / A 17987
       割り当て済み:8943 5516 N / A 14459
            無料:336 1531 N / A 1867
           (ps):4585 9282 11916 25783
  (共有ダーティ):2184 3596 916 6696
    (priv dirty):4504 5956 7456 17916

 オブジェクト
           ビュー:149ビュールート: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 psspriv 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接合体
  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使用量であり、プロセス間でRAM使用量を合計すると、途方もなく大きな数が得られます)。

Pss私たちは前に見てきたようで、とUssありますPriv Dirty

ここで注目すべき興味深い点は、で見たものPssUssは少し(または少しだけ)異なる点ですmeminfo。何故ですか?まあprocrankは、データを収集するために異なるカーネルメカニズムを使用しており、meminfo結果はわずかに異なります。何故ですか?正直なところ、私には手掛かりがありません。私procrankはより正確な方だと思います...しかし、実際には、これはポイントを残します。

最後にadb shell cat /proc/meminfo、システムの全体的なメモリ使用量の概要を示すコマンドがあります。ここには多くのデータがありますが、議論する価値がある最初のいくつかの数(および残りの数は少数の人々によって理解され、それらについての私の質問についての私の質問はしばしば矛盾する説明をもたらします):

MemTotal:395144 kB
MemFree:184936 kB
バッファー:880 kB
キャッシュ:84104 kB
SwapCached:0 kB

MemTotal カーネルとユーザースペースで使用可能なメモリの総量です(多くの場合、デバイスの実際の物理RAMよりも少なくなります。そのRAMの一部は無線、DMAバッファーなどに必要であるためです)。

MemFreeまったく使用されていないRAMの量です。ここに表示される数は非常に多いです。通常、Androidシステムでは、使用可能なメモリを使用してプロセスを実行し続けるため、これは数MBになります。

Cachedファイルシステムのキャッシュなどに使用されるRAMです。通常のシステムでは、ページング状態が悪くなるのを防ぐために、20MB程度必要です。Androidのメモリ不足キラーは、特定のシステム用に調整されており、キャッシュされたRAMが過度に消費されてページングが発生する前に、バックグラウンドプロセスが強制終了されるようにします。


1
見ていpixelbeat.org/scripts/ps_mem.pyプログラムに使用するRAMを示すために、上記の技術を使用しています
pixelbeat

17
書いてとてもいい!私は、メモリ管理とさまざまなツールの使用に関する投稿を書いて、ここでmacgyverdev.blogspot.com/2011/11/にヒープの使用状況を調べています。
JohanNorén11年

3
2つの列「dalvik」は「ネイティブ」とは正確には何ですか?
dacongy 2012

1
「ネイティブ」「ダルビック」「その他」とは正確には何ですか?私のアプリでは、「その他」が非常に大きいですか?どうすれば削減できますか?
着陸

。私は、 『:procrank:/システム/ binに/ shが見つからない』を教えて「adbのシェルはprocrank」「adbのシェルdumpsys meminfoの」を使用することができますが、私は持っていませclue.Wishあなたは私を助けることができる
ヒューゴ

79

はい、プログラムでメモリ情報を取得し、メモリを集中的に使用するかどうかを決定できます。

以下を呼び出して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で入手でき ます。


7
このランタイムは、現在のプロセスまたはシステム全体のヒープによるメモリ使用量を返しますか?
Mahendran 2014年

1
totalMemory()メソッドのJavaDocからの@mahemadhi「実行中のプログラムで使用できるメモリの総量を返します」
Alex

これは質問に対する正しい答えではありません。答えは特定のアプリケーションについてではありません。
アミールRezazadeh 2018

52

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

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


2
それらは、入力配列と同じ順序である可能性があります。
taer

2
それは物事を行うための非常に非直感的な方法のようです。はい、それはおそらくそうです、しかし、それはどのようにしてOOPですか?
Ryan Beesley、2010

6
APIは、使いやすさやシンプルさではなく、効率性を重視して設計されています。これはアプリの99%が触れるべきものではないため、効率が最も重要な設計目標です。
10

2
けっこうだ。作成している1つ以上のアプリケーションのメモリ使用量を追跡する内部ツールを作成しようとしています。結果として、他のプロセスへの影響を最小限に抑えながら、可能な限り詳細な結果(後処理)を維持しながら、この監視を行う方法を探しています。各.getProcessMemoryInfo呼び出しにある程度のオーバーヘッドがあると仮定すると、プロセスを繰り返し処理してから、各プロセスの呼び出しを行うのは効率が悪いようです。返された配列が呼び出しと同じ順序であることが保証されている場合は、結果を盲目的に処理し、パリティを仮定します。
ライアンビーズリー2010

5
これは軽微な問題ですが、Logの場合、処理されるラインフィードを追加する必要はありません。
ThomasW

24

HackbodはStack Overflowの最良の回答の1つです。非常にあいまいな被写体に光を当てます。それは私を大いに助けました。

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


更新:

ブログ投稿で説明されているメモリを管理する方法を発見するためのサービスであるプロセス統計:プロセス統計:アプリがRAMをどのように使用するかを理解するDianne Hackborn氏:


19

Android Studio 0.8.10以降では、メモリモニターと呼ばれる非常に便利なツールが導入されています。

ここに画像の説明を入力してください

それが良いこと:

  • 使用可能なメモリと使用されたメモリをグラフで表示し、ガベージコレクションイベントを時系列で表示します。
  • アプリの速度低下が過剰なガベージコレクションイベントに関連しているかどうかをすばやくテストする。
  • アプリのクラッシュをすばやくテストすることは、メモリ不足に関連している可能性があります。

ここに画像の説明を入力してください

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

これを使用することで、アプリのRAMリアルタイム消費に関する十分な情報を得ることができます。


16

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

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

1
(ActivityManager activityManager = =(ActivityManager)getSystemService(ACTIVITY_SERVICE);)の代わりに(ActivityManager activityManager =(ActivityManager)getSystemService(ACTIVITY_SERVICE);)に変更します
Rajkamal

7

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

  • 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のネクサス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メトリックを返します。あなたはここでそれについての詳細を見つけることができます:123を


PSテーマには、パフォーマンスが重要な要件ではない場合に、プロセスのプライベートメモリ使用量を推定する方法の実際の単純なコードスニペットがまだ不足していることに気付きました。

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;


1

上記の答えはたくさんありますが、間違いなくあなたを助けますが、(2日間の余裕とadbメモリツールの研究の後)私も私の意見を参考にできると思います。

以下のよう Hackbodは言う:あなたが実際に各プロセスにマッピングされた中での物理RAMのすべてを取り、すべてのプロセスを追加した場合このように、あなたはおそらく、実際の総RAMよりもはるかに大きい数で終わるでしょう。 したがって、プロセスごとに正確なメモリ量を取得する方法はありません。

しかし、あなたはいくつかの論理によってそれに近づくことができます。そして、私は方法を教えます。

上記のようなAPIがいくつかありますがandroid.os.Debug.MemoryInfoActivityManager.getMemoryInfo()それらについては既に読んで使用しているかもしれませんが、私は別の方法で話します

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

これが、上記のプロセスで使用するロジックです

あなたは、プロセスのouputstream取得する場合(procrank、dumpsysは、meminfoなど)で使用すると、コマンド渡す\n代わりに、IDのとそのを取得inputstream]など。使用[、] [バイトストリームを格納し、文字を読み data..andあなたを完了しました!!!!!

許可:

<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: 結果

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

これは単なる試用です。何かを見逃した場合は、私に提案してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.