当前位置: 首页 > article >正文

Java的内存管理机制之(垃圾回收(GC)原理)

垃圾回收(GC,Garbage Collection)是Java虚拟机(JVM)中一项核心的内存管理机制,用于自动管理和回收Java堆内存中不再使用的对象,从而防止内存泄漏,提高内存利用率。

垃圾回收的基本原理

垃圾回收的基本思想是将内存中不再被使用的对象进行回收,释放其占用的内存空间。JVM通过一系列的算法和策略来判断哪些对象是可以被回收的,然后执行回收操作。

垃圾回收的判断方法

 Java采用的是基于“可达性分析”的垃圾回收算法,而不是引用计数法。

  1. 引用计数法

    •  原理:为每个对象维护一个引用计数器,每当有一个地方引用它时,计数器就加1;当引用失效时,计数器就减1。当计数器为0时,表示该对象不再被引用,可以被回收。
    • 缺点:无法处理循环引用的问题。因此,主流的JVM并没有采用这种算法。 

      引用计数法简介

      引用计数法是一种简单的垃圾回收算法,它通过跟踪每个对象被引用的次数来判断对象是否应该被回收。当一个对象被创建时,引用计数器的值被设置为1;每当有一个新的引用指向该对象时,计数器的值就增加1;而当某个引用不再指向该对象时(比如引用被置为null或者指向了另一个对象),计数器的值就减少1。当计数器的值变为0时,表示该对象不再被任何引用所指向,因此可以被垃圾回收器回收。

  2. 可达性分析算法

    • 原理:从一组称为GC Roots的根对象开始,通过遍历对象之间的引用链,判断对象是否可以从GC Roots到达。如果对象可以通过引用链与GC Roots关联,则认为对象是存活的;否则,认为对象不再存活,可以被回收。
    • GC Roots通常包括虚拟机栈中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象、本地方法栈中JNI引用的对象等。

      Java中的垃圾回收机制——可达性分析算法

      可达性分析算法的基本思想是通过一系列称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(即GC Roots到这个对象不可达)时,则证明此对象是不可用的,可以被回收。

      Java中常见的GC Roots包括:

       虚拟机栈(栈帧中的局部变量表)中引用的对象

      方法区中的类静态属性引用的对象

      方法区中的常量引用的对象

      本地方法栈中JNI(即一般说的Native方法)引用的对象  

垃圾回收的算法

  1.  标记-清除算法

    • 分为标记和清除两个阶段。首先标记出所有存活的对象,然后清除所有未被标记的对象。
    • 优点:速度快,不需要移动对象。
    • 缺点:会产生内存碎片。

      标记阶段

      • 根集合识别:算法首先识别出所有的根对象(Root Objects)。在Java中,根对象通常包括:

        虚拟机栈(栈帧中的局部变量表)中引用的对象。

        方法区中的类静态属性引用的对象。

        方法区中的常量引用的对象。

        本地方法栈中JNI(即一般说的Native方法)引用的对象。

      • 可达性分析:从根集合开始,通过引用链逐步遍历对象,所有能够从根集合通过引用链到达的对象都被认为是“存活”的。这一过程也被称为可达性分析。

      • 标记存活对象:在遍历过程中,所有被访问到的对象都会被标记为“存活”状态。这通常是通过修改对象的某个标志位来实现的。

        清除阶段

        • 回收内存:遍历堆中的所有对象,对于未被标记为“存活”的对象(即那些从根集合无法到达的对象),它们被认为是垃圾,JVM会回收它们所占用的内存空间。

        • 内存整理(可选):在某些实现中,清除阶段后可能会进行内存整理,将存活的对象移动到堆的一端,以形成一个连续的内存空间,便于后续的内存分配。但请注意,标记-清除算法本身并不强制要求这一步。

  2. 标记-复制算法

    • 将可用内存按容量划分为大小相等的两块,每次只使用其中一块。当这块内存用完时,就将还存活的对象复制到另一块内存上,然后清空当前内存块。
    • 优点:解决了内存碎片问题,实现简单。
    • 缺点:需要两倍内存空间。

    算法的执行过程: 

    暂停应用程序:在垃圾回收开始时,会暂停应用程序的执行,以进行垃圾回收的准备工作。这一过程也被称为“Stop-The-World”事件。

    标记存活对象:垃圾回收器从根集合(如虚拟机栈、方法区中的静态属性等)开始,遍历所有的引用链,标记所有从根可达的对象为存活对象。

    复制存活对象:将标记为存活的对象从来源空间复制到目标空间。这个过程中,只会复制存活的对象,而不会复制那些已经被回收的对象。

    清理来源空间:复制完成后,来源空间中剩下的对象都是不再被使用的,可以被清理掉。此时,来源空间变成了空的,可以用于下一次的分配。

    交换空间:在下一次垃圾回收时,目标空间会变成新的来源空间,而原来的来源空间则变成新的目标空间。这个过程交替进行,以确保内存的有效利用。

    特点

    简单高效:由于每次只处理一半的内存空间,因此理论上垃圾回收的最大暂停时间不会随着堆大小的增加而增加。
    无内存碎片:由于每次都是从空的半空间开始分配对象,因此不会产生内存碎片。
    内存利用率低:由于需要同时维护两个相等的内存空间,因此在实际应用中,内存的利用率会降低。这意味着如果有4G的内存,那么实际可用于应用程序的内存可能只有2G。
    适用于短生存期的对象:由于每次都需要复制存活的对象,因此对于长生存期的对象来说,这种算法的效率并不高。因此,它更适合用于管理那些生命周期较短的对象。

  3. 标记-整理算法

    • 结合了标记-清除和标记-复制的优点。标记阶段与标记-清除算法相同,但在清除阶段,不是直接清除未标记的对象,而是让所有存活的对象都向一端移动,然后清理掉端边线以外的内存。
    • 优点:解决了内存碎片问题,不需要额外内存空间。
    • 缺点:效率相对较低。

    标记阶段:

    遍历堆中的所有对象,通过根集合(如栈帧中的局部变量表、静态变量等)开始,标记出所有从根集合可达(即存在引用链)的对象。

    未被标记的对象被视为垃圾,即不可达对象,它们将在后续的GC过程中被清理。

    整理(或压缩)阶段:

    将所有存活的对象移动到堆的一端,确保它们是连续存储的。

    清理掉存活对象之后的所有空间,即删除所有垃圾对象,并调整存活对象的引用,以反映它们在堆中的新位置。

    这一步的目的是减少堆内存的碎片化,提高内存使用的效率。

  4. 三色标记算法 

    • 一种优化标记过程的算法,将对象分为白色、灰色和黑色三种状态。通过初始标记、并发标记和重新标记三个阶段来标记对象,以减少STW(Stop The World)的时间。

      三色标记算法对象三种状态的转换过程

      三色标记算法中, 对象被分为三种状态: 白色 、 灰色 和 黑色 。

