JVM-GC

GC基础知识

一、什么是垃圾

C语言申请内存:malloc free

C++: new delete

c/C++ 手动回收内存

Java: new ?

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

  1. 忘记回收

  2. 多次回收

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

二、如何定位垃圾

引用计数(ReferenceCount),不能解决循环引用

根可达算法(RootSearch) 0240523a6a984e429b260540c3367a16.png

对象被判定为不可达之后并不是就被判死刑了,在收集阶段会先判断对象是否有必要执行finalize方法,yes:把它放进F-queue虚拟机会起一个线程执行finalize方法,如果finalize方法里面没有再与根对象连接,就被判死刑

三、常见的垃圾回收算法

标记清除(mark sweep)

算法比较简单,存活对象比较多的情况下效率比较高,所以不适合用于Eden区;两边扫描效率比较低,容易产生碎片,无法提供足够的连续内存而不得不提前触发另一次垃圾收集动作

标记:找出哪些有用的,其他需要回收的都标记出来;此时堆中所有的对象都会被扫描一遍,从而才能确定需要回收的对象,比较耗时
清除:清除掉被标记需要回收的对象,释放出对应的内存空间d37e2a20bb94486ba219c6e6a9579ee6.png

 

拷贝算法 (copying)

将内存划分为两块相等的区域,每次只使用其中一块,当其中一块内存使用完了,就将还存活的对象复制到另外一块上面,然后把已经使用过的内存空间一次清除掉。

适用于存活对象较少的情况,只扫描一次,效率提高没有碎片,但空间浪费使得利用率减低,而且移动复制对象需要调整对象引用

98593964fe184409a5f985d78d03dad6.png

标记压缩(mark compact)

标记过程仍然与"标记-清除"算法一样,但是后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。不会产生碎片,方便对象分配,不会产生内存减半

缺点:扫描两次需要移动对象,效率较低

6e727885738044bd9db015801eb2a65a.png

四、JVM内存分代模型

1. 部分垃圾回收器使用的模型
除Epsilon ZGC Shenandoah之外的GC都是使用逻辑分代模型
G1是逻辑分代,物理不分代
除此之外不仅逻辑分代,而且物理分代

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

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

3.新生代 = Eden + 2个suvivor区
YGC回收之后,大多数的对象会被回收,活着的进入s0
再次YGC,活着的对象eden + s0 -> s1
再次YGC,eden + s1 -> s0
年龄足够 -> 老年代 (15 CMS 6)
s区装不下 -> 老年代
4.老年代
顽固分子
老年代满了FGC Full GC
5.GC Tuning (Generation)
尽量减少FGC
MinorGC = YGC
MajorGC = FGC

6.动态年龄:(不重要)

eden+s1到s2的对象超过s2的一半年龄最大的直接进入old区

7.分配担保:(不重要)

YGC期间 survivor区空间不够了 空间担保直接进入老年代

1、堆内存逻辑分区

d5e7c4e148624b1da13e5088b3f5163f.png

 2、栈上分配


C语言里面有struct这样的结构体,那么这种结构的东西是可以直接在栈上分配的。Java为了对标这一点,设计了一个栈上分配的理念。
1.线程私有小对象 :小对象,线程是私有的
2.无逃逸: 就在某一段代码中使用,出了这段代码就没有人认识它了
3.支持标量替换: 意思是用普通的属性、把普通的类型代替对象就叫标量替换
无需调整
栈上分配要比堆上分配要快,在栈上分配不下了,它会优先进行本地分配

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

在伊甸区好多线程都往里头分配对象,分配对象的时候你这个线程一定会进行空间的征用,谁抢到算谁的。多线程的同步,效率就会降低,所以设计了这么一个机制叫做TLAB

1:占用eden,默认1%,在伊甸区取用百分之一的空间,这块空间叫做这个线程独有。分配对象的时候首先往我线程独有的这块空间里进行分配。
2:多线程的时候不用竞争eden就可以申请空间, 提高效率。
小对象
无需调整
老年代
大对象
eden
逃逸:方法内部new了一个没有任何引用指向的对象,这个对象逃不出这个方法,所以就逃逸了;如果这个对象被方法外的引用引用了,就逃逸了。

-XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:-UseTLAB
-XX:-DoEscapeAnalysis 去掉逃逸分析
-XX:-EliminateAllocations 去掉标量替换
-XX:-UseTLAB 去掉TLAB```
new 对象会变慢 栈比堆快 栈弹出对象就没了 不用垃圾回收 线程本地分配快

4、对象什么时候进入老年代

1.超过 XX:MaxTenuringThreshold 指定次数(YGC)
– Parallel Scavenge 15
– CMS 6
– G1 15
最大是15 因为对象头4;
2.动态年龄 垃圾回收s1 s2超过50%进入old区
– s1 - > s2超过50%。两个s之间拷贝来拷贝去只要超过百分之50的时候把年龄最大的直接就放入到
old区,也就是不一定非得到15岁,不一定非得到6岁
– 把年龄最大的放入O

在s1里面有这么多对象拷贝到了s2里面超过了百分之50的话,s1里
面在加上伊甸区里面,整个一个对象一下子拷贝到s2里,经过一次垃圾回收(YGC)。过去之后,这个时候整个加起来对象已经超过s2的一半了,这里面年龄最大的一些对象直接进入old区,这叫做动态年龄的一个判断。

 五、常见的垃圾回收器

6a13ae3126ff40d99e7deb459dcaa860.png

STW

stop-the-world,简称STw,指的是GC事件发生过程中,会产生应用程序的停顿。停顿产生时整个应用程序线程都会被暂停,没有任何响应,有点像卡死的感觉,这个停顿称为STW。STW是JVM在后台自动发起和自动完成的。在用户不可见的情况下,把用户正常的工作线程全部停掉。可达性分析算法中枚举根节点(GC Roots)会导致所有Java执行线程停顿。

serial

A stop-the-world ,copying collector which uses a single GC thread

Serial是单线程垃圾回收器,对早期的单CPU效率最高,STW的时间比较长,很少用了当工作的时候所有工作线程全部停止, 当工作的时候断开的线程则是垃圾 ,如果突然加入Serial则停止,进行清理垃圾

8f15aa41689c4c069f3866c7b249c10e.png

Serial old

A stop-the-world ,mark-sweep-compact collector that uses a single GC thread

是Serial垃圾回收器在老年代的版本,在old区使用,使用的是标记-整理算法,工作流程和Serial一样。

8f15aa41689c4c069f3866c7b249c10e.png

 

Parallel Scavenge(PS,与PO组合)

A stop-the-world ,copying collector which uses a multiple GC threads

上线没有做任何调优的话,默认是PS+PO的组合

和Serial的区别就是它是多线程回收垃圾,Parallel Scavenge收集器是一个新生代收集器,它也是使用复制算法的收集器,又是并行的多线程收集器,看上去和ParNew一样,但是Parallel Scanvenge更关注系统的吞吐量。

5fee5f84bb044b7daab664fbffa5f9a7.png

Parallel Old

A compacting collector which uses a single GC threads

Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程和标记-整理算法进行垃圾回收,也是更加关注系统的吞吐量。

829b4a243bda403a9efd5fed9fd708e7.png

ParNew(Parallel New,新Parallel Scavenge)

A STW copying collector which uses multiple GC thread

和Parallel Scavenge的区别就是它做了增强以便和CMS配合使用

优点:在多CPU时,比Serial效率高。PN响应时间优先
缺点:收集过程暂停所有应用程序线程,单CPU时比Serial效率差。
算法:复制算法
适用范围:新生代
应用:运行在Server模式下的虚拟机中首选的新生代收集器

a4b9e4b4ac2d45818619546183fa9db2.png

ConcurrentMarkSweep(CMS)

毛病多,但开启了并发回收,是里程碑式的GC

c9e21ede731f464e8b03f551ec4a4724.png

CMS的4个阶段

  • 初始标记:标记根对象,有STW
  • 并发标记:由于这一块最耗费时间,所以把这个和应用程序同时进行
  • 重新标记:大部分的垃圾都标记了但是并发标记过程中产生的垃圾还没有标记 有STW
  • 并发清理:在并发清理时会产生的新垃圾也叫浮动垃圾,要等下一次CMS的时候再清理

CMS的问题

1、内存碎片化(Memory Fragmentation)

由于CMS使用的是标记清除算法,就必定会产生内存碎片,等CMS应对大内存是会出现问题,大量的内存碎片化,使得没有空间让新生代进入,这个时候Serial Old会出现用一个线程来做标记压缩,内存很大,但是Serialold是单线程的,相当于一个老奶奶在天安门广场扫垃圾,很慢很慢。

2、浮动垃圾(Floating Garbage)

老年代满了,但有浮动垃圾这个时候Serialold又登场进行压缩。

解决方法:减低触发CMS的阈值--达到百分之几就触发FGC、留足够空间

–XX:CMSInitiatingOccupancyFraction 92% 可以降低这个值,让CMS保持老年代足够的空间

G1

算法:三色标记算法+SATB,另起一篇

Shenandoah、

算法:ColoredPointers + WriteBarrier

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

  1. Serial 几十兆

  2. PS 上百兆 - 几个G

  3. CMS - 20G

  4. G1 - 上百G

  5. ZGC - 4T - 16T(JDK13)

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

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

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

-XX:+UseParNewGC = ParNew + SerialOld
这个组合已经很少用(在某些版本中已经废弃)

-XX:+UseConc(urrent)MarkSweepGC = 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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值