畜生!GCガベージコレクションメカニズムはとてもシンプルです!あなたはまだこれを理解していません、私はそこにいました!このコンピューター画面を食べるだけ

序文

Android開発者として、上級レベルに進みたい場合、メモリ管理は避けられないリンクであることが多く、メモリ管理垃圾回收 以下简称GC(Garbage Collection)の最も重要な部分としてのメカニズムは、私たちが習得しなければならないものです。今日は、次のガベージコレクションリサイクル戦略の世代別理解のために共有します

目次


  • 1.背景

  • 2.2つのリサイクルメカニズム

    • 2.1。参照カウント
    • 2.2。アクセシビリティ分析
  • 3.回復アルゴリズム

    • 3.1。マーク除去アルゴリズム
    • 3.2。コピーアルゴリズム
    • 3.3。マーカー圧縮アルゴリズム
  • 4.世代別リサイクル戦略

    • 4.1. 新生代
    • 4.2。旧世代
  • 5.4つの主要な引用


1.背景

一般的に、プログラミングの過程で、データをメモリに書き込み続けます。これらのデータは、使い果たされたときにメモリからクリアする必要があります。そうしないと、トリガーされるOutOfMemory(内存溢出)ため、すべてのプログラマーはこの原則に従う必要があります。听说(我也不懂C语言~)在C语言阶段,ガベージはプログラマーが手動で収集する必要があります。JVMが存在するため、Javaerは比較的満足してGC机制います。つまり、JVMはガベージを自動的にクリーンアップするのに役立ちますが、常にある程度のガベージが存在するため、幸福には代償が伴います。GC算法この現象も内存泄漏誤ってオブジェクトを回避することと呼ばれるため、GCメカニズムを習得することによってのみ、メモリをリークするプログラムの作成を回避できます。

2.2つのリサイクルメカニズム

2.1参照カウント

参照カウントとは何ですか?比喩A a = new A()、符号A参照オブジェクトがされて保持され、この時間参照カウントが+1場合、に設定参考ヌル即ち、対象とするAの参照カウントをに変更される0、アルゴリズムは、GC検出Aのアンオブジェクトの参照カウントを0はリサイクルされます。非常に単純ですが、参照カウントにはいくつかの欠点がありますa = null

シナリオは次のとおりです。

A a = new A();
B b = new B();
a.next = b;
b.next = a;
a = null;
b = null;

上記のコードが実行された後、オブジェクトABはリサイクルされますか?参照がnull設定されているように見えますが、実際にabの次はそれぞれ相互の参照を保持し、相互に参照を保持する状況を形成し、ABがゴミオブジェクトになり、リサイクルできなくなります。一部の学生は、メモリリークがあまりにも簡単に確認することです、と言うこと、するだけでは十分ではない、それぞれの設定次を空にする前に、そしてbは空です。そうですね、それは正しいのですが、実際のビジネスで巨大なビジネスロジックメモリリークが一目でわかるのは難しいです。そのため、JVMは後で参照カウントを放棄し、到達可能性分析を採用しました。

2.2アクセシビリティ分析

到達可能性解析は、実際に数学の概念である。JVMでは、いくつかの特別な言及がされているとみなさようGcRootおよびアクセスできるオブジェクトを通じてGcRootがゴミオブジェクトと見なされることはありません。つまり、オブジェクトがGcRootによって直接的または間接的に保持されている場合、そのオブジェクトはガベージオブジェクトとして扱われません。写真を使用して、次のようになっていることを示します。

写真A、B、C、D可GcRoot訪問しているため、リサイクルされません。GcRootはアクセスE、Fできないため、ガベージオブジェクトとしてマークされます。最も一般的なのは、相互に参照していても、GcRootからアクセスできないため、ガベージオブジェクトとしてマークされることです。要約すると到達可能性分析、問題のオブジェクトの参照カウントを解決できます。相互に参照することはリサイクルできません。G、H

GcRootとして使用できる参照の種類は何ですか?次のように大まかに4つのタイプがあります。

  • スタック内のローカル変数
  • メソッド領域の静的変数
  • メソッド領域の定数
  • ローカルメソッドスタックJNIの参照オブジェクト

注意点

