Androidアプリを開発しましたが、電話アプリ開発の段階にあり、すべてがうまく機能しているようで、勝利と出荷を宣言したいのですが、ただそれだけでいいのです。メモリとリソースのリークがそこにあります。 Androidにはわずか16MBのヒープしかなく、Androidアプリでリークするのは驚くほど簡単です。
私は周りを見回してきましたが、これまでのところ「hprof」と「traceview」に関する情報を掘り下げることができましたが、どちらも多くの好評を得ていません。
OSプロジェクトで共有したいツールや方法はありますか?
Androidアプリの開発で見つかった最も一般的なエラーの1つは、「Java.lang.OutOfMemoryError:ビットマップサイズがVM Budget」を超えていることです。向きを変更した後、多くのビットマップを使用するアクティビティでこのエラーを頻繁に発見しました。アクティビティは破棄され、再度作成され、ビットマップに使用可能なVMメモリを消費するXMLからレイアウトが「膨張」します。
前のアクティビティレイアウトのビットマップは、アクティビティへの相互参照を持っているため、ガベージコレクタによって適切に割り当て解除されません。多くの実験の後、私はこの問題の非常に良い解決策を見つけました。
最初に、XMLレイアウトの親ビューで「id」属性を設定します。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="fill_parent"
Android:layout_height="fill_parent"
Android:id="@+id/RootView"
>
...
次に、ActivityのonDestroy()メソッドで、参照を親Viewに渡してunbindDrawables()メソッドを呼び出し、System.gc()を実行します。
@Override
protected void onDestroy() {
super.onDestroy();
unbindDrawables(findViewById(R.id.RootView));
System.gc();
}
private void unbindDrawables(View view) {
if (view.getBackground() != null) {
view.getBackground().setCallback(null);
}
if (view instanceof ViewGroup) {
for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
unbindDrawables(((ViewGroup) view).getChildAt(i));
}
((ViewGroup) view).removeAllViews();
}
}
このunbindDrawables()メソッドは、ビューツリーを再帰的に探索し、次のことを行います。
Eclipse Memory Analyzerを入手( http://www.Eclipse.org/mat/ )チェック http://kohlerm.blogspot.com/2010/02/Android-memory-usage -analysis-slides.html および http://kohlerm.blogspot.com/search/label/memory
主に将来のGoogle旅行者向け:
ほとんどのJavaツールは、JVMヒープのみを分析するため、残念ながらこのタスクには適していません。ただし、すべてのAndroidアプリケーションにもネイティブヒープがあり、これも最大16 MBの制限内に収まる必要があります。通常、たとえばビットマップデータに使用されます。したがって、多くのドロウアブルを使用すると、JVMヒープが3 MB前後であっても、メモリ不足エラーに非常に簡単に遭遇する可能性があります。
@ hp.Androidからの回答は、ビットマップの背景を操作しているだけでうまく機能しますが、私の場合、BaseAdapter
にImageView
sのセットを提供するGridView
がありました。アドバイスとしてunbindDrawables()
メソッドを修正し、条件が次のようになるようにしました。
if (view instanceof ViewGroup && !(view instanceof AdapterView)) {
...
}
しかし、問題は再帰メソッドがAdapterView
の子を処理しないことです。これに対処するために、代わりに次のことを行いました。
if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) view;
for (int i = 0; i < viewGroup.getChildCount(); i++)
unbindDrawables(viewGroup.getChildAt(i));
if (!(view instanceof AdapterView))
viewGroup.removeAllViews();
}
AdapterView
の子がまだ処理されるように、メソッドはすべての子を削除しようとしません(サポートされていません)。
ただし、ImageView
sはバックグラウンドではないビットマップを管理するため、これで問題は解決しません。したがって、次を追加しました。それは理想的ではありませんが、動作します:
if (view instanceof ImageView) {
ImageView imageView = (ImageView) view;
imageView.setImageBitmap(null);
}
全体的にunbindDrawables()
メソッドは次のとおりです。
private void unbindDrawables(View view) {
if (view.getBackground() != null)
view.getBackground().setCallback(null);
if (view instanceof ImageView) {
ImageView imageView = (ImageView) view;
imageView.setImageBitmap(null);
} else if (view instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) view;
for (int i = 0; i < viewGroup.getChildCount(); i++)
unbindDrawables(viewGroup.getChildAt(i));
if (!(view instanceof AdapterView))
viewGroup.removeAllViews();
}
}
このようなリソースを解放するためのより原則的なアプローチがあることを望んでいます。
Androidのメモリ管理に関するGoogle I/Oトーク(2011年)、およびメモリプロファイリングのツール+テクニックの詳細:
http://www.youtube.com/watch?v=_CruQY55HOk
ValgrindはAndroid(Mozillaがスポンサー)に移植されました。 AndroidでのValgrind —現在のステータス および ARMでのAndroidのValgrindの実行のサポート (コメント67)を参照してください。
まあ、それらはAndroidが使用するユニークなフォーマットにフックするツールです。あなたが満足していないのは、使用中の基礎となるテストコードフレームワークです。
Android Mock Frameworkを使用してコードの領域を模擬テストしてみましたか?