素人の言語JVMのメモリモデルで+ガベージコレクションアルゴリズム

序文

以前にこのノートを書き、自分の思い出を統合しながら、私たちは、一つの研究を願っています!と組み合わせて、自分の空き時間、冬時間のブロガー、Java仮想マシンの深い理解学校の教師周志明を駆動し、学ぶことを学びます、

JVMのメモリ・モデル

:我々はすべて知っているように、JVMは5つのモジュールに分かれて
仮想マシンのスタック、ネイティブメソッドスタック、プログラムカウンタ、メソッド領域、およびヒープ。
共有スレッドと専用スレッド:5つのメモリモジュールが二つのタイプに分けることができ、前記
スレッドシェア: VMスタック、プログラムカウンタ、ネイティブメソッドスタック
プライベートスレッド:方法面積を、スタックが
示します:
ここに画像を挿入説明

1.プログラムカウンタは、(現在のスレッドを記録します)

プログラムカウンタは、スレッドプライベートである、非常に小さなメモリ空間であり、(PCのCPUと同様に)現在のスレッドライン数の指標とみなすことができる
(これはマルチコアCPUコアである場合)、プロセッサに、中時間を決定することだけ、スレッドに複数の命令を命令のスレッドを実行する、ハンドオーバを実行するスレッドは、異なるプログラム・スレッドとの間に、正しい位置、別のプログラムカウンタの各スレッドの必要性に戻すことができます互いに独立して、単離された記憶装置のカウンタ。注:スレッドがJavaメソッド、仮想マシンのバイトコード命令のアドレスを記録するカウンタを実行している場合。[ネイティブ]根本的な方法は、その後、カウンタが空の場合。そして、このメモリ領域は、仮想マシン仕様のOutOfMemoryErrorエリアではありません。

2. Javaスタック(VMスタック)

また、カウンタースレッドプライベート、ライフサイクルと同じスレッドで、それは我々が通常スタックは、Javaのメモリモデルを実行する方法を説明すると言うものです。スタックフレームは、局所変数テーブル、操作情報スタック、動的リンク、方法輸出を格納するために作成されたときにそれぞれの方法が実行されます。方法の各プロセスは、プロセス・スタックのスタックにスタックから仮想マシンへのスタックフレームの対応と呼ばれます。また、ショーのテーブルを格納するためのVMスタックのローカル変数の8つの基本データ型をコンパイルし、ここではそれがああ小さなテストセンターである:
整数:バイト、ショート、整数、ロングフロート:フロート、ダブル文字:文字boolean型:boolean型
の参照、およびオブジェクトタイプとRETURNADDRESS
ここに画像を挿入説明
ここで言及ポイントには、次のとおりです。オブジェクトアクセスの方法

  1. 指针直接访问实例数据
    在这种方式中,JVM栈中的栈帧中的本地变量表中所存储的引用地址就是实例数据的地址。通过这个引用就能直接获取到实例数据的地址。
    除此之外,其实引用所指向的对内存中的对象数据有两部分组成,一部分就是这个对象实例本身,另一部分是对象类型在方法区中的地址。
  2. 使用句柄间接访问实例数据
    JVM会在堆中划分一块内存来作为句柄池,JVM栈中的栈帧中的本地变量表中所存储的引用地址**是这个对象所对应的句柄地址,而非对象本身的地址。**句柄池中的一个个对象地址有两部分组成,一部分就是对象数据在堆内存中实例池中的地址,另一部分就是对象类型在方法区中的地址。

ここに画像を挿入説明

3. 本地方法栈

本地方法栈是与虚拟机栈发挥的作用十分相似,区别是虚拟机栈执行的是Java方 法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的native方法服务,这里个人理解的是:前者调用外部的代码,本地方法栈调用的是JDK自带的代码。本地方法栈可能底层调用的c或者c++,我们打开jdk安装目录可以看到也有很多用c编写的文件,可能就是native方法所调用的c代码。

4. 堆

周老师在这里皮了一下,说这个是 “垃圾堆” 这个比喻很形象,所谓垃圾堆即指GC频繁发生的区域,即对象多的地方。在这个区域所有线程是共享的,它的目的是存放对象实例。同时它也是GC所管理的主要区域,因此常被称为GC堆(“垃圾堆”),又由于现在收集器常使用分代算法,Java堆中还可以 细分为新生代和老年代,再细致点还有Eden(伊甸园)空间等等,如下图所示。 根据虚拟机规范,Java堆可以存在物理上不连续的内存空间,就像磁盘空间只要 逻辑是连续的即可。它的内存大小可以设为固定大小,也可以扩展。 当前主流的虚拟机如HotPot都能按扩展实现(通过设置 -Xmx和-Xms),如果堆中没有内存内存完成实例分配,而且堆无法扩展将报OOM错误 (OutOfMemoryError)
图示:
ここに画像を挿入説明

5.方法区

方法区同堆一样,是所有线程共享的内存区域,为了区分堆,又被称为非堆。 用于存储已被虚拟机加载的版本、字段、方法、接口和常量池(存储字面量和符号引用)、常量、静态变量,如 static 修饰的变量加载类的时候就被加载到方法区中。这里就解释了之前的博客里面的

String name=new String("HXZ");

强引用类型。
为什么就创建一或者两个对象了:
1 个的情况: 如果字符串池中已经存在了"HXZ"这个对象,那么直接在创建一个对象放入堆中,返回 name 引用。
2 个的情况: 如果字符串池中未找到"HXZ"这个对象,那么分别在堆中和字符串池中创建一个对象,但是如果遇到了new关键字,则还是会在内存(不是字符串池)中创建一个对象,然后将对象返回给引用name。字符串池中的比较都是采用equals方法。String的equals的方法是重写了Object超类13个方法中的equals的方法故能进行字符串内容的比较,重写equals方法必须重写hashcode方法(why?比如我去酒店,登记的前台好比哈希算法,名字是对比好比 equals 方法,身份证号的对比好比 hashcode 方法只有equals 和 hashcode 都满足的时候才能确保是同一个对象)。
这里还是强调一点,就是在jdk1.8及以后方法区的字符串池被挪到了堆里,不在方法区了,方法区的永久代也被元空间所取代。

6.直接内存

直接内存并不是虚拟机规范定义的数据区的一部分,也不是虚拟机运行时数据区 的一部分。但是这部分内存也被频繁的使用,而且也可能导致 OutOfMemoryerror。
调用native方法时,如要操作native方法的数据时,需要来回不停地复制,而直 接内存可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆 中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场合中 显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。

JVM垃圾回收

前面讲到堆是Java虚拟机是虚拟机所管理的内存里面最大的一块,所有的对象几乎都在这里分配内存。《深入理解Java虚拟机》这本书里还提到了随着时间的推移,这种情况也在随之改变:JIT编译器 的发展和
针对不同区域JVM采用了不同的GC,不同的GC是通过不同的算法实现的。在JDK8中,按照回收区域的不 同,把GC分为工作在新生代的普通GC(minorGC)和工作在堆全局空间的全局GC(Full GC)。 新生代和老年代占比空间为1:2,且采用了不同的算法,所以minor GC 的速度要比Full GC快很多。

ここに画像を挿入説明

垃圾判断标准

1. 引用计数法

给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。
目前主流的java虚拟机都摒弃掉了这种算法,最主要的原因是它很难解决对象
之间相互循环引用的问题。尽管该算法执行效率很高。

2. 可达性分析算法(根索法)

这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。
ここに画像を挿入説明

圾回收算法

1. 标记清除

先假设堆中所有对象都可以回收,然后找出不能回收的对象,给这些对象打上标记,最后堆中没有打标记的对象都是可以被回收的。这种算法虽然不需要多余的内存空间 “周转” 对象,但是会导致内存碎片化,如图所示:
ここに画像を挿入説明

2. 复制算法

HotSpot 把新生代分为三个部分:Eden区和两个Survivor区(From区和To区),(上方有图,这里不放了)默认比例8:1:1。对象 创建时会被放在Eden区,当Eden区触发GC(minor GC),GC会对Eden和Survivor区进行垃圾回收,将Eden和Survivor中还存活着的对象一次性复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。而幸存下来的对象会被 “复制” 到Survivor1区(To区),然后清空Eden和From区,最后将To和From 交换,让刚才被清空的From作新的To区,让刚才保存对象的To区作新的From区,以保证下一次GC可以扫 描到这些对象。这个过程中涉及到了一个 “复制” 的操作,就是 “复制算法” 的产物。顺带一提:当一个对 象在多次GC后依然无法被回收,在From区和To区来回复制,每复制一次“年龄”加1,一旦“年龄”达到 MaxTenuringThreshold的值(阈值,默认为15)就会被移动到老年代。
如图所示:
ここに画像を挿入説明
“复制算法” 的优缺点:
优点:1、由于“复制算法”采用了复制—清空的方法,所以不会导致内存空间的碎片化。
缺点:1、由于复制算法需要另外的空间来 “周转” 这些幸存的对象,所以内存消耗比较大。2、如果存在“极端情况”,比如大量的对象循环引用而导致无法回收的幸存对象占比很大,假设 为80%,那么就需要将这些数量庞大的对象都复制遍,并将所有的引用地址重置一遍,这回耗费比较多 的时间。所以复制算法的最佳工作环境就是这一块的对象存活率比较低。

3. 标记整理

标记整理其实就是在标记清除后加了一个 “整理” 操作,将分散的数据整理到一块连续的内存空间。就是慢,但慢工出细活。
ここに画像を挿入説明
优点:减少了地址空间的占用。
缺点:时间效率问题。

4.分代回收

一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记—清除”或者“标记—整理”算法来进行回收

GC垃圾回收器

如图所示:
ここに画像を挿入説明
按照新生代和老年代的划分,可将GC回收器分成两部分,

1. Serial 垃圾收集器(单线程、复制算法) (新生代)

Serial(英文连续)是最基本垃圾收集器,使用复制算法,曾经是JDK1.3.1之前新生代唯一的垃圾 收集器。Serial 是一个单线程的收集器,它不但只会使用一个 CPU 或一条线程去完成垃圾收集工 作,并且在进行垃圾收集的同时,必须暂停其他所有的工作线程,直到垃圾收集结束。 Serial 垃圾收集器虽然在收集垃圾过程中需要暂停所有其他的工作线程,但是它简单高效,对于限 定单个 CPU 环境来说,没有线程交互的开销,可以获得最高的单线程垃圾收集效率,因此 Serial 垃圾收集器依然是java虚拟机运行在Client模式下默认的新生代垃圾收集器。

2. ParNew 垃圾收集器(Serial+多线程) (新生代)

ParNew垃圾收集器其实是Serial收集器的多线程版本,也使用复制算法,除了使用多线程进行垃 圾收集之外,其余的行为和Serial收集器完全一样,ParNew垃圾收集器在垃圾收集过程中同样也 要暂停所有其他的工作线程。
ParNew 收集器默认开启和 CPU 数目相同的线程数,可以通过-XX:ParallelGCThreads 参数来限 制垃圾收集器的线程数。【Parallel:平行的】 ParNew虽然是除了多线程外和Serial收集器几乎完全一样,但是ParNew垃圾收集器是很多java 虚拟机运行在Server模式下新生代的默认垃圾收集器。

3. Parallel Scavenge 收集器(多线程复制算法、高效) (新生代)

Parallel Scavenge 收集器也是一个新生代垃圾收集器,同样使用复制算法,也是一个多线程的垃 圾收集器,它重点关注的是程序达到一个可控制的吞吐量(Thoughput,CPU 用于运行用户代码 的时间/CPU 总消耗时间,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)), 高吞吐量可以最高效率地利用 CPU 时间,尽快地完成程序的运算任务,主要适用于在后台运算而 不需要太多交互的任务。自适应调节策略也是 ParallelScavenge 收集器与 ParNew 收集器的一个 重要区别。

4. Serial Old 收集器(单线程标记整理算法 ) (老年代)

Serial Old 是 Serial 垃圾收集器年老代版本,它同样是个单线程的收集器,使用标记-整理算法, 这个收集器也主要是运行在Client默认的java虚拟机默认的年老代垃圾收集器。 在Server模式下,主要有两个用途:

  1. 在JDK1.5之前版本中与新生代的Parallel Scavenge收集器搭配使用。
  2. バックアップガベージコレクション方式として、旧世代のCMSコレクタを使用してください。旧世代とガベージコレクションプロセスを持つ新世代のシリアルシリアル旧マップ:
    ここに画像を挿入説明

5.パラレルオールド・コレクターは(才)(アルゴリズムを整理するタグをマルチスレッド)

照合アルゴリズム、JDK1.6からのみ利用可能-パラレルオールド・コレクターは古い世代の並列スカベンジバージョン、マルチスレッドマークの使用です。JDK1.6前に、唯一のParallelScavenge古い世代のシリアル・オールド・コレクターの使用とコレクターの新世代は、だけで、全体のスループットを保証することはできません、スループット優先の新世代を保証することができ、古い世代に正確に古いパラレル優先順位はまた、スループットガベージコレクタを提供要件スループットシステムが比較的高い場合は、旧世代とパラレル清掃をパラレルオールド・コレクターの新世代と戦略に優先順位を付けることができ、。スカベンジにプロセス・ダイアグラムを実行している新世代と旧世代パラレルオールド・コレクターパラレル:
ここに画像を挿入説明

6. CMSコレクタ(マルチスレッドのマークスイープアルゴリズム)(歳)

参照

周志明「Java仮想マシンの深い理解」
https://www.jianshu.com/p/2f4a8e04c657
https://blog.csdn.net/canot/article/details/51037938
https://www.jianshu.com/p/ d09207c047ec

リリース9件のオリジナルの記事 ウォン称賛73 ビュー8734

おすすめ

転載: blog.csdn.net/AAAhxz/article/details/103969259