本人在这篇文章中讲解了垃圾回收机制,这为前置知识
美团一面面经:Threadlocal(线程局部变量的原理)->内存泄漏问题->垃圾回收机制_threadlocal回收-CSDN博客
首先对前置知识漏洞做一个补充:java的引用类型
-
强引用:被仍一GC Roots对象【强引用】的对象,该对象都不会被垃圾回收
javaUser user=new User();
-
软引用:仅有软引用该对象时,在垃圾回收后,如果内存仍不足会再次触发垃圾回收
javascriptUser user=new User(); SoftReference softReference =new SoftReference (user)
-
弱引用:仅有弱引用该对象时,垃圾回收无论内存是否充足都会回收弱引用对象,需要配合SoftReference(软引用)使用。此时就是ThreadLocal内存泄漏的原因
javaUser user=new User(); WeakReference weakReference=new WeakReference(user)
-
虚引用:必须配合引用队列使用,被引用对象回收时,会将虚引用入队,由Reference Handler线程调用虚引用相关方法释放直接内存
JVM 有哪些垃圾回收器?
- 串行垃圾收集器
- 并行垃圾收集器
- CMS(并发)垃圾收集器
- G1垃圾收集器
串行垃圾收集器
Serial和Serial Old串行垃圾收集器,是指使用单线程进行垃圾回收,堆内存较小,适合个人电脑
- Serial 作用于新生代,采用复制算法
- Serial Old作用与老年代,采用标记-整理算法
垃圾回收时,只有一个线程在工作,并且java应用中的所有线程都要暂停(STW),等待垃圾回收的完成
一个线程 进行垃圾回收 ,其他线程阻塞
并行垃圾收集器
Parallel New和Parallel Old是一个并行垃圾回收器,JDK8默认使用此垃圾回收器
- Parallel New作用于新生代,采用复制算法
- Parallel Old作用于老年代,采用标记-整理算法
在进行垃圾回收的时候,所有线程都要进行暂停(STW),等待垃圾回收的完成
多个 线程同时进行垃圾回收
CMS(并发)垃圾收集器
CMS是使用标记-清除算法的、仅仅针对老年代的垃圾回收器。
1.以最短回收停顿时间为目标的收集器、停顿时间短
2.进行垃圾回收时,应用任然能正常运行
1.初始化阶段,指挥标记与GCRoots之间关联的A。(找出GCRoots之间引用的节点)
2.并发标记的时候,会根据引用链路标记B、C、D(根据引用链路标记节点)
3.重新标记阶段,因为初始化阶段和并发标记阶段中其他线程都是可以运行的,所以就会存在漏标记的问题。在此期间,如果X被引用了,那么重新标记会标记X,如果D或者B没有被引用了也会取消对应的标记。(最后检查在此期间是否有其他线程引用了新节点或者取消了新节点的引用)
4.并发清理
G1垃圾回收器
1.应用于新生代和老年代,JDK9的默认回收器
2.划分多个区域,每个区域都可以充当eden区,survivor,old,humongous,其中humongous转为大对象准备
3.采用复制算法
4.并发失败会触发Full GC
5.分为三个阶段:新生代回收、并发标记、混合收集
1.年轻代垃圾回收
- 初始时,所有区域都处于空闲状态
- 创建了一些对象,跳出一些空闲区域作为伊甸园区存储这些对象
- 当伊甸园需要垃圾回收时,跳出一个空闲区域作为幸存区,用复制算法复制存活对象,需要暂停用户线程
- 随着时间流逝,伊甸园的内存又有不足
- 将伊甸园以及之前心存区中的存活对象,采用复制算法,复制到新的幸存区,其中较老对象晋升至老年代
2.年轻代垃圾回收+并发标记
当老年代占用内存超过阈值(默认45%)后触发并发标记,这是无需暂停用户线程
1.并发标记之后,会有重新标记阶段解决漏标问题,此时需要暂停用户线程
2.这些都完成后就知道了老年代有哪些存活对象,随后进入混合收集阶段。此时不会对所有老年代区域进行回收,而是根据暂停时间目标 优先回收价值高(存活对象少)的区域
混合垃圾回收
混合收集阶段中,参与复制的有 eden、survivor、old
这次回收中:
1.eden区和s区的垃圾回收都将存活对象放入新的S区中
2.s区中到达阈值的存货对象和old区占用内存超过45%的经过GC后的存货对象放入新的O区中
复制完成,内存得到释放。进入下一轮的新生代回收、并发标记、混合收集
一个对象如果太大了会存入一个巨型对象中,一个区域装不下,会分配连续的区域进行存储
文章部分图片来自互联网收集