gc

1, jvm堆空间布局


gc_第1张图片
堆.png

gc堆由年轻代和老年代组成。
年轻代由一个eden和2个survivor组成 大小比例一般为8:1:1
另外有持久代,存储加载的class和常量池,hotspot9改为了元数据空间,可以利用单个进程可用的剩余物理空间。

gc_第2张图片
minor gc.png
gc_第3张图片
card table.png

每次new对象分配空间失败时触发young gc,young gc采用复制算法,把eden和from survivor中的存活对象复制到to survivor,同时增加age,放不下的直接提升到老年代,from survivor中age超过指定值的也提升到老年代,提升失败触发full gc。为了加快young gc速度,尽快确定哪些对象被老年代对象引用,采用card table数据结构,把老年代分成512字节的块(card),card table采用数组结构,每个块在card table中对应一个字节,如果某块中的对象引用了年轻代,对应的字节加标记,young gc时只需要扫描打了标记的老年代空间来确定哪些年轻代对象不能回收。

gc是基于事件触发的,而不是定时任务!!!

一个jvm进程中可以有多个线程请求分配空间,每次都要加锁,影响效率,所以采用Thread-Local Allocation Buffers (TLABs)的方式,每个线程先加锁从eden分配一块较大空间缓存在Thread-Local里,每次分配空间可以采用指针碰撞的方式快速完成。

gc_第4张图片
Serial和Parallel.png
gc_第5张图片
整理和清除.png

Serial gc:
每次gc时stop the world,由单个线程完成年轻代复制或老年代标记整理任务。适用于管理100M左右的堆。

Parallel gc:
每次gc时stop the world,由多个线程完成年轻代复制或老年代标记整理任务。适用于管理较大的堆以及cpu核心数较多时。

gc_第6张图片
cms和g1.png

Concurrent Mark-Sweep gc:
初始标记:stop the world,单线程标记gc root
并发标记:多线程标记gc root可达的对象
预清理:分担重新标记阶段的工作,重新检查并发标记节点修改的对象,尽量降低stop the world的时间。
重新标记:stop the world,多线程重新标记并发标记阶段发生变动的对象(利用card table)。
并发清除:清除不可达对象。

cms回收只会空间不连续,因此需要一个数据结构记录可用空间。cms需要更大的java 堆,因为gc期间,应用线程还在运行。
cms的优点是降低了stop the world的时长,代价是更慢的young gc(因为老年代可用空间不连续,对象提升较慢),降低吞吐量(gc线程抢占应用线程的cpu时间片),额外的堆空间(gc时 应用线程还在请求分配空间)

g1 gc:
把整个gc分割成小块,

gc_第7张图片
对比.png

gc优化
性能指标:高吞吐量,低延迟,低内存占用。
提高其中一个属性,代价都是另外一个或2个属性降低。根据应用需求确定性能指标优先级,提高高优先级牺牲低优先级。

jvm选项:
-X 稳定参数
-XX:[+/-] 实验性参数 +启用 -禁用

打印gc log:
-XX:+PrintGCTimeStamps -XX:+PrintGCDetails -Xloggc:

-XX:+PrintGCTimeStamps:打印从jvm启动到gc发生时的秒数。
-XX:+PrintGCDateStamps:打印时间戳 格式: 年-月-日T时:分:秒.毫秒-时区

gc log格式:
25.753: [GC (Allocation Failure) 25.753:
[ParNew: 39296K->4352K(39296K), 0.0358874 secs]
157512K->133835K(236324K), 0.0360190 secs]
[Times: user=0.13 sys=0.01, real=0.04 secs]

25.753:jvm启动到gc发生时的秒数

[ParNew: 295648K->32968K(306432K)]
[gc名称:gc之前年轻代占用空间 -> gc之后年轻代占用空间(年轻代总空间)]

157512K->133835K(236324K), 0.0360190 secs]
gc之前堆占用空间-> gc之后堆占用空间(堆总空间) gc消耗时间]

[Times: user=0.13 sys=0.01, real=0.04 secs]
[gc线程在用户模式消耗秒数 gc线程系统调用消耗秒数, 完成gc秒数]

gc之后年轻代占用空间即是survivor占用空间。
gc之前堆占用空间 - gc之前年轻代占用空间 = gc之前老年代占用空间
gc之后堆占用空间 - gc之后年轻代占用空间 = gc之后老年代占用空间
由此可以计算出young gc过程中是否有对象提升到老年代,如果有提升,且survivor满了,说明年轻代空间太小了,需要调整young和old比例或增加堆大小。

-XX:+PrintGCApplicationStoppedTime:safepoint操作时应用暂停时长
-XX:+PrintGCApplicationConcurrentTime:safepoint操作之间应用运行时长
-XX:+PrintSafepointStatistics:区分gc和其他safepoint操作
这三个参数用于优化应用延迟

-Xms[g|m|k]:初始/最小堆空间
-Xmx[g|m|k]:最大堆空间
强调高吞吐量和低延迟的应用设置-Xms=-Xmx,防止full gc扩大堆。
-Xss[g|m|k]:栈空间大小
-Xmn[g|m|k]:年轻代大小

-XX:NewSize=[g|m|k]:设置初始/最小年轻代空间
-XX:MaxNewSize=[g|m|k]:设置最大年轻代空间
-Xmn[g|m|k]:设置年轻代空间
堆空间 - 年轻代空间 = 老年代空间

年轻代和老年代任何一个不能满足对象分配需求时触发gc,年轻代触发young gc,young gc提升对象到老年代,老年代使用的gc认为老年代空间不足时触发full gc。full gc中会先回收年轻代再回收老年代。

-XX:-ScavengeBeforeFullGC:full gc时不回收年轻代

你可能感兴趣的:(gc)