756

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

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


8 답변


982

Linuxなどの最近のオペレーティングシステムでのメモリ使用量は極めて複雑でわかりにくい地域です。実際、あなたが実際にあなたが得るどんな数字でも正しく解釈する可能性は非常に低いです。 (私が他のエンジニアとメモリ使用量の数値を比較するたびに、それらが実際に何を意味するのかについての長い議論が常にありますが、それはあいまいな結論にしかならないということです。)

注:私たちは今、より広範なドキュメントを手に入れました。アプリのメモリを管理するこれはここでの資料の大部分をカバーしており、Androidの状態に関してより最新のものです。

最初にすることは、おそらくこの記事の最後の部分を読んで、Android上でメモリがどのように管理されるかについていくらか議論することです。

Android 2.0からサービスAPIの変更点

ActivityManager.getMemoryInfo()全体的なメモリ使用量を調べるための最高レベルのAPIです。これは、アプリケーションがバックグラウンドプロセス用のメモリをこれ以上使用できなくなっていることをアプリケーションが判断するのに役立ちます。したがって、サービスのように必要なプロセスを強制終了する必要があります。純粋なJavaアプリケーションの場合、これはあまり役に立ちません。Javaヒープの制限は、1つのアプリケーションがシステムにこの時点までストレスをかけることができないようにするためです。

より低いレベルでは、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;

違いは何ですか?PssPrivateDirty、そしてSharedDirty...さて、楽しみが始まります。

Android(そして一般にLinuxシステム)の多くのメモリは実際には複数のプロセスで共有されています。したがって、プロセスがどれだけのメモリを使用するのかは、実際には明らかではありません。ディスクにページアウトすることに加えて(私たちがAndroid上で使用しないスワップはもちろんのこと)、それはそれほど明確ではありません。

したがって、実際に各プロセスにマップされているすべての物理RAMを使用して、すべてのプロセスを合計すると、実際の合計RAMよりはるかに大きい数になる可能性があります。

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

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

これはほとんどSDKのAPIです。しかし、あなたのデバイスの開発者としてできることは他にもあります。

を使うadb実行中のシステムのメモリ使用量についてあなたが得ることができるたくさんの情報があります。一般的なものはコマンドです。adb shell dumpsys meminfoこれは各Javaプロセスのメモリ使用量に関するたくさんの情報を吐き出すでしょう。また、単一のプロセスの名前または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追加の割り当てのためにヒープに残っている残りのkb空き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使用量です。プロセス間での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より少なくなります。そのRAMの一部は無線、DMAバッファなどに必要です)。

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

Cachedファイルシステムのキャッシュなどに使用されているRAMです。典型的なシステムはこれが悪いページング状態に入らないようにするために20MBかそこらが必要になるでしょう。 Androidのメモリ不足キラーは特定のシステム向けに調整されており、キャッシュされたRAMが消費されてページングが発生する前にバックグラウンドプロセスが強制終了されるようになっています。


  • 見てpixelbeat.org/scripts/ps_mem.pyプログラムに使用されているRAMを表示するために、上記のテクニックを使用します。 - pixelbeat
  • とても素敵に書かれています!ここでは、ヒープの使用状況を調べるためのメモリ管理とさまざまなツールの使用についての投稿を書きました。macgyverdev.blogspot.com/2011/11/…誰かがそれを便利だと思ったら。 - Johan Norén
  • 「dalvik」という2つの列は正確には何ですか。 「ネイティブ」 - dacongy
  • 「ネイティブ」とは" dalvik" "その他"私のアプリでは、 "other";非常に巨大ですか?どうすればそれを減らすことができますか? - landry
  • 「adb shell dumpsys meminfo」を使用できますが、「adb shell procrank」で「/ system / bin / sh:procrank:not found」と表示されます。私は手がかりを持っていません。あなたが私を助けてくれることを願っています。 - Hugo

72

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

