Loading

Jvm GC

Garbage

丢失指针的对象

java 与 c++ 垃圾处理区别

Java: new

自动内存回收,编程上简单,系统不容易出错,手动释放内存,容易出两种类型的问题:

  • 忘记回收
  • 多次回收

没有任何引用指向的一个对象或者多个对象(循环引用)

如何找垃圾

  • 引用计数(ReferenceCount)Python用这种
  • 根可达算法(RootSearching)   Hotspot用这种

常见的垃圾回收算法

  • 标记清除(mark sweep) - 位置不连续 产生碎片 效率偏低(两遍扫描)
  • 拷贝算法 (copying) - 没有碎片,浪费空间
  • 标记压缩(mark compact) - 没有碎片,效率偏低(两遍扫描,指针需要调整)

Root Searching,根可达查找

哪些实例是根

  • Jvm stack
  • native method stack
  • runtime constant pool
  • static references in method area
  • clazz

GC Algorithms

  • Mark-Sweep,标记清除
  • Copying,拷贝
  • Mark-Compact,标记压缩

标记清除

  • 算法相对简单,存活对象多的情况下效率高
  • 两遍扫描,位置不连续,产生碎片,效率偏低

拷贝清除

  • 没有碎片
  • 浪费空间,产生内存减半

标记压缩

  • 不会产生碎片,方便对象分配,不会产生内存减半
  • 扫描两次,需要移动对象,效率偏低

Jvm内存分代模型

new : old = 1:2

  • 新生代大量死去,少量存活,采用复制算法
  • 老年代存活率高,回收较少,采用MC或MS

一个对象从出生到消亡

尝试在栈上分配

如果分配不下,进入Eden

 在Eden区经过一次垃圾回收,进入Surivor区,S1

在S1经过一次垃圾回收,进入S2

随着垃圾回收的进行,在S1和S2之间移动,最后年龄足够大进入Old区

GC概念

  • 年轻代:MinorGC/YGC(young gc)
  • 老年代:MajorGC/FullGC

栈上分配

  • 线程私有小对象
  • 无逃逸
  • 支持标量替换

线程本地分配TLAB(Thread Local Allocation Buffer)

  • 占用Eden,默认1%
  • 多线程的时候不用竞争Eden就可以申请空间,提高效率
  • 小对象

对象何时进入老年代

超过 XX:MaxTenuringThreshold 指定次数(YGC)

  • Parallel Scavenge 15
  • CMS 6
  • G1 15

动态年龄

  • s1 -> s2超过50%
  • 把年龄最大的放入O

Java -X

java -XX:+PrintFlagsFinal -version | grep NewRatio

对象分配过程

部分垃圾回收器使用的模型

  • 除Epsilon ZGC Shenandoah之外的GC都是使用逻辑分代模型 
  • G1是逻辑分代,物理不分代  
  • 除此之外不仅逻辑分代,而且物理分代

新生代 + 老年代 + 永久代(1.7)Perm Generation/ 元数据区(1.8) Metaspace

  • 永久代 元数据 - Class
  • 永久代必须指定大小限制 ,元数据可以设置,也可以不设置,无上限(受限于物理内存)
  • 字符串常量 1.7 - 永久代,1.8 - 堆
  • MethodArea逻辑概念 - 永久代、元数据

新生代 = Eden + 2个suvivor区    

  • YGC回收之后,大多数的对象会被回收,活着的进入s0
  • 再次YGC,活着的对象eden + s0 -> s1
  • 再次YGC,eden + s1 -> s0
  • 年龄足够 -> 老年代 (15 CMS 6)
  • 死区装不下 -> 老年代

老年代

  • 顽固分子
  • 老年代满了FGC Full GC

GC Tuning (Generation)

  • 尽量减少FGC
  • MinorGC = YGC
  • MajorGC = FGC

动态年龄:(不重要)

https://www.jianshu.com/p/989d3b06a49d

分配担保:(不重要)

  • YGC期间 survivor区空间不够了 空间担保直接进入老年代
  • 参考:https://cloud.tencent.com/developer/article/1082730

常见的垃圾回收器

Serial

Serial,年轻代,串行回收,stw(stop the world)

Parallel Scavenge

PS 年轻代,并行回收

ParNew

ParNew 年轻代 配合CMS的并行回收

ParNew vs Parallel Scavenge

  • ParNew是和CMS一起使用的,他是Parallel Scavenge的一个变种
  • PN响应时间优先,配合CMS
  • PS吞吐量优先

并发垃圾回收是因为无法忍受STW

CMS是里程碑式的GC,它开启了并发回收的过程,但是CMS毛病较多,因此目前没有任何一个JDK版本默认是CMS

从线程角度,CMS4个阶段

第二个阶段最耗时间,与工作线程并行执行,不影响工作时间

CMS initial mark

标记根对象,stw,根对象比较少,耗时小

CMS concurrent mark

最耗时间,顺着根对象向下查找

如果在并发标记过程中,垃圾有引用了,进入重新标记阶段即 remark