参照とオブジェクトの2つの概念を混同しないでください。オブジェクトは実際にはメモリに存在し、参照は単なる変数/定数であり、オブジェクトのアドレスをメモリに保持します。

いくつかのコードでいくつかのGcRootを確認しましょう

ローカル変数

作者はデバッグにAndroidコードを使用しています。Androidを理解していない人はAndroidコードをメソッドと見なしてonCreatemainます。

public class MyApp extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        method();
    }

    private void method(){
        Log.i("test","method start");
        A a = new A();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Log.i("test","method end");
    }

    class A{
        @Override
        protected void finalize() throws Throwable {
            Log.i("test","finalize A");
        }
    }
}

促す

  • Javaでは、オブジェクトがリサイクルされ、そのfinalizeメソッドが呼び出されます
  • JVMでのガベージコレクションは、別のスレッドで実行されます。検証効果を高めるには、ここに2000ミリ秒の遅延を追加します

印刷結果は次のとおりです。

17:58:57.526   method start
17:58:59.526   method end
17:58:59.591   finalize A

メソッドメソッド実行時間は2000ミリ秒で、オブジェクトAメソッドの終了直後リサイクルされます。したがって、スタック内のローカル変数をGcRootとして使用できると想定できます。

ローカルメソッド領域の静的変数

public class MyApp extends Application {

    private static A a;
    @Override
    public void onCreate() {
        super.onCreate();
        Log.i("test","onCreate");
        a = new A();
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        a = null;
        Log.i("test","a = null");
    }
}

印刷結果は次のとおりです。

18:12:35.988   a = new A()
18:12:38.028   a = null
18:12:38.096   finalize A

Aオブジェクトを作成し、それを静的変数a割り当て、2000ミリ秒後に静的変数aをnull設定しますログから静的変数a空になった直後にオブジェクトAがリサイクルされることわかります。したがって、静的変数はGcRootとして使用できると見なすことができます。

方法区常量与静态变量验证过程完全一致,关于native 验证过程比较复杂,感兴趣的同学可自行验证。

メンバー変数がGcRootとして使用できることを確認します

public class MyApp extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        A a = new A();
        B b = new B();
        a.b = b;
        a = null;
    }

    class A{
        B b;
        @Override
        protected void finalize() throws Throwable {
            Log.i("test","finalize A");
        }
    }

    class B{
        @Override
        protected void finalize() throws Throwable {
            Log.i("test","finalize B");
        }
    }
}

印刷結果は次のとおりです。

13:14:58.999   finalize A
13:14:58.999   finalize B

ログから、オブジェクトAとオブジェクトBの両方がリサイクルされていることがわかります。Bのオブジェクトがされて保持されていることにより、Bの基準Aのオブジェクト、メンバ変数はとして使用することができないGcRootので、Bのオブジェクトが到達不能であるとゴミとして扱われます。

3.回復アルゴリズム

前の要約ではGCメカニズムについて説明しましたが、具体的な実装はアルゴリズムによって異なります。以下では、いくつかの一般的なGCアルゴリズムについて簡単に説明します

3.1。マーク除去アルゴリズム

すべてのGcRootを取得し、メモリ内のすべてのオブジェクトトラバースします。GcRootである可能性がある場合は、マークを追加すると、残りのすべてのオブジェクトがガベージとして扱われ、クリアされます。

  • 利点:実装が簡単で、実行効率が高い
  • 短所:連続したメモリの大きなブロックを適用する必要がある場合、メモリの断片化(使用可能なメモリの分散分散)が発生しやすく、GCが頻繁にトリガーされる可能性があります

3.2。コピーアルゴリズム

メモリを2つのブロックに分割し、毎回1つのブロックのみを使用します。まず、すべてのオブジェクトをトラバースし、使用可能なオブジェクトを別のメモリブロックにコピーします。この時点で、前のメモリブロックはすべてガベージと見なすことができます。クリーニング後、新しいメモリブロックは現在使用可能に設定されます。繰り返します

  • 利点:メモリの断片化の問題を解決します
  • 短所:メモリを順番に割り当てる必要があり、使用可能なメモリが元のメモリの半分になります。

3.3。マーカー圧縮アルゴリズム