以下を呼び出して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


  • このランタイムは、現在のプロセスまたは全体のシステムヒープによるメモリ使用量を返しますか? - mahe madhi
  • totalMemory()メソッドのJavaDocから@mahemadhi"実行中のプログラムで使用可能なメモリの合計量を返します" - Alex

49

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

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


  • これらはおそらく入力配列と同じ順序になります。 - taer
  • それは物事を行うための非常に直感的ではない方法のようです。はい、それはおそらくそうですが、どのようにOOPですか。 - Ryan Beesley
  • APIは、使いやすさや単純さではなく、効率のために設計されました。これは、99%のアプリが触れるべきものではないため、効率が最も重要な設計目標です。 - hackbod
  • けっこうだ。作成中の1つ以上のアプリケーションのメモリ使用量を追跡するための内部ツールを作成しようとしています。そのため、他のプロセスへの影響を最小限に抑えながらこのモニタリングを実行する方法を模索していますが、それでも可能な限り詳細な結果を得られるようにしています(後処理)。 .getProcessMemoryInfo呼び出しごとにいくらかのオーバーヘッドがあると仮定すると、プロセスを繰り返し処理してから各プロセスを呼び出すことは非効率的です。返された配列が呼び出しと同じ順序であることが保証されている場合は、結果を盲目的に処理し、パリティを仮定します。 - Ryan Beesley
  • これは軽微な問題ですが、Logでは、改行を追加する必要はありません。これは自動的に処理されます。 - ThomasW

24

Hackbodは、Stack Overflowに関する最も優れた回答の1つです。あいまいな被写体に光を当てます。とても助かりました。

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


更新:

Process Stats、アプリがどのようにメモリを管理しているかを発見するサービス、ブログ投稿で説明されていますプロセス統計:アプリケーションがRAMをどのように使用しているかを理解するDianne Hackborn著:


19

Android Studio 0.8.10以降では、非常に便利なツールが導入されました。メモリモニタ

enter image description here

何が良いのか

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

enter image description here

図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);


  • (ActivityManager activityManager =(ActivityManager)getSystemService(ACTIVITY_SERVICE);)ではなく(ActivityManager activityManager =(ActivityManager)getSystemService(ACTIVITY_SERVICE);)に変更します。 - Rajkamal

3

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

  • 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ネクサス61回の通話でパフォーマンスのオーバーヘッドは、この関数を定期的に呼び出すのでこの関数を役に立たなくし、すべての呼び出しが非常に目立つようになります(参照)。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メトリック。あなたはそれに関するより多くの詳細をここで見つけることができます:1そして


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;


0

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

としてハックボッドは言う:したがって、実際に各プロセスにマップされているすべての物理RAMを使用して、すべてのプロセスを合計すると、実際の合計RAMよりはるかに大きい数になる可能性があります。 そのため、プロセスごとに正確な量のメモリを確保することはできません。

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

のようないくつかのAPIがありますandroid.os.Debug.MemoryInfoそしてActivityManager.getMemoryInfo()あなたはすでに読んで使ったことがあるかもしれませんが、私は他の方法についてお話します

だから最初にあなたはそれを動作させるためにrootユーザーになる必要があります。実行することでroot権限でコンソールに入りますsu処理中ですoutput and input stream。それから渡すid\n (入る)出力ストリームに入力し、それをプロセス出力に書き込むと、uid=0あなたはrootユーザーです。

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

プロセスのアウトストリームが出たときあなたにコマンド(procrank、dumpsys meminfoなど)を渡します。\nidの代わりにそしてそれを入手inputstreamそして読み込み、ストリームをbytes []、char []などに格納します。データ..そして、あなたはやった!!!!!

許可:

<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からのインスタンスではなく、コンソールから1つの文字列で生データを取得します。手動で分離する必要があるため、格納が複雑です。

これはただの試みです、私が何かを逃したならば私に提案してください

リンクされた質問


関連する質問

最近の質問