CMS remark

CMS collect

CMS的问题

Memory Fragmentation

  • -XX:+UseCMSCompactAtFullCollection
  • -XX:CMSFullGCsBeforeCompaction 默认为0,指的是经过多次FGC才进行压缩
  • CMS既然是MarkSweep,就一定会有碎片化的问题
  • 碎片到达一定程度,CMS的老年代分配对象分配不下的时候,使用SerialOld进行老年代回收   

Floating Garbage

此刻之后,对象E/F/G是“应该”被回收的。然而因为E已经变为灰色了,其仍会被当作存活对象继续遍历下去。最终的结果是:这部分对象仍会被标记为存活,即本轮GC不会回收这部分内存

这部分本应该回收 但是 没有回收到的内存,被称之为“浮动垃圾”。浮动垃圾并不会影响应用程序的正确性,只是需要等到下一轮垃圾回收中才被清除。

另外,针对并发标记开始后的新对象,通常的做法是直接全部当成黑色,本轮不会进行清除。这部分对象期间可能会变为垃圾,这也算是浮动垃圾的一部分。

Concurrent Mode Failure

  • 老年代要满了,浮动垃圾却没有清理完
  • 解决方案:降低触发CMS的阈值

PromotionFailed

  • 解决方法类似,保持老年代有足够空间

CMS缺点

memory fragmentation

  • -XX:CMSFullGCsBeforeCompaction

floating garbage

  • Concurrent Mode Failure -XX:CMSInitiatingOccupancyFraction:92%
  • SerialOld

if the concurrent collector is unable to finish reclaiming the unreachable objects before the tenured generation fills up,

or if an allocation cannot be satisfiedwith the available free space blocks in the tenured generation,

then theapplication is paused and the collection is completed with all the applicationthreads stopped

–XX:CMSInitiatingOccupancyFraction 92% 

可以降低这个值,让CMS保持老年代足够的空间

Concurrent remark阶段

  • PS + PO -> 加内存 换垃圾回收器 -> PN + CMS + SerialOld(几个小时 - 几天的STW)
  • 几十个G的内存,单线程回收 -> G1 + FGC 几十个G -> 上T内存的服务器 ZGC

CMS

算法:三色标记 + Incremental Update

三色标记算法

  • 初始时,所有对象都在 【白色集合】中;
  • 将GC Roots 直接引用到的对象 挪到 【灰色集合】中;
  • 从灰色集合中获取对象:
  • 3.1. 将本对象 引用到的 其他对象 全部挪到 【灰色集合】中;
  • 3.2. 将本对象 挪到 【黑色集合】里面。
  • 重复步骤3,直至【灰色集合】为空时结束。
  • 结束后,仍在【白色集合】的对象即为GC Roots 不可达,可以进行回收。

注:如果标记结束后对象仍为白色,意味着已经“找不到”该对象在哪了,不可能会再被重新引用。

当Stop The World (以下简称 STW)时,对象间的引用 是不会发生变化的,可以轻松完成标记。
而当需要支持并发标记时,即标记期间应用线程还在继续跑,对象间的引用可能发生变化多标漏标的情况就有可能发生

G1(10ms),JDK1.7以后才有,JDK1.9默认就是G1

  • 算法:三色标记 + SATB

ZGC (1ms) PK C++

  • 算法:ColoredPointers + LoadBarrier

Shenandoah,JDK12新版GC

  • 算法:ColoredPointers + WriteBarrier

垃圾收集器跟内存大小的关系

  • Serial 几十兆
  • PS 上百兆 - 几个G
  • CMS - 20G
  • G1 - 上百G
  • ZGC - 4T - 16T(JDK13)

1.8默认的垃圾回收:PS + ParallelOld

常见垃圾回收器组合参数设定:(1.8)

  • -XX:+UseSerialGC = Serial New (DefNew) + Serial Old
  • 小型程序。默认情况下不会是这种选项,HotSpot会根据计算及配置和JDK版本自动选择收集器
  • -XX:+UseParNewGC = ParNew + SerialOld

这个组合已经很少用(在某些版本中已经废弃)

https://stackoverflow.com/questions/34962257/why-remove-support-for-parnewserialold-anddefnewcms-in-the-future

  • -XX:+UseConcurrentMarkSweepGC = ParNew + CMS + Serial Old
  • -XX:+UseParallelGC = Parallel Scavenge + Parallel Old (1.8默认) 【PS + SerialOld】
  • -XX:+UseParallelOldGC = Parallel Scavenge + Parallel Old
  • -XX:+UseG1GC = G1
  • Linux中没找到默认GC的查看方法,而windows中会打印UseParallelGC 
  • java +XX:+PrintCommandLineFlags -version
  • 通过GC的日志来分辨

Linux下1.8版本默认的垃圾回收器到底是什么

  • 1.8.0_181 默认(看不出来)Copy MarkCompact
  • 1.8.0_222 默认 PS + PO
posted @ 2021-02-19 00:37  BigBender  阅读(31)  评论(0编辑  收藏  举报