GC基础知识
一、什么是垃圾
C语言申请内存:malloc free
C++: new delete
c/C++ 手动回收内存
Java: new ?
自动内存回收,编程上简单,系统不容易出错,手动释放内存,容易出两种类型的问题:
忘记回收
多次回收
没有任何引用指向的一个对象或者多个对象(循环引用)
二、如何定位垃圾
引用计数(ReferenceCount),不能解决循环引用
根可达算法(RootSearch)
对象被判定为不可达之后并不是就被判死刑了,在收集阶段会先判断对象是否有必要执行finalize方法,yes:把它放进F-queue虚拟机会起一个线程执行finalize方法,如果finalize方法里面没有再与根对象连接,就被判死刑
三、常见的垃圾回收算法
标记清除(mark sweep)
算法比较简单,存活对象比较多的情况下效率比较高,所以不适合用于Eden区;两边扫描效率比较低,容易产生碎片,无法提供足够的连续内存而不得不提前触发另一次垃圾收集动作
标记:找出哪些有用的,其他需要回收的都标记出来;此时堆中所有的对象都会被扫描一遍,从而才能确定需要回收的对象,比较耗时
清除:清除掉被标记需要回收的对象,释放出对应的内存空间
拷贝算法 (copying)
将内存划分为两块相等的区域,每次只使用其中一块,当其中一块内存使用完了,就将还存活的对象复制到另外一块上面,然后把已经使用过的内存空间一次清除掉。
适用于存活对象较少的情况,只扫描一次,效率提高没有碎片,但空间浪费使得利用率减低,而且移动复制对象需要调整对象引用
标记压缩(mark compact)
标记过程仍然与"标记-清除"算法一样,但是后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。不会产生碎片,方便对象分配,不会产生内存减半
缺点:扫描两次需要移动对象,效率较低
四、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、堆内存逻辑分区
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区,这叫做动态年龄的一个判断。
五、常见的垃圾回收器
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则停止,进行清理垃圾
Serial old
A stop-the-world ,mark-sweep-compact collector that uses a single GC thread
是Serial垃圾回收器在老年代的版本,在old区使用,使用的是标记-整理算法,工作流程和Serial一样。
Parallel Scavenge(PS,与PO组合)
A stop-the-world ,copying collector which uses a multiple GC threads
上线没有做任何调优的话,默认是PS+PO的组合
和Serial的区别就是它是多线程回收垃圾,Parallel Scavenge收集器是一个新生代收集器,它也是使用复制算法的收集器,又是并行的多线程收集器,看上去和ParNew一样,但是Parallel Scanvenge更关注系统的吞吐量。
Parallel Old
A compacting collector which uses a single GC threads
Parallel Old收集器是Parallel Scavenge收集器的老年代版本,使用多线程和标记-整理算法进行垃圾回收,也是更加关注系统的吞吐量。
ParNew(Parallel New,新Parallel Scavenge)
A STW copying collector which uses multiple GC thread
和Parallel Scavenge的区别就是它做了增强以便和CMS配合使用
优点:在多CPU时,比Serial效率高。PN响应时间优先
缺点:收集过程暂停所有应用程序线程,单CPU时比Serial效率差。
算法:复制算法
适用范围:新生代
应用:运行在Server模式下的虚拟机中首选的新生代收集器
ConcurrentMarkSweep(CMS)
毛病多,但开启了并发回收,是里程碑式的GC
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
六、垃圾收集器跟内存大小的关系
-
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
这个组合已经很少用(在某些版本中已经废弃)
-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