JVM整理(二)

垃圾回收

1. 如何判断对象可以回收
2. 垃圾回收算法
3. 分代垃圾回收
4. 垃圾回收器
5. 垃圾回收调优

1. 如何判断对象可以回收

1.1引用计数器

如个这个对象被其他对象引用的,那么就+1   。存在问题:循环依赖 

1.2 可达性分析算法

  • Java 虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象
  • 扫描堆中的对象,看是否能够沿着 GC Root对象 为起点的引用链找到该对象,找不到,表示可以回收
  • 肯定不可能当成垃圾的对象可以作为GC Root

1.3四种引用

1. 强引用
  • 只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收
2. 软引用(SoftReference)
  • 仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用对象
  • 可以配合引用队列来释放软引用自身
3. 弱引用(WeakReference)
  • 仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象
  • 可以配合引用队列来释放弱引用自身
4. 虚引用(PhantomReference)
  • 必须配合引用队列使用,主要配合 ByteBuffffer 使用,被引用对象回收时,会将虚引用入队,由 Reference Handler 线程调用虚引用相关方法释放直接内存
5. 终结器引用(FinalReference)
  • 无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 fifinalize方法,第二次 GC 时才能回收被引用对象

2、垃圾回收算法

2.1标记清除(Mark Sweep)

  • 速度较快
  • 会造成内存碎片

 

 2.2标记整理(Mark Compact)

  • 速度慢
  • 没有内存碎片

 

 2.3复制(Copy)

  • 不会有内存碎片
  • 需要双倍的内存空间

3、 分代垃圾回收 

 

 

  • 对象首先分配在伊甸园区域
  • 新生代空间不足,触发minor gc ,伊甸园和from存活的对象使用copy复制到to中,存活的对象年龄+1并且交换from to
  • minor gc 会引发 stop the world(STW),暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行 
  • 当对象寿命超过阈值时,会晋升至老年代,最大寿命是15(4bit) 
  • 当老年代空间不足,会先尝试触发 minor gc,如果之后空间仍不足,那么触发 full gc,STW的时间更长

 细节:大对象在新生代空间不够的情况下,会直接晋升到老年代

4、垃圾回收器

1.串行

  • 单线程
  • 对内存较小,适合个人电脑

2.吞吐量优先

  • 多线程
  • 堆内存较大,多核cpu
  • 单位时间内,STW 的时间最短 0.2 0.2 = 0.4,垃圾回收时间占比最低,这样就称吞吐量高

3.响应时间优先

  • 多线程
  • 堆内存较大,多核 cpu 
  • 尽可能让单次 STW 的时间最短 0.1 0.1 0.1 0.1 0.1 = 0.5 

 4.1串行

 

-XX:+UseSerialGC =         Serial                 +               SerialOld 
                                     新生代复制算法                    老年代标记整理算法

 4.2吞吐量优先 

并行(并行是指两个或者多个事件在同一时刻发生)

4.3 响应时间优先(CMS)

并发(并发是指两个或多个事件在同一时间间隔发生)

XX:+UseConcMarkSweepGC ~ -XX:+UseParNewGC ~SerialOld
            并发标记清除                        复制算法                 如果并发失败,会退化到串行
             工作在老年代                       工作在新生代     
-XX:ParallelGCThreads=n ~ -XX:ConcGCThreads=threads
        并行垃圾回收器                并发垃圾回收器
-XX:CMSInitiatingOccupancyFraction=percent
         
-XX:+CMSScavengeBeforeRemark
        在重新标记前清理一次新生代

         

初始标记:只标记一些GC Root根对象,STW非常短

并发标记:没有STW

重新标记:有STW 

 

 注:响应时间 优先在并发清理的时候,其他线程在运行时可能又会产生新的垃圾(浮动垃圾),只会在第二次GC才会被清理   

        对整个应用程序的吞吐量有一定影响

4.4 G1(Garbage First)

适用场景
  • 同时注重吞吐量(Throughput)和低延迟(Low latency),默认的暂停目标是 200 ms
  • 超大堆内存,会将堆划分为多个大小相等的 Region
  • 整体上是 标记+整理 算法,两个区域之间是 复制 算法 

 1) G1 垃圾回收阶段

 

 1、Young Collection 

 会STW,就是一个普通的分代垃圾回收

2、Young Collection + CM 
  • 在 Young GC 时会进行 GC Root 的初始标记
  • 老年代占用堆空间比例达到阈值时,进行并发标记(不会 STW),由下面的 JVM 参数决定

                       -XX:InitiatingHeapOccupancyPercent=percent (默认45%) 

3、Mixed Collection
会对 E、S、O 进行全面垃圾回收
  • 最终标记(Remark)会 STW
  • 拷贝存活(Evacuation)会 STW
                -XX:MaxGCPauseMillis=ms
有选择的收集老年代,垃圾最多的老年代区

 2)Full GC 

  • SerialGC
                  新生代内存不足发生的垃圾收集 - minor gc
                  老年代内存不足发生的垃圾收集 - full gc
  • ParallelGC
                   新生代内存不足发生的垃圾收集 - minor gc
                   老年代内存不足发生的垃圾收集 - full gc
  • CMS
                    新生代内存不足发生的垃圾收集 - minor gc
                    老年代内存不足
  • G1
                     新生代内存不足发生的垃圾收集 - minor gc
                     老年代内存不足
 
CMS和G1有两种情况  :
 G1为例:
老年代内存与整个堆内存占比达到某个阈值,就会触发并发标记和混合收集阶段  
如果垃圾回收速度比产生快,不会触发full gc ,否则触发  

 3)Young Collection 跨代引用

  • 新生代回收的跨代引用(老年代引用新生代)问题

  • 卡表与 Remembered Set
  • 在引用变更时通过 post-write barrier + dirty card queue
  • concurrent refinement threads 更新 Remembered Set

在进行对象引用创建时,会有一个查找过程,查找引用是否被其他区域对象所引用若是,则在RSet中标注,也就是脏卡

避免遍历整个老年代去查找新生代的引用

 

 4)Remark

重标记

 5)JDK 8u20 字符串去重 

 

 6)JDK 8u40 并发标记类卸载

所有对象都经过并发标记后,就能知道哪些类不再被使用,当一个类加载器的所有类都不再使用,则卸载它所加载的所有类

 7)JDK 8u60 回收巨型对象

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

posted @   梦醒如赦  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示