2012-12-04 13 views
7

私はAndroid/Java(通常はPHPとJavaScriptで動作します)の一種です。私は、参照が間違った方法で使用されているときに、メモリリークの問題についての記事をいくつか読んだので、他の人々の仕事でよく見たことがある質問があります。参考資料を含むプロパティ良い/悪い

多くの人が、ビューのようなものに複数の方法でアクセスする必要がある場合、アクティビティの作成中に割り当てられたプロパティにこれを参照します。私が読んだこと(または私が読んだことを少なくとも理解している)から、これはメモリリークのコースの1つですか?

オブジェクトにIDを割り当てて、各メソッドでIDを検索する方がよいでしょうか?もしそうなら、動的に作成されたオブジェクトはどうですか?

答えて

0

Androidで参照されているリークの問題は、Contextオブジェクトへの参照を保持しているものへの参照が、ではなく、がUIに関連付けられている場合に発生します。あるアクティビティがシステム内の何ものによっても(onDestroy()の後に)参照されなくなり、ガベージコレクションルートからの参照がなくなるため(グローバル変数のように)、そのアクティビティ内のビューへの参照を保持します)、それはガベージコレクションの対象です。言い換えれば、アクティビティとそのビューとの間に設定された循環参照は、もはや一度も問題になりません。elseはそのアクティビティへの参照を保持します。

問題が発生するのは、アクティビティへの参照であり、そのアクティビティ自体は、で、Contextです。UIおよびアクティビティのライフサイクルコールの外にあります。登録解除するのを忘れた場所リスナーなどのようなもの。これはその参照を保持し、ツリー全体をガベージコレクションの対象にしません。したがって、大きな漏れ。

2

正しく使用すると良いですが、正しく使用しないと悪くなります。

アクティビティ外のものを別のクラスに渡し、そのクラスのライフタイムがアクティビティのライフタイムよりも長い場合にのみ、リークします。 Androidはフォアグラウンドアクティビティではなくなったアクティビティを破棄し、アクティビティ外のものがリファレンスを保持している場合、ガーベッジコレクタはメモリをヒープに戻すことができません。

特に、アクティビティのコンテキスト、スタティック、シングルトンには注意してください。

アクティビティ内のビューへの参照を保持することは、絶対に正常です。

悪い(疑似コード)の例を以下に示します。

public class MyApplication extends Application{ 
    public static ImageView activityBackgroundImageView; 
} 

public class MyActivity extends Activity{ 

    ImageView iv = findViewById(R.id.myImageView); 
    myApplication.activityBackgroundImageView= iv; // <==== LEAK 

} 

実際には、リークはありません。myActivityが終了()または破棄されたときにのみリークします。

各オブジェクトには参照カウントがあります。オブジェクトへの参照を保持するオブジェクトの数です。アクティビティでImageViewへの参照を設定すると、参照カウントは1になります。その参照をApplicationクラスにコピーします。 NB。 Javaのすべてが値渡しされるので、参照の値、正確には値のコピー、つまり同じオブジェクトへの新しい参照を渡します。 ImageViewの参照カウントが2になりました。

いつかあなたのアクティビティを終了し、参照カウントを減らします。今は1つです。ガベージコレクタは、参照カウントがゼロでないためImageViewオブジェクトを解放できません。

もちろん、アプリケーションで参照を無効にすることで修正できますが、スパゲッティコードを使用できます。

+1

画像への静的参照は、自らが何らかの形でアクティビティに明らかに束縛されているため、特に問題となります。したがって、ビットマップオブジェクトへの静的な参照は、アクティビティが決して解放されないことを意味します。 – Emile

+0

うん、誰か混乱している人のために私の元の答えは静的なビットマップを使っていましたが、ImageViewを参照するだけで後でインスタンス化したアクティビティのコンテキストを参照しています。 – Simon

0

あなたのメモリリークの原因に関するあなたの見解はあまり正しくありません。

ビュー要素への参照の格納は、これを行う方法とメモリリークの原因となる可能性があります。たとえば、静的参照を使用しないでください。たとえば、ビットマップイメージを静的に参照すると、誤ってガベージコレクションの問題が発生する可能性があります。

次のようにしてください。

class{ 
private TextView myTextView; 

onCreate() 
myTextView = findViewById(R.id.mytextview); 

myMethod() 
myTextView.text = "hello view." 

} 

MyMethodはの便宜のために純粋に既存の参照を使用して、あなたが参照の多くを持っていた場合、本当に読めないコードのためになるだろうしかし

findViewById(R.id.mytextview).text = "hello view"; 

を入れてからあなたを止めるものは何もありません。したがって、ローカルスコープ変数を使用する可能性があります。

myMethod() 
TextView myTextView = findViewById(R.id.mytextview); 
myTextView.text = "Hello" 
..... 

あなたの個人的な好みに応じて、必ずしもメモリリークが発生することはありません。

ここで問題となるのは、findViewByIdは集中的なプロシージャなので、実際に繰り返し呼び出すことは望ましくありません。リストビューは特にこの傾向があり、これに対応していないと大幅に減速します。

したがって、リストビューでは、人々がviewHolderパターンを実装していることがわかります。ビューの子要素への参照を割り当てる小さなオブジェクトです。このオブジェクトは親ビューのTagプロパティに割り当てられます。 ViewタグのプロパティにviewHolderがあるかどうかを確認するためにテストするビューの後続の呼び出しでは、ビューの内容を更新する必要があるたびにfindViewByIdを呼び出す時間と労力を節約する子オブジェクトへの参照があります。

非常に大まかなアイデアは、わずかに異なります。 viewHolder = new ViewHolder(); viewHolder.myTextField = findViewById(R.id.mytextview); myView.setTag(viewHolder) .... (viewHolder) viewHolder.text = "こんにちは" あなただけのリストビューでこれを使用することになり

注意ください。私はそれを一般的な経験則として使っていません。

リストビューアダプタの効率的なビューホルダーパターンをルックアップします。

0

回答をいただきありがとうございます。人々に問題を引き起こすようなアプリを作成する前に確認してください。

安全対策として、onStopとonRestartを追加してリファレンスを削除して再作成しました。これは、静的なプロパティで参照を使用することはありませんが、この種のものは何も存在しないようにする必要があります。

関連する問題