メモリリークの原因と解決策は何ですか

醉折花枝作酒筹
リリース: 2023-01-13 00:36:27
オリジナル
29703 人が閲覧しました

理由と解決策は次のとおりです: 1. 静的内部クラスを使用して、スレッドによるメモリ リークを回避します。 2. キャッシュされた ConvertView を使用してアダプタを構築し、ListView によるメモリ リークを回避します。 3. プログラムを終了する前に、コレクションコンテナーでのメモリリークを避けるために、コレクション内の内容は null に設定されます。

メモリリークの原因と解決策は何ですか

# このチュートリアルの動作環境: Windows 7 システム、Dell G3 コンピューター。

メモリ リークの一般的な原因

1. シングルトンによるメモリ リーク

シングルトンによる静的インスタンスの性質により、そのライフ サイクルはアプリケーションのライフ サイクルと同じ長さになります。オブジェクトが不要になり、シングルトン オブジェクトがまだオブジェクトへの参照を保持している場合、オブジェクトは正常にリサイクルできず、メモリ リークが発生します。 。

例: シングルトンによって引き起こされるメモリ リークのインスタンスを防止する

// 使用了单例模式
public class AppManager {
    private static AppManager instance;
    private Context context;
    private AppManager(Context context) {
        this.context = context;
    }
    public static AppManager getInstance(Context context) {
        if (instance != null) {
            instance = new AppManager(context);
        }
        return instance;
    }
}
ログイン後にコピー

2. 静的インスタンスを作成する非静的内部クラスによって引き起こされるメモリ リーク

Forたとえば、頻繁に開始されるアクティビティでは、同じデータ リソースの繰り返し作成を避けるために、次のような記述が表示される場合があります:

  public class MainActivity extends AppCompatActivity {

    private static TestResource mResource = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if(mResource == null){
            mResource = new TestResource();
        }
        //...
    }
    
    class TestResource {
    //...
    }
}
ログイン後にコピー

3. Handler によるメモリ リーク

例: 匿名内部クラスの静的オブジェクトを作成します

public class MainActivity extends AppCompatActivity {

    private final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            // ...
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread(new Runnable() {
            @Override
            public void run() {
                // ...
                handler.sendEmptyMessage(0x123);
            }
        });
    }
}
ログイン後にコピー

1. Android の観点から

Android アプリケーションが開始されると、アプリケーションのメイン スレッドは自動的にLooper オブジェクトとそれに関連付けられた MessageQueue。 Handler オブジェクトがメイン スレッドでインスタンス化されると、メイン スレッド Looper の MessageQueue に自動的に関連付けられます。 MessageQueue に送信されるすべてのメッセージはハンドラーへの参照を保持するため、ルーパーはそれに応じてハンドルの handleMessage() メソッドをコールバックしてメッセージを処理します。 MessageQueue に未処理のメッセージがある限り、ルーパーは引き続きメッセージを取り出し、処理のためにハンドラーに渡します。さらに、メインスレッドの Looper オブジェクトは、アプリケーションのライフサイクル全体に付随します。

2. Java の観点

Java では、非静的内部クラスと匿名内部クラスは、それらが属する外部クラスへの参照を保持する可能性がありますが、静的内部クラスは保持しません。

上記の例を分析してください。MainActivity が終了すると、未処理のメッセージはハンドラーへの参照を保持し、ハンドラーはそれが属する外部クラス (MainActivity) への参照を保持します。この参照関係はメッセージが処理されるまで残るため、ガベージ コレクターによる MainActivity のリサイクルが妨げられ、メモリ リークが発生します。

解決策: メモリ リークを避けるために、Handler クラスを分離するか、静的な内部クラスを使用します。

4. スレッドによるメモリ リーク

