JVM-GC

Posted on By xqw

JAVA虚拟机基本结构

7个:堆、虚拟机栈、本地方法栈、方法区、程序计数器、运行时常量、直接内存
HotSpot下:
不区分虚拟机栈和本地方法栈,永久代(Permanent Generation)就是方法区,通过-XX:PermSize和-XX:MaxPermsize设置(不是堆里的老年代)
1.6前常量池在永久代(方法区),1.7后移出
Xmx(最大堆)+MaxPermSize(最大方法区)+程序计数器(忽略不计)+剩余内存=操作系统内存
-XX:MaxDirectMemorySize(本地直接接内存)、运行时常量、虚拟机栈和本地方法栈都在剩余内存中争夺

JVM内存 ≈ Java永久代 + Java堆(新生代和老年代) + 线程栈+ Java NIO Linux与JVM的内存关系分析

方法区:
局部变量表:函数参数、局部变量
操作数栈:中间操作结果
帧数据区:常量池解析、正常方法返回、异常处理

对象分配过程

尝试栈上分配
尝试TLAB分配(本地线程分配缓冲Thread Local Allocation Buffer,每个线程自己的堆空间、较小、在Eden上)
判断是否满足直接进入老年代
最后在eden分配

多线程内存分配 1、CAS加失败重试 2、先TLAB上分配,用完后同步锁定 (是否使用TLAB设定 -XX:+/-UseTLAB)
内存分配后,将除了对象头都初始化为0,保证了某些类型字段能访问到其对应的0值

晋升策略
TargetSurvivorRatio(survivor区的使用率,默认50):达到该值进入老年区
MaxTenuringThreshold(进入老年代阈值,默认15):达到该值进入老年区

垃圾回收

算法:

引用计数法
标记清除法
复制算法(新生代,存活对象少,from、to相当于两个区域,一次只使用一个)
标记压缩算法(老年代,存活对象多,复制清除部分,才能产生连续的内存)
分代算法
分区算法

什么时候gc

(1)年轻代Minor GC:当jvm无法为新的对象分配空间的时候就会发生Minor gc,所以分配对象的频率越高,也就越容易发生Minor gc。

(2)老年代Full GC:发生GC有两种情况,①当老年代无法分配内存的时候,会导致MinorGC,②当发生Minor GC的时候可能触发Full GC,由于老年代要对年轻代进行担保,由于进行一次垃圾回收之前是无法确定有多少对象存活,因此老年代并不能清除自己要担保多少空间,因此采取采用动态估算的方法:也就是上一次回收发送时晋升到老年代的对象容量的平均值作为经验值,这样就会有一个问题,当发生一次Minor GC以后,存活的对象剧增(假设小对象),此时老年代并没有满,但是此时平均值增加了,会造成发生Full GC

垃圾收集器:

收集器 串行、并行or并发 新生代/老年代 算法 目标 适用场景
Serial 串行 新生代 复制算法 响应速度优先 单CPU环境下的Client模式
Serial Old 串行 老年代 标记-整理 响应速度优先 单CPU环境下的Client模式、CMS的后备预案
ParNew 并行 新生代 复制算法 响应速度优先 多CPU环境时在Server模式下与CMS配合
Parallel Scavenge 并行 新生代 复制算法 吞吐量优先 在后台运算而不需要太多交互的任务
Parallel Old 并行 老年代 标记-整理 吞吐量优先 在后台运算而不需要太多交互的任务
CMS 并发 老年代 标记-清除 响应速度优先 集中在互联网站或B/S系统服务端上的Java应用
G1 并发 both 标记-整理+复制算法 响应速度优先 面向服务端应用,将来替换CMS

注意:+UseParallelGC 在JDK 7U4后会使用Parallel Scavenge + Parallel Old,而不是书上讲的Parallel Scavenge + Serial Old

G1垃圾回收器详解

JDK8垃圾收集器

四种引用类型:

强引用 强引用,不会被GC
软引用 SoftReference,GC后发现内存还不够会请理软引用对象
弱引用 WeakReference,对象都是被弱引用,执行GC会被回收,最多活一个GC周期
幽灵引用 PhantomReference,get获取对象永远为null,结合引用队列专门用来通知对象被回收的事件

引用队列(ReferenceQueue),将软引用、弱引用、幽灵引用绑定一个队列,JVM在回收的时候会通知这个队列