gball个人知识库
首页
基础组件
基础知识
算法&设计模式
  • 操作手册
  • 数据库
  • 极客时间
  • 每日随笔
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
  • 画图工具 (opens new window)
关于
  • 网盘 (opens new window)
  • 分类
  • 标签
  • 归档
项目
GitHub (opens new window)

ggball

后端界的小学生
首页
基础组件
基础知识
算法&设计模式
  • 操作手册
  • 数据库
  • 极客时间
  • 每日随笔
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
  • 画图工具 (opens new window)
关于
  • 网盘 (opens new window)
  • 分类
  • 标签
  • 归档
项目
GitHub (opens new window)
  • 面试

  • 数据库

  • linux

  • node

  • tensorFlow

  • 基础组件

  • 基础知识

    • java集合

    • jvm调优

      • JVM大纲
      • 类加载器
      • JVM入门
      • java内存模型
      • java运行时数据区
      • 05_GC and Tuning
      • java对象内存布局
      • jvm垃圾收集器和内存分配策略
        • 序言
        • 💠如何判断对象“已死”
          • 引用计数法
          • 可达性分析算法
        • 🥡什么时候触发GC(垃圾回收)
        • Garbage Collection (垃圾回收)算法
        • 收集器
        • 疑问
          • 1.根可达算法如何解决循环引用的呢?
        • 参考资料:
    • java并发编程

    • java网络编程

    • java8新特性

    • javaAgent

    • java高级

  • 算法与设计模式

  • 分布式

  • 疑难杂症

  • go学习之旅

  • 极客时间

  • 知识库
  • 基础知识
  • jvm调优
ggball
2023-03-09

jvm垃圾收集器和内存分配策略

20230315001155

# 序言

20230314234149

20230315000112

今天讲的是jvm垃圾收集器是如何回收垃圾的,现在我们的生产环境动不动就是几十G,每时每刻产生的垃圾对象也会很多,所以回收他们也是JVM重中之重的任务。

想象下,如果你是环卫工人,你会怎么捡垃圾,首先站在大街上,环顾四周,看看哪些是垃圾,然后在不太忙的时候,先去垃圾比较多的地方收拾垃圾,最后以自己最优美的姿势捡起啦,哦了!jvm和这个捡垃圾过程也十分相像。

jvm回收垃圾的过程很简单,一共就三个步骤:

  1. 如何判断那些对象是垃圾
  2. 什么时候触发垃圾回收
  3. 采用什么算法回收垃圾

# 💠如何判断对象“已死”

因为垃圾回收器的作用是回收无效的对象实例,因此第一件事就是确定对象是否不被使用,即“已死”。

# 引用计数法

对象持有一个引用计数器,当对象被引用时+1,当引用失效时-1,任何时刻对象引用计数器的值为0时,对象被认定为不可使用状态。 优点:原理简单。判定效率高 缺点:

  • 对象之间互相引用,会导致对象的引用计数器一直不为0,从而永远不被认定为不可使用状态。
  • 性能问题,多线程环境下,需要维护每个对象的引用计数器,可能会有加锁操作,降低系统性能
  • 无法处理跨代引用:引用计数法只能处理同一代对象之间的引用关系,无法处理跨代引用。

# 可达性分析算法

首先会有一个 称之为“GC ROOT”的根对象作为起点,我们创建的对象的引用都会链接到“GC ROOT”上,可以想象下,这就是一棵树,“GC ROOT”就是树顶,创建对象的引用根据引用关系在下面开枝散叶。当引用对象到“GC ROOT"之间的引用链断开时,说明该对象不可达,即认定为不可被使用状态。

可达性分析算法

# 🥡什么时候触发GC(垃圾回收)

先要介绍下,堆内存的分布情况,因为大多数对象在创建后都会很快不再被使用,只有少部分对象会被持续使用,所以也反映到了堆上,采用分代收集成了大多数虚拟机的收集理论。

堆内存逻辑分布

堆内存逻辑分布分为新生代(占1/3)和老年代(占2/3),新生代又有1/5的区域是survivor(幸存者分为from和to两个区域)区域

回归正题,由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有两种类型:Scavenge GC和Full GC。 Scavenge GC   一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。

Full GC   对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个堆进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于Full GC的调节。有如下原因可能导致Full GC:

a) 年老代(Tenured)被写满;

b) 持久代(Perm)被写满;

c) System.gc()被显示调用;

d) 上一次GC之后Heap的各域分配策略动态变化;

Young、Tenured和Perm解释:

Young区:也叫新生代,是JVM分配给新创建的对象的内存区域。Young区又分为Eden区、Survivor0区和Survivor1区。当一个对象被创建时,首先会在Eden区分配内存空间。当Eden区满时,JVM会执行Minor GC,将还存活的对象移动到Survivor区,如果Survivor区也满了,则会将存活的对象移动到另一个Survivor区,如果另一个Survivor区也满了,则将对象移动到Tenured区。因为Young区通常存活时间较短,因此使用复制算法来进行垃圾回收。 Tenured区:也叫老年代,是JVM分配给长时间存活的对象的内存区域。因为Tenured区存活时间较长,因此使用标记清除算法来进行垃圾回收。当Tenured区满时,JVM会执行Full GC,对整个堆进行垃圾回收,这个过程比Minor GC的开销要大。 Perm区:也叫永久代,是JVM用来存储类、方法等元数据的内存区域。永久代的大小是有限的,因此如果应用程序不断加载类、卸载类,则可能会导致Perm区内存溢出。从JDK8开始,Perm区被元空间(Metaspace)所替代,元空间的内存大小可以动态调整

# Garbage Collection (垃圾回收)算法

# mark-sweep(标记清除)

先标记不可用对象内存地址,然后清除掉

优点:速度快,不卡顿,效率高 缺点:会产生内存碎片,容易内存泄漏 标记清除

# mark-copying(标记复制)

堆空间分为两块,每次只使用一块,当一块内存用完了,先将不可用对象标记,再移动活下来的对象到另一块区域,新生代会使用。

优点:分配内存方便,没有碎片 缺点:如果是大量对象存活,会增加移动成本,每次只有一半内存可以使用 标记复制

# mark-compact(标记整理)

标记好不可适用对象地址后,针对活下来的对象往一块内存区域移动,标记-整理算法是在标记-清除算法的基础上,又进行了对象的移动,因此成本更高,但是却解决了内存碎片的问题;老年代大多数对象仍然会被使用,适合老年代。

优点:分配内存方便,没有碎片,内存利用率高 缺点:移动对象特别耗时,效率最低 标记整理

# 收集器

  • Serial收集器
  • Serial Old收集器
  • ParNew收集器
  • Parallel Scavenge收集器 关注吞吐量 ,是基于标记-整理算法的
  • Parallel Old收集器
  • CMS收集器 关注延迟 是基于标记-清除算法的
  • Garbage First收集器

垃圾收集器

# 疑问

# 1.根可达算法如何解决循环引用的呢?

在根可达算法中,当JVM遍历堆中的对象时,如果发现一个对象A被引用了,那么JVM就会将对象A标记为存活对象,并继续遍历对象A所引用的其他对象。如果在遍历对象A所引用的其他对象时,发现某个对象B又引用了对象A,那么JVM就会暂时将对象A标记为“可疑对象”,并将对象B继续遍历,直到遍历完所有的对象后,再次遍历所有的对象,重新检查所有可疑对象,如果发现某个可疑对象仍然被引用了,那么就将其标记为存活对象,否则就将其标记为垃圾对象。

这种方法可以解决循环引用的问题,因为在第一次遍历时,对象A会被标记为可疑对象,而不是垃圾对象,因此JVM会继续遍历对象B,直到遍历完所有的对象后,再次检查可疑对象,如果对象A仍然被引用了,那么就将其标记为存活对象,否则就将其标记为垃圾对象。这样,即使对象A和对象B互相引用,也不会被错误地标记为垃圾对象。

# 参考资料:

  • https://www.cnblogs.com/1024community/p/honery.html
  • 《深入理解Java虚拟机3》
  • 马士兵jvm
  • https://segmentfault.com/a/1190000040742205 动图来源
上次更新: 2025/06/04, 15:06:15
java对象内存布局
进程与线程

← java对象内存布局 进程与线程→

最近更新
01
AIIDE
03-07
02
githubActionCICD实战
03-07
03
windows安装Deep-Live-Cam教程
08-11
更多文章>
Theme by Vdoing
总访问量 次 | 总访客数 人
| Copyright © 2021-2025 ggball | 赣ICP备2021008769号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×

评论

  • 评论 ssss
  • 回复
  • 评论 ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss
  • 回复
  • 评论 ssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss
  • 回复
×