垃圾回收的触发条件

1. 内存分配达到阈值

在Java等语言中,当堆内存中的空闲空间不足以满足新对象的分配需求时,垃圾回收器(GC)可能会被触发。这个阈值可能是固定的,也可能是根据运行时情况动态调整的。

2. 特定区域的内存满了

  • 年轻代(Young Generation)满了:在JVM中,新创建的对象通常首先被分配到年轻代的Eden区。当Eden区满了时,会触发Minor GC(也称为Young GC),以回收不再被使用的对象并清理出空间。
  • 老年代(Old Generation)满了:当老年代没有足够的空间容纳新的对象或年轻代中晋升的对象时,会触发Major GC(也称为Full GC),以回收整个堆中的垃圾对象。
  • 元空间(Metaspace)满了(Java 8及以上版本):元空间用于存储类的元数据。当元空间满了时,也会触发Full GC。

3. 显式调用垃圾回收

在某些语言中,如Java,可以通过调用System.gc()方法显式地建议JVM进行垃圾回收。但请注意,这只是一个建议,JVM可以忽略它,具体是否执行以及何时执行垃圾回收由JVM的内部机制决定。

4. 系统内存压力

当系统内存不足时,操作系统可能会向正在运行的应用程序发送信号,要求其释放不必要的内存。在这种情况下,垃圾回收器可能会被触发以响应内存压力。

5. 特定垃圾回收器的策略

不同的垃圾回收器可能有不同的触发条件。例如,G1垃圾回收器在年轻代和老年代的内存使用达到一定阈值时,会触发混合回收(Mixed GC),即同时回收年轻代和部分老年代的区域。而CMS(Concurrent Mark Sweep)垃圾回收器则会在老年代使用达到一定比例时,触发并发标记和清理过程。


http://www.kler.cn/a/282425.html

相关文章:

  • Python学习从0到1 day26 第三阶段 Spark ④ 数据输出
  • 软考教材重点内容 信息安全工程师 第 4 章 网络安全体系与网络安全模型
  • Odoo :一款免费开源的日化行业ERP管理系统
  • react+hook+vite项目使用eletron打包成桌面应用+可以热更新
  • 无人机应用场景:石油管道巡检技术详解
  • STM32设计学生宿舍监测控制系统
  • 【我的Android进阶之旅】 Android官方推荐的依赖注入框架:Dagger Hilt
  • foundation model
  • 文本分析之关键词提取(TF-IDF算法)
  • 行为型设计模式-模板方法(template method)模式
  • 数据结构(邓俊辉)学习笔记】串 17——Karp-Rabin算法:散列
  • 数据结构:栈、队列详解篇
  • Java 集合之List
  • C++ STL adjacent_find 用法与实现
  • VMware16安装包+详细安装教程
  • 虚拟机Ubuntu误操作导致无法自动联网的解决办法
  • (第三十七天)
  • Unity(2022.3.41LTS) - 着色器
  • 【自由能系列(初级)】大脑功能与贝叶斯计算——深层生成模型的自由能原理
  • junit格式报告解析工具
  • shell脚本-采集容器内自定义端口tcp连接数并通过http接口推送到Prometheus
  • Ruby 多线程
  • UTONMOS:探索未来游戏的元宇宙纪元新篇章
  • 微知-nandflash和norflash名字为什么叫nand和nor?主要区别是什么?
  • js | XMLHttpRequest
  • 【QT | 开发环境搭建】Linux系统(Ubuntu 18.04) 安装 QT 5.12.12 开发环境