例: AsyncTask と Runnable

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread(new MyRunnable()).start();
        new MyAsyncTask(this).execute();
    }

    class MyAsyncTask extends AsyncTask<Void, Void, Void> {

        // ...

        public MyAsyncTask(Context context) {
            // ...
        }

        @Override
        protected Void doInBackground(Void... params) {
            // ...
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            // ...
        }
    }

    class MyRunnable implements Runnable {
        @Override
        public void run() {
            // ...
        }
    }
}
ログイン後にコピー

AsyncTask と Runnable はどちらも匿名の内部クラスを使用しており、Holds が発生します。それが存在するアクティビティへの暗黙的な参照。アクティビティが破棄される前にタスクが完了しない場合、アクティビティのメモリ リソースはリサイクルされず、メモリ リークが発生します。

解決策: AsyncTask クラスと Runnable クラスを分離するか、静的内部クラスを使用してメモリ リークを回避します。

5. リソースを閉じないことによるメモリ リーク

BroadcastReceiver、ContentObserver、File、Cursor、Stream、Bitmap などのリソースについては、破棄する必要があります。アクティビティが破棄されるまでに、閉じるかログアウトしてください。そうしないと、これらのリソースがリサイクルされず、メモリ リークが発生します。

1) たとえば、BraodcastReceiver はアクティビティに登録されていますが、アクティビティの終了後も BraodcastReceiver の登録は解除されません。

2) カーソル、ストリーム、ファイルなどのリソース オブジェクトは、バッファを使用することがよくあります。これらを使用していないときは、バッファがメモリを適時に再利用できるように、適時にそれらを閉じる必要があります。それらのバッファは Java 仮想マシン内に存在するだけでなく、Java 仮想マシンの外部にも存在します。参照を閉じずに単に null に設定すると、メモリ リークが頻繁に発生します。

3) リソース オブジェクトが使用されていない場合は、close() 関数を呼び出してオブジェクトを閉じ、null に設定する必要があります。プログラムの終了時にリソース オブジェクトが閉じられていることを確認する必要があります。

4) Bitmap オブジェクトが使用されなくなったら、recycle() を呼び出してメモリを解放します。 2.3 以降のビットマップは、メモリがすでに Java 層にあるため、手動でリサイクルする必要はありません。

6. ListView 使用時に発生するメモリ リーク

最初に、ListView は現在の画面レイアウトに従って BaseAdapter から一定数の View オブジェクトをインスタンス化し、ListView はこれらをキャッシュします。オブジェクトを表示します。 ListView を上にスクロールすると、最初に一番上にあった項目の View オブジェクトがリサイクルされ、下に表示される項目の構築に使用されます。この構築プロセスは getView() メソッドによって完了します。getView() の 2 番目の仮パラメータ ConvertView は、キャッシュされた項目の View オブジェクトです (初期化中にキャッシュに View オブジェクトがない場合、convertView は null です)。

アダプターを構築するとき、キャッシュされた ConvertView は使用されません。

解決策: アダプターを構築するときに、キャッシュされた ConvertView を使用します。

7. コレクションコンテナーでのメモリリーク

通常、いくつかのオブジェクト参照をコレクション コンテナ (ArrayList など) に追加しますが、オブジェクトが必要なくなっても、その参照をコレクションから削除しないため、コレクションはますます大きくなります。このコレクションが静的である場合、状況はさらに深刻になります。

解決策: プログラムを終了する前に、コレクション内の項目をクリアし、null に設定してからプログラムを終了します。

8. WebView によるリーク

WebView オブジェクトを使用しない場合は、その destroy() 関数を呼び出してオブジェクトを破棄し、占有しているメモリを解放する必要があります。そうしないと、長期間占有されていたメモリを再利用できず、メモリ リークが発生します。

解決策: WebView の別のプロセスを開き、AIDL を介してメイン スレッドと通信します。WebView が配置されているプロセスは、ビジネス ニーズに応じて適切な時間を選択して破棄できるため、完全なメモリ解放が実現します。

コンピュータ関連の知識について詳しくは、FAQ 列をご覧ください。

以上がメモリリークの原因と解決策は何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート