序文
新世代は、JVMのヒープメモリの2歳のモデル、および区域、両者の違いのメインエリアに分割されますであり、そのオブジェクトのストレージ短い生存期間の新世代、異なる生存期間に加えて、古い店舗の生存時間が長いオブジェクト、加えて、さまざまなガベージコレクション戦略、JVMに次のコレクションアルゴリズムがあります。
- クリアラベル
- 整理するタグ
- レプリケーションのアルゴリズム
- 世代別コレクションアルゴリズム
ガベージコレクションのアルゴリズムと、オブジェクトがJVMのガベージオブジェクトであることがあると判断された場合はどうなりますか?オブジェクトは、JVMが生き残っているかどうかを決定することを決定するためのアルゴリズムの独自のセットを持っています。
- 参照カウント
- 到達可能性解析
これら二つの概念のガベージコレクションと決意ターゲットの存在と、徐々にそれらを再度分析します。
JVMは、オブジェクト生きているかどうかを判断することですか?
それは、開発者はオブジェクトが有用であるかどうかを判断することを可能にする場合は、簡単に言えば、非常に単純である:对象没有任何引用
それは、オブジェクトが回収できると考えています。次のプログラムコードを考えてみましょう。
public class App {
public static void main(){
checkFile("/");
}
public static boolean checkFile(String path ){
File file = new File(path);
return file.exists();
}
}
コールを実装するためのプログラムcheckFile
、おそらくこのようにJVM図を:
checkFile
メソッドの実装が完了した後、それはローカル変数が内部にあるfile
スタックフレームと一緒にクリアされ、この時間もJVMで生き残るFileオブジェクトは無用であるヒープ:
人々は非常に明確に見つかったFileオブジェクトは役に立たないされている、それはJVMがある交換していると判断した場合どのオブジェクトがそれを生き残ることができるかどうかを判断するには?
参照カウント
参照カウントアルゴリズムの原理は持っている想像したオブジェクトがあり、比較的簡単であるcount
プロパティは、その都度オブジェクトへの参照が発生しますcount
オブジェクトがチェックするの生きているかどうかを決定する際にJVMを想定し、プラス1のcount
プロパティをし、このプロパティは0説明ではないことがわかりましたオブジェクト内の他のオブジェクトへの参照があります。
まではcheckFile
カウントされます実行方法の完了後は0 1割引になり:
JVMとして、オブジェクトが生き残ったかどうかを判断することは容易です。
オブジェクトは、オブジェクトが回復していないかどうかを決定する方法はありません>の関係よう - > B - A:ただし、参照カウントは明らかな欠点は、問題は、循環参照として、解決することができないことである持っています。
GCルート(到達可能性解析)
为什么会被称为可达性分析
算法呢?可以这样理解如果通过GC Root
能到达一个对象那么这个对象就是存活的。那什么样的对象才是GC Root
呢?
在Java语言中,可作为GC Roots的对象包括下面几种:
- 虚拟机栈中引用的对象(栈帧中的本地变量表);
- 方法区中类静态属性引用的对象;
- 方法区中常量引用的对象;
- 本地方法栈中JNI(Native方法)引用的对象。
还是用上面的例子,在checkFile
方法执行时,因为栈帧变量file
可做为GC Root
所以在执行期间JVM是绝对不会回收掉这个File对象:
但是等到checkFile
执行完成之后,这个栈帧会被弹出,其中的变量也会被释放,相应的没有GC Root
能到达堆中的File对象,这个时候就可以判断这个对象是一个无用的对象了,然后安全回收。
垃圾收回算法
标记清除
这种算法分两分:标记、清除两个阶段,
标记阶段是从根集合(GC Root)开始扫描,每到达一个对象就会标记该对象为存活状态,清除阶段在扫描完成之后将没有标记的对象给清除掉。
用一张图说明:
这个算法有个缺陷就是会产生内存碎片,如上图B被清除掉后会留下一块内存区域,如果后面需要分配大的对象就会导致没有连续的内存可供使用。
标记整理
标记整理就没有内存碎片
的问题了,也是从根集合(GC Root)开始扫描进行标记然后清除无用的对象,清除完成后它会整理内存。
这样内存就是连续的了,但是产生的另外一个问题是:每次都得移动对象,因此成本很高。
复制算法
复制算法会将JVM推分成二等分,如果堆设置的是1g,那使用复制算法的时候堆就会有被划分为两块区域各512m。给对象分配内存的时候总是使用其中的一块来分配,分配满了以后,GC就会进行标记,然后将存活的对象移动到另外一块空白的区域,然后清除掉所有没有存活的对象,这样重复的处理,始终就会有一块空白的区域没有被合理的利用到。
两块区域交替使用,最大问题就是会导致空间的浪费,现在堆内存的使用率只有50%。
分代回收
新生代回收
JVM的堆分为新生代和老年代,两种类型有不同的特性,根据它们的特性来选择不同的回收算法,这种算法会将新生代划分为一块Eden
和二个Survivor
区:
如上面的图有三块区域它们会按照8:1:1的比例进行分配,如1000m的堆Eden
是800m,二个Survivor
各占100m,那它们是如何运行的呢?
- 始终会有一块
Survivor
是空着的,内存使用率是90% - 程序运行会在
Eden
和其中一块Survivor 1
中分配内存 - 等到执行
Minor gc
,会将存活下来的对象移动到空着的Survivor 2
中 - 然后在
Eden
和Survivor 2
中继续分配内存,Survivor 1
空着等着下次使用
这样就能使内存使用率达到90%,也不会产生内存碎片。
老年代回收
老年代对象即使进行了垃圾回收,对象的存活率也高,所以采用标记清除或标记整理算法都是不错的选择,这里就不做阐述。