プログラムでAndroidアプリケーションで使用されているメモリを見つけるにはどうすればよいですか?
それを行う方法があるといいのですが。さらに、どうすれば電話の空きメモリも取得できますか?
プログラムでAndroidアプリケーションで使用されているメモリを見つけるにはどうすればよいですか?
それを行う方法があるといいのですが。さらに、どうすれば電話の空きメモリも取得できますか?
回答:
Linuxなどの最新のオペレーティングシステムでのメモリ使用量は、非常に複雑で理解しにくい領域です。実際、実際にあなたが得た数字を正しく解釈する可能性は非常に低いです。(私が他のエンジニアとメモリ使用量の数値を見るときはいつも、それらが実際に何を意味するのかについての長い議論が常にあり、それは曖昧な結論をもたらすだけです。)
注:現在、アプリのメモリの管理に関するはるかに広範なドキュメントがあり、ここで資料の多くをカバーしており、Androidの状態についてはより最新です。
まず最初に、Androidでメモリがどのように管理されるかについての議論があるこの記事の最後の部分を読むことでしょう。
現在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;
しかし、違いは間にあるものにとPss
、PrivateDirty
とSharedDirty
...よく今の楽しみが始まります。
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 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接合体 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 バッファー:880 kB キャッシュ:84104 kB SwapCached:0 kB
MemTotal
カーネルとユーザースペースで使用可能なメモリの総量です(多くの場合、デバイスの実際の物理RAMよりも少なくなります。そのRAMの一部は無線、DMAバッファーなどに必要であるためです)。
MemFree
まったく使用されていないRAMの量です。ここに表示される数は非常に多いです。通常、Androidシステムでは、使用可能なメモリを使用してプロセスを実行し続けるため、これは数MBになります。
Cached
ファイルシステムのキャッシュなどに使用されるRAMです。通常のシステムでは、ページング状態が悪くなるのを防ぐために、20MB程度必要です。Androidのメモリ不足キラーは、特定のシステム用に調整されており、キャッシュされた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の最良の回答の1つです。非常にあいまいな被写体に光を当てます。それは私を大いに助けました。
もう1つの本当に役立つリソースは、この必見のビデオです。GoogleI / O 2011:Androidアプリのメモリ管理
更新:
ブログ投稿で説明されているメモリを管理する方法を発見するためのサービスであるプロセス統計:プロセス統計:アプリがRAMをどのように使用するかを理解するDianne Hackborn氏:
Android Studio 0.8.10以降では、メモリモニターと呼ばれる非常に便利なツールが導入されています。
それが良いこと:
- 使用可能なメモリと使用されたメモリをグラフで表示し、ガベージコレクションイベントを時系列で表示します。
- アプリの速度低下が過剰なガベージコレクションイベントに関連しているかどうかをすばやくテストする。
- アプリのクラッシュをすばやくテストすることは、メモリ不足に関連している可能性があります。
図1. Android Memory MonitorでのGC(ガベージコレクション)イベントの強制
これを使用することで、アプリの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の上ネクサス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メトリックを返します。あなたはここでそれについての詳細を見つけることができます:1、2と3を。
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;
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()
それらについては既に読んで使用しているかもしれませんが、私は別の方法で話します
したがって、最初に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();
}
}
APIからのインスタンスではなく、コンソールから単一の文字列で生データを取得します。これは、手動で分離する必要があるため、保存が複雑です。
これは単なる試用です。何かを見逃した場合は、私に提案してください