序文
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;
上記のコードが実行された後、オブジェクトAとBはリサイクルされますか?参照がnullに設定されているように見えますが、実際には、aとbの次はそれぞれ相互の参照を保持し、相互に参照を保持する状況を形成し、AとBがゴミオブジェクトになり、リサイクルできなくなります。一部の学生は、メモリリークがあまりにも簡単に確認することです、と言うことと、するだけでは十分ではない、それぞれの設定次を空にする前に、そして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コードをメソッドと見なしてonCreate
いmain
ます。
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つの参照の巧妙な使用は、ある程度解決される可能性があります。メモリリーク
わかりますか?ニャー〜
わかったらトリプルカンパニーにあげてみませんか?