すべてのGcRootを取得します。GcRootは、メモリ内のすべてのオブジェクトをトラバースし、使用可能なオブジェクトをもう一方の端に圧縮してから、ガベージオブジェクトをクリアすることから始めます。実際、時間の複雑さは、スペースの複雑さを減らすために犠牲にされています

  • 利点:マークを解決してメモリの断片化をクリアするために、アルゴリズムのメモリブロックをコピーする必要がありません
  • 短所:オブジェクトを移動する必要があり、実行効率がわずかに低下します。

4.世代別リサイクル戦略

JVMガベージコレクターでは、オブジェクトがより長く存続する場合、ガベージコレクター作成/回復の重複を回避するために非常にビジーです。犠牲にできないさらなる負担ポイントメモリはそれをキャッシュしますか?答えはイエスです。JVMは、各オブジェクトのライフサイクルを設定する世代別収集ポリシーを開発しました。ヒープメモリは、オブジェクトのライフサイクルを格納するために、それぞれ異なる領域に分割されます。ライフサイクルの一般的なオブジェクトには、新世代、旧時代、永続世代(java 8は廃止)があります。

4.1. 新生代

まず、新世代のメモリ構造の回路図を見てください。

8:1:1によると、新世代のメモリはEden、SurvivorA、SurvivorBに分けられます

新世代のメモリワークフロー:

  • オブジェクトがちょうど作成されると、それが中に配置されますエデンのとき。エリアエデンの面積は約いっぱいであるために、ガベージコレクションが実行され、現在生き残ったオブジェクトがコピーされているSurvivorA、そしてエデンされ、その後空に
  • ときエデンは、次の時間がいっぱいになる、それは別のガベージコレクションを行います。生き残ったオブジェクトはにコピーされSurvivorB、その後のすべてのオブジェクトエデンSurvivorAがリサイクルされています。
  • Edenが再びいっぱいになったら、ガベージコレクションを再度実行し、残っているオブジェクトをSurvivorAにコピーしてからEdenオブジェクトSurvivorBオブジェクトをリサイクルします。これを約15繰り返し、まだ生きているものを老後のエリアに置きます。

新世代のワークフローとレプリケーションアルゴリズムのシナリオはより一貫性があり、コアをコピーすることですコピーアルゴリズムを使用します

4.2。老後

前のセクションによると、オブジェクトの場合、より長い生存期間が古い地域にクレジットされることがわかります旧世代のエリアがいっぱいになると、ガベージコレクションが行われます。

そのため、古い領域は、ライブオブジェクト、ガベージオブジェクトの減少、圧縮された移動時間の少ないアルゴリズムの使用によって特徴付けられ、メモリの断片化を生成しません。そのため、古い地域では、マーク圧縮アルゴリズムを選択して効率をさらに向上させることができます

5.4つの主要な引用

プログラム開発の過程で、Androidでピクセル情報を運ぶために使用されるような比較的大きなオブジェクトを作成することは避けられませんBitmap。わずかな不適切な使用はメモリリークを引き起こします。同様のオブジェクトが多数ある場合、影響メモリ上はかなり大きいです。

上記の状況を可能な限り回避するために、JVMには、強参照、ソフト参照、弱参照、およびファントム参照の4つのオブジェクト参照メソッドが用意されています。表を使用して類推します。

  • 以下は、GcRootが訪問した対象
参照型 リサイクルのタイミング
強い引用 リサイクルされることはありません(デフォルト)
ソフトリファレンス メモリが少ないときに再利用する
弱い参照 GCが最初にトリガーされたときにリサイクルされます
ファントムリファレンス いつでもリサイクルされます、実用的な意味はありません

参考文献:《Android 工程师进阶 34 讲》 第二讲

結論

この記事では、GCメカニズムを5つの側面から説明しています。

  • GCメカニズムは、開発者の効率を向上させるために生まれました。
  • 相互参照の参照カウントの問題を解決するための到達可能性分析
  • さまざまなシナリオさまざまなGCアルゴリズムを使用すると、効率を向上させることができます
  • GC効率さらに向上させるための世代別リサイクル戦略
  • 4つの参照の巧妙な使用は、ある程度解決される可能性があります。メモリリーク

わかりますか?ニャー〜
わかったらトリプルカンパニーにあげてみませんか?

おすすめ

転載: blog.csdn.net/Androiddddd/article/details/110089308