JVM一些体会

一、从小例子开始

环境是
java version “1.8.0_191”
Java™ SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot™ 64-Bit Server VM (build 25.191-b12, mixed mode)

openjdk version “1.8.0_191”
OpenJDK Runtime Environment (build 1.8.0_191-8u191-b12-2ubuntu0.16.04.1-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)

首先有个简单的垃圾回收的例子,

class TestGC{
    public static void main(String[] args) {
        new TestGC();
        System.gc();//显式调用Full GC(),给出一个回收新老生代的信号(注意,不是一定会回收,因为jvm也会偷懒),一般是自动的,这里作为展示而手动触发
        System.runFinalization();   //强制调用对象的终结方法,这里才是真的销毁(事无绝对,隐式调用gc()时,有的对象如果在二次标记时找回组织,可以继续存活)
    }
}

编译后查看GC

下图环境是windows的HotSpot jdk8

jdk8(图1)
查看详细java堆使用情况(java非标准命令可以参考oracle官方解释
java -XX:+PrintGCDetails -XX:+UseG1GC
解释一下上面的命令,就是在使用G1垃圾回收器的情况下,打印垃圾回收细节(加上过打印时间戳,不过没效果)
图2(图2)

下图环境是Ubuntu下的openjdk8
在这里插入图片描述(图3)
在这里插入图片描述(图4)
对比之后,结果非常有意思。

类型 Full GC G1 GC
jdk 内存占用小,耗时长 堆内存占用少,时间未知
openjdk 内存占用大,耗时短 堆内存占用太多,时间未知

补充一下什么是Full GC?

minor GC:只回收年轻代YoungGen(包括Eden,Survivor),但是从图2、图4发现young和survivors是分开的,所以这就有意思了。
Major GC: 只清理老生代。不过这个定义不太合适,因为不会仅仅清理老生代,且常常由minor GC触发,这两个不能分的太开。
Full GC: 清理全部。

二、G1涉及术语

1、Metaspace

这里改动很大,原来jdk7中还有永久代PermGen,而这里jdk8完全移除了,转为使用元空间Metaspace。

官方JDK8 HotSpot JVM解释是因为要和JRockit 融合而完成的一个小目标。

Hotspot JVM使用本地内存来存储类元数据信息并称这块区域为元空间Metaspace。相比以往PermGen,元空间扩容更容易,可以容纳更多的类数据。

2. Mixed GC Event

这是混合GC事件。注意看开头写的环境信息,其中包含build 25.191-b12, mixed mode,这就表明JVM默认为混合模式。在这个时间内部,所有的年轻代region和一部分老年代region一起回收。

3. Region

G1收集器将堆进行分区,划分为一个个区域,每次收集的时候,选择部分区域,以此控制垃圾回收产生的停顿时间,这个区域就是region,也称作堆区。通常划分为三个区域Eden、Survivor、Old,而Survivor也可以划分为From和To区域。

4. Reclaimable

G1 GC为了能够回收对象,而创建出一系列专门用于存放这些回收对象的堆区region。这些region都在一个链表队列中,存活率由-XX:G1MixedGCLiveThresholdPercent界定。

5. RSet

全称是Rememberd Set,存储不同region之间对象的相互引用,参与遍历region根时的引用可达性分析。HotSpot中用来记录这个的数据结构是OopMap。这样会使各个分区的GC更加独立。

6. CSet

全称是Collection Set,保存一次GC中将被执行垃圾回收的region。GC时,在CSet中所有的存活数据(Live Data)都会被转移(复制/移动)。堆区可以是Eden,Survivor和/或Old Generation,也可以都不是,即为空白分区,region的概念比Eden之类更小,换句话说,Eden会有很多个region。region的大小是一致的,是连续的虚拟内存块,且CSet占JVM内存不能超过1%。

7. G1 Pause Time Target

就是G1停顿目标时间,因为GC是独占式的,所以会引起应用程序停顿,这个停顿时间就是设置的期望时间。在G1中使用了停顿预测模型去预测用户设定的目标停顿时间,并基于这个时间进行垃圾回收。

8. PLAB

Promotion Local Allocation Buffers,用于年轻代回收。其作用是避免多线程竞争相同的资源,每个线程拥有独立的PLAB,用于针对Survivor和Old。这里说个题外话,对象初次创建在Eden中为Allocation,然后因为存在时间较长,慢慢promoting到Survivor,再就是Old。这两个Allocation和Promotion在用法上有相同的含义。

9. TLAB

Thread Local Allocation Buffers,即线程本地分配缓存,是一个线程专用的内存分配区域。避免对象分配时的多线程竞争产生冲突。对于G1来说,TLAB是Eden的一个Region,单一线程使用它分配资源。主要用途是让一个线程通过栈操作的方式独享内存空间,用于对象分配,这样比多个线程之间共享要快很多。

三、JVM内存模型

这个很重要,但是很多人写得很好,不赘述,直接给出引用。
https://blog.csdn.net/u011552404/article/details/80306316
https://blog.csdn.net/wangshuminjava/article/details/80921305
因为模型这个东西是一个框架,理解了就知道jvm的整体运行机制,细节还是需要推敲。

四、G1 GC

G1是在jdk7中正式出现的全新垃圾回收器,从长期愿景来看,它是为了取代CMS回收器。它仍然区分年轻代和老年代,但堆的结构进行调整,不要求整个Eden区、年轻代或者老年代包含的Region区在物理上都是连续的。全新的分区算法,其特点有:
并行性(多个GC线程同时工作)、并发性(与应用程序线程交替执行)、空间整理(不同于CMS的标记-清理和多次GC后才碎片整理,G1每次GC都会整理)、可预见性(由于分区,G1只选择部分区域进行回收,不再是整体的大堆块)

G1的并行循环

包括以下活动:初始标记、并行Root区间扫描、并行标记、重标记和清理。除了最后的清理阶段,其他都属于标记存活对象阶段。
1、初始标记阶段,收集所有GC根。独占式的。
2、并行Root区间扫描必须扫描和标记所有幸存者区间的对象引用,应用程序也可以并行执行,不过有时间约束,即必须在下一个GC开始前结束。
3、并行标记阶段,这里基本上完成全部的标记工作。利用多线程并行标记存活对象及对应的逻辑地图,但是应用程序吞吐量会有所下降。
4、重标记阶段是一个独占式阶段,通常是一个很短的停顿,到此完成所有标记工作。
5、清理阶段,没有包含存活对象的region会被回收,并加入到可用region队列。

猜你喜欢

转载自blog.csdn.net/u014377853/article/details/88763948
今日推荐