G1摘要
G1
启动参数示例
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -XX:+PrintTenuringDistribution -XX:+UseG1GC -XX:+DisableExplicitGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:ParallelGCThreads=4 -Xloggc:/export/Logs/gclogs
- 打印GC日志是很重要的,对性能基本没有影响
- 设置一个合理的GC线程数是很有必要的,特别是当前容器环境,jvm可能获取到的是物理机的处理器个数来作为gc线程个数的基准,而实际上容器只是一个2核4G内存的虚拟机,无疑,GC线程数过多,反而对cpu的利用率不高,并且多线程回收时造成无谓的cpu竞争、切换上下文。
诊断性配置
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -XX:+PrintTenuringDistribution -XX:+UseG1GC -XX:+DisableExplicitGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:ParallelGCThreads=4 -XX:+PrintGCApplicationConcurrentTime -XX:+PrintHeapAtGC -XX:+UnlockDiagnosticVMOptions -XX:+G1PrintRegionLivenessInfo -XX:MaxGCPauseMillis=200 -Xloggc:/export/Logs/gclogs
特点
G1采用分区的思路,用内存分为若干个大小相等的区域,每一块区域都可以为年轻代、老年代服务
并发标记流程
- 初始标记(STW事件) 普通的young gc会捎带这个步骤。标记 Survivor regions which may 引用了老年代 GC pause (young)(inital-mark)
- 并发标记阶段 找到所有在heap上的存活对象,这个过程可以被年轻代收集打断
- 重新标记 完成标记存活对象 in the heap,使用snapshot-at-the-beginning算法
- 清理(STW + Concurrent事件) 分为3小步:1.执行记录存活对象,并且完全的释放regions;2.刷洗rset;3.重置空的regions,返回给free list,最后这个操作是并发的
- 复制 将存活的对象copy到未使用的region,年轻代收集或mixed收集都可以做这个事情
最佳实践
- 不要设置年轻代大小,它会使停顿时间配置无效;无法在需要时扩展年轻代空间
- XX:MaxGCPauseMillis=,N不要设置成一个100%的目标,应该设置为能满足90%或以上的情况的目标,这是个目标,不能保证总是会被满足,所以设置的时间稍微小一点
- 防止浮动垃圾导致Full GC,多开点gc线程,-XX:ConcGCThreads=n;提前开始标记;多分配点heap内存给进程
Region
Old\Eden\Survivor\Humongous(H直接分配到Old,防止反复拷贝移动,大小大于等于region一半的对象)
一个Region的大小可以通过参数-XX:G1HeapRegionSize设定
After the mark phase completes, G1 knows which regions are mostly empty(这里应该说的是被回收后的情况,感觉应该是垃圾比较多的集合). It collects in these regions first, which usually yields a large amount of free space. This is why this method of garbage collection is called Garbage-First.
G1 concentrates its collection and compaction activity on the areas of the heap that are likely to be full of reclaimable objects, that is, garbage
http://www.oracle.com/technetwork/tutorials/tutorials-1876574.html
G1在疏散被标记为可回收的Regions时,将1个或多个Region的对象copy到一个region中,在这个过程中,压缩了和释放干净内存。
这个疏散操作是并行在多处理器上执行的,减少了停顿时间,增加了吞吐量。
参数说明
-XX:MaxGCPauseMillis:每次年轻代垃圾回收的最长时间(最大暂停时间), 如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值.
-XX:GCTimeRatio:设置垃圾回收时间占程序运行时间的百分比,设置吞吐量大小,它的值是一个 0-100 之间的整数。假设 GCTimeRatio 的值为 n,那么系统将花费不超过 1/(1+n) 的时间用于垃圾收集。
Card Table
在cms中,为了在并发标记阶段结束后,可以快速找到哪些card有引用更新,因此将老年代内存分为很多个Card,再使用Card Table维护每个card是否在并发标记阶段有引用变化,如果变化了,我们就说这个Card是Dirty的,这样可以提高重新标记阶段的速度
如果引用了发生了变化,则标记1。标记时,采用了bit位的方式标记,大大节省了空间,如byte=3的二进制位0000 0011,从右到左,第1位可以表示dirtyCard,第2位可以表示有引用年轻代,这样在minor gc时,也可以快速找到需要扫描的老年代card。(很多博文上说一个Card管理512Bytes的heap)
RSet
已记忆集合 Remember Set (RSet):一个谁引用了我的机制,记录引用了当前region的分区内对象的卡片索引,当要回收该分区时,通过扫描分区的RSet,来确定引用本分区内的对象是否存活,来确定本分区内的对象存活情况,如被引用的分区内对象全是垃圾了,则当前分区内的对象可能也是垃圾了。
Remembered Set是辅助GC过程的一种结构,典型的空间换时间工具,和Card Table有些类似。还有一种数据结构也是辅助GC的:Collection Set(CSet),它记录了GC要收集的Region集合,集合里的Region可以是任意年代的。在GC的时候,对于old->young和old->old的跨代对象引用,只要扫描对应的CSet中的RSet即可。
逻辑上说每个Region都有一个RSet,RSet记录了其他Region中的对象引用本Region中对象的关系,属于points-into结构(谁引用了我的对象)。而Card Table则是一种points-out(我引用了谁的对象)的结构,每个Card 覆盖一定范围的Heap(一般为512Bytes)。G1的RSet是在Card Table的基础上实现的:每个Region会记录下别的Region有指向自己的指针,并标记这些指针分别在哪些Card的范围内。 这个RSet其实是一个Hash Table,Key是别的Region的起始地址,Value是一个集合,里面的元素是Card Table的Index。
RSet究竟是怎么辅助GC的呢?在做YGC的时候,只需要选定young generation region的RSet作为根集,这些RSet记录了old->young的跨代引用,避免了扫描整个old generation。 而mixed gc的时候,old generation中记录了old->old的RSet,young->old的引用由扫描全部young generation region得到,这样也不用扫描全部old generation region。所以RSet的引入大大减少了GC的工作量。
RSET数据伪结构
[
{"region:[region`s card index]}...
]
rset记录了引用了当前region的其他region,并精确到了card
card table记录了当前region引用了哪些region
G1提供了两种GC模式,Young GC和Mixed GC,两种都是完全Stop The World的。
Young GC
选定所有年轻代里的Region。通过控制年轻代的region个数,即年轻代内存大小,来控制young GC的时间开销。当Eden区无法申请新的内存时,开始Young GC
Mixed GC
选定所有年轻代里的Region,外加根据global concurrentt marking统计得出收集收益高的若干老年代Region。在用户指定的开销目标范围内尽可能选择收益高的老年代Region。
当堆(总的堆)的使用比例占总Heap(整个堆,包括young)的比例超过InitiatingHeapOccupancyPercent,默认45之后,就会开始ConcurentMarking, 完成了Concurrent Marking后,G1会从Young GC切换到Mixed GC, 在Mixed GC中,G1可以增加若干个Old区域的Region到CSet中
Full GC
由上面的描述可知,Mixed GC不是full GC,它只能回收部分老年代的Region,如果mixed GC在并发阶段,无法跟上程序分配内存的速度,导致新的对象占满了所有空间,导致老年代填满无法继续进行Mixed GC,就会使用serial old GC(full GC)来收集整个GC heap。我们可以知道,G1是不提供单独full GC的,只提供了降级的单线程 old full gc,这次回收可能是很慢的。
- CMS的Initial Marking和Remarking两个STW阶段在Heap区越来越大的情况下需要的时间越长,并且由于内存碎片,需要压缩的话也会造成较长停顿时间。所以需要一种高吞吐量的短暂停时间的收集器,而不管堆内存多大。如果没有Full GC,老年代的空间不会压缩整理。
- TLAB为线程本地分配缓冲区,它的目的为了使对象尽可能快的分配出来。如果对象在一个共享的空间中分配,我们需要采用一些同步机制来管理这些空间内的空闲空间指针。在Eden空间中,每一个线程都有一个固定的分区用于分配对象,即一个TLAB。分配对象时,线程之间不再需要进行任何的同步。
对TLAB空间中无法分配的对象,JVM会尝试在Eden空间中进行分配。如果Eden空间无法容纳该对象,就只能在老年代中进行分配空间。 - Snapshot-At-The-Beginning,由字面理解,是GC开始时活着的对象的一个快照。它是通过Root Tracing得到的,作用是维持并发GC的正确性
一个对象a,如果在gc前,被一个还未标记完成的对象b引用了,gc过程中,并发执行时,如果应用程序将b对a的引用删除,同时,改为另一个已经标记完成的对象c来引用a,garbage collector可能会漏标a,把它当成一个已经没人其他对象引用的白对象,这时严重的错误,为防止这个问题,gc加入了写屏障,将旧的引用关系b <- a记录下来,这样a还是被b引用,就不会被回收了,但是也产生了浮动垃圾
日志
-XX:G1HeapRegionSize=n
诊断性配置
-XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m -XX:+PrintTenuringDistribution -XX:+UseG1GC -XX:+DisableExplicitGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:ParallelGCThreads=4 -XX:+PrintGCApplicationConcurrentTime -XX:+PrintHeapAtGC -XX:+UnlockDiagnosticVMOptions -XX:+G1PrintRegionLivenessInfo -XX:MaxGCPauseMillis=200 -Xloggc:/export/Logs/gclogs
Java HotSpot(TM) 64-Bit Server VM (25.20-b23) for linux-amd64 JRE (1.8.0_20-b26), built on Jul 30 2014 13:13:52 by "java_re" with gcc 4.3.0 20080428 (Red Hat 4.3.0-8)
Memory: 4k page, physical 263727076k(113563148k free), swap 16777212k(16371860k free)
CommandLine flags: -XX:+DisableExplicitGC -XX:+G1PrintRegionLivenessInfo -XX:+HeapDumpOnOutOfMemoryError -XX:InitialHeapSize=2147483648 -XX:MaxHeapSize=2147483648 -XX:MaxMetaspaceSize=268435456 -XX:MetaspaceSize=134217728 -XX:ParallelGCThreads=4 -XX:+PrintGC -XX:+PrintGCApplicationConcurrentTime -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC
2018-12-07T14:41:12.624+0800: 0.148: Application time: 0.0834746 seconds
2018-12-07T14:41:12.637+0800: 0.161: Application time: 0.0125164 seconds
2018-12-07T14:41:13.069+0800: 0.593: Application time: 0.4322354 seconds
2018-12-07T14:41:13.169+0800: 0.693: Application time: 0.0993848 seconds
2018-12-07T14:41:13.169+0800: 0.693: Application time: 0.0004720 seconds
2018-12-07T14:41:13.176+0800: 0.700: Application time: 0.0065544 seconds
2018-12-07T14:41:13.186+0800: 0.710: Application time: 0.0103469 seconds
2018-12-07T14:41:13.197+0800: 0.721: Application time: 0.0102892 seconds
2018-12-07T14:41:13.949+0800: 1.473: Application time: 0.7518253 seconds
2018-12-07T14:41:14.481+0800: 2.005: Application time: 0.5315235 seconds
{Heap before GC invocations=0 (full 0):
garbage-first heap total 2097152K, used 105472K [0x0000000080000000, 0x0000000100000000, 0x0000000100000000)
region size 1024K, 102 young (104448K), 0 survivors (0K) //年轻代一共102个,由于第一次回收,其中全是eden
Metaspace used 13539K, capacity 13806K, committed 13952K, reserved 1060864K
class space used 1471K, capacity 1563K, committed 1664K, reserved 1048576K
2018-12-07T14:41:14.481+0800: 2.005: [GC pause (G1 Evacuation Pause) (young) //撤空暂停,表示从eden copy到survior的产生的停顿,survior装不下就会gc
Desired survivor size 6815744 bytes, new threshold 15 (max 15)
, 0.0215033 secs]
[Parallel Time: 11.1 ms, GC Workers: 4]
[GC Worker Start (ms): Min: 2005.0, Avg: 2005.0, Max: 2005.0, Diff: 0.0]
[Ext Root Scanning (ms): Min: 0.6, Avg: 0.8, Max: 1.2, Diff: 0.6, Sum: 3.3] //扫描非堆root用的时间,如classloaders,jni引用,jvm system roots....
[Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0]
[Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0]
[Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.1, Sum: 0.1]
[Code Root Scanning (ms): Min: 0.1, Avg: 0.2, Max: 0.3, Diff: 0.3, Sum: 0.8] //从code调用来的root扫描时间 How long it took to scan the roots that came from the actual code: local vars, etc.
[Object Copy (ms): Min: 9.7, Avg: 9.9, Max: 10.2, Diff: 0.4, Sum: 39.8] //把存活的对象copy出被收集的regions的时间
[Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] //判断是否可以安全停止,没有更多的工作需要做的时间
[GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1]
[GC Worker Total (ms): Min: 11.0, Avg: 11.0, Max: 11.0, Diff: 0.0, Sum: 44.1] //工作线程一共工作了多次时间
[GC Worker End (ms): Min: 2016.0, Avg: 2016.0, Max: 2016.0, Diff: 0.0]
[Code Root Fixup: 0.7 ms]
[Code Root Migration: 2.1 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.2 ms]
[Other: 7.5 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 6.8 ms] //执行非强引用:清理他们或者不需要清理
[Ref Enq: 0.1 ms] //非强引用入队时间
[Redirty Cards: 0.0 ms]
[Free CSet: 0.2 ms]
[Eden: 102.0M(102.0M)->0.0B(89.0M) Survivors: 0.0B->13.0M Heap: 103.0M(2048.0M)->22.5M(2048.0M)]
Heap after GC invocations=1 (full 0):
garbage-first heap total 2097152K, used 23027K [0x0000000080000000, 0x0000000100000000, 0x0000000100000000)
region size 1024K, 13 young (13312K), 13 survivors (13312K)
Metaspace used 13539K, capacity 13806K, committed 13952K, reserved 1060864K
class space used 1471K, capacity 1563K, committed 1664K, reserved 1048576K
}
[Times: user=0.05 sys=0.01, real=0.03 secs]
https://liuzhengyang.github.io/2017/06/07/garbage-first-collector/
-XX:MetaspaceSize=2m -XX:MaxMetaspaceSize=256m -XX:+PrintTenuringDistribution -XX:+UseG1GC -XX:+DisableExplicitGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:ParallelGCThreads=4 -XX:+PrintHeapAtGC
-XX:MaxMetaspaceSize=256m -XX:+PrintTenuringDistribution -XX:+UseG1GC -XX:+DisableExplicitGC -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:ParallelGCThreads=4 -XX:+PrintHeapAtGC -Xloggc:/export/Logs/gclogs
2018-07-05T20:02:34.115+0800: 2.949: [GC pause (G1 Evacuation Pause) (young) 表示从eden copy到survior的停顿
-XX:MetaspaceSize=2m时,会有很多mixed gc;
[GC pause (G1 Evacuation Pause) (young)
[GC pause (G1 Evacuation Pause) (mixed)
-XX:MaxMetaspaceSize=16m 2018-07-16T11:43:08.780+0800: [GC pause (Metadata GC Threshold) (young) (initial-mark)
设置-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=8m 8m是一个不足的内存
2018-07-16T15:55:09.036+0800: [Full GC (Last ditch collection) 1718K->1718K(8192K), 0.0078003 secs]
[Eden: 0.0B(4096.0K)->0.0B(4096.0K) Survivors: 0.0B->0.0B Heap: 1718.9K(8192.0K)->1718.9K(8192.0K)], [Metaspace: 7989K->7989K(1056768K)]
Heap after GC invocations=1132 (full 809):
garbage-first heap total 8192K, used 1718K [0x0000000088e00000, 0x0000000088f00040, 0x0000000100000000)
region size 1024K, 0 young (0K), 0 survivors (0K)
Metaspace used 7989K, capacity 8134K, committed 8192K, reserved 1056768K
class space used 926K, capacity 990K, committed 1024K, reserved 1048576K
}
[Times: user=0.02 sys=0.00, real=0.01 secs]
- metaspace回收:classloader无效时才会回收,相比于jdk7的permgen空间,metaspace不是一块连续的内存空间,full gc时,会根据classloader是否能回收,来确定相关联的class能否回收,即使相关联的class由于重复创建导致的错误,并未正常给用户使用,并且已满足class回收条件(此时错误class并未引用classloader,在创建class后进行的约束检查中会失败),仍然不会被回收掉。而permgen空间在full gc时,会和老年代一起做可用性分析,如果无引用,会被回收掉,这也是很多应用升级成jdk8产生metaspace oom的原因:一些框架会由于weakhashmap等缓存结构在某些情况下key被回收,导致判断class是否已存在时失败,重复创建class,但又无法通过jvm的约束校验,此时生成了重复的class,在jdk7时会在full gc时被回收,在jdk8中metaspace在full gc时,却没有回收
GC root
- 虚拟机栈栈帧中的变量引用的对象,扫描栈时,hotspot使用oopmap加速扫描,而不用遍历每个栈帧
- 类的常量,如果类在,这个常量引用的对象就得在
- 静态变量引用的对象
- 本地方法栈中引用的对象
备注
Metadata GC Threshold:metaspace空间不能满足分配时触发,这个阶段不会清理软引用;
Last ditch collection:经过Metadata GC Threshold触发的full gc后还是不能满足条件,这个时候会触发再一次的gc cause为Last ditch collection的full gc,这次full gc会清理掉软引用。
-XX:InitiatingHeapOccupancyPercent是基于整个堆的,而不是某一代
H区域会存放大于RegionSize 一半的大对象,当xms=xmx=4g时,regionSize=2M,region个数=2048
不要设置年轻代大小-Xmn,
设置-XX:NewRatio=1或者2 后,启动时的用 gc变少了,gc平均耗时立即突破200ms,达到209ms,因此,设置NewRatio也会导致MaxGCPauseMillis失效
参考文章
oopmap \remeberset - >https://dsxwjhf.iteye.com/blog/2201685
https://blogs.oracle.com/poonam/understanding-g1-gc-logs
G1摘要的更多相关文章
- 16、爬取知乎大v张佳玮的文章“标题”、“摘要”、“链接”,并存储到本地文件
爬取知乎大v张佳玮的文章“标题”.“摘要”.“链接”,并存储到本地文件 # 爬取知乎大v张佳玮的文章“标题”.“摘要”.“链接”,并存储到本地文件 # URL https://www.zhihu.co ...
- 深入理解 Java G1 垃圾收集器--转
原文地址:http://blog.jobbole.com/109170/?utm_source=hao.jobbole.com&utm_medium=relatedArticle 本文首先简单 ...
- java根据html生成摘要
转自:http://java.freesion.com/article/48772295755/ 开发一个系统,需要用到这个,根据html生成你指定多少位的摘要 package com.chendao ...
- Atitit HTTP 认证机制基本验证 (Basic Authentication) 和摘要验证 (Digest Authentication)attilax总结
Atitit HTTP认证机制基本验证 (Basic Authentication) 和摘要验证 (Digest Authentication)attilax总结 1.1. 最广泛使用的是基本验证 ( ...
- 2、摘要函数——MD2/MD4/MD5数字签名
摘要是用来防止数据被私自改动的方法,其中用到的函数叫做摘要函数.这些函数的输入可以是任意大小的信息,但是输出是大小固定的摘要.摘要有个重要的特性:如果改变了输入信息的任何内容,即使改变一位,输出也将发 ...
- 前端学HTTP之摘要认证
前面的话 上一篇介绍的基本认证便捷灵活,但极不安全.用户名和密码都是以明文形式传送的,也没有采取任何措施防止对报文的篡改.安全使用基本认证的唯一方式就是将其与SSL配合使用 摘要认证与基本认证兼容,但 ...
- Java 消息摘要 散列 MD5 SHA
package xxx.common.util; import java.math.BigInteger; import java.security.MessageDigest; import jav ...
- rpm查询命令摘要
任务 命令 显示软件包的相关信息 rpm -q -i NAME 列出软件包中含有的所有文件 rpm -q -i NAME 列出软件包中含有的配置文件 rpm -q -c NAME 列出软件包中含有的文 ...
- G1 垃圾收集器
概念先知 什么是垃圾回收 简单的说垃圾回收就是回收内存中不再使用的对象. 垃圾回收的基本步骤: 查找内存中不再使用的对象 释放这些对象占用的内存 查找内存中不再使用的对象 如何判断哪些对象不再被使用呢 ...
随机推荐
- 4.CountDownLatch-闭锁
- selenium初探
先看看官方给的小demo from selenium import webdriver from selenium.webdriver.common.keys import Keys driver = ...
- 搜索引擎学习(三)Lucene查询索引
一.查询理论 创建查询:构建一个包含了文档域和语汇单元的文档查询对象.(例:fileName:lucene) 查询过程:根据查询对象的条件,在索引中找出相应的term,然后根据term找到对应的文档i ...
- RestTemplate get请求多参数 简单封装
使用RestTemplate发送get请求时,如果有多个参数拼接起来会比较麻烦,在此做个简单的封装 public static void main(String[] args) { Map<St ...
- 你可能不知道的 Date 类
Date 是 JS 中的重要的一个内置对象,其实例主要用于处理时间和日期,其时间基于 1970-1-1 (世界标准时间)起的毫秒数,时间戳长度为 13 位(不同于 Unix 时间戳的长度 10 位). ...
- sping aop 源码分析(-)-- 代理对象的创建过程分析
测试项目已上传到码云,可以下载:https://gitee.com/yangxioahui/aopdemo.git 具体如下: public interface Calc { Integer add( ...
- 项目里出现两个配置类继承WebMvcConfigurationSupport时,为什么只有一个会生效(源码分析)
为什么我们的项目里出现两个配置类继承WebMvcConfigurationSupport时,只有一个会生效.我在网上找了半天都是说结果的,没有人分析源码到底是为啥,博主准备讲解一下,希望可以帮到大家! ...
- Python-序列切片原理和切片协议-[start:end:step] __getitem__
切片原理图(顾头不顾尾的正则原理) # [0:1] 其实只取到C, 取e则 [-1:], 如果步长为负数则倒过来取,从第几个往回取 name = "ChuiXue" print(n ...
- 类名@6d5037a9(地址)
问题: 输出的结果不正确 应出现结果 解决问题方法 在@Autowired注入的@Value中缺少toString方法 @Override public String toString() { ret ...
- JS之回调函数(callback)
1.什么是回调函数? -- 简单点说,一个函数被作为参数传递给另一个函数(在这里我们把另一个函数叫做"otherFunction"),回调函数在otherFunction中被调用. ...