Java进阶—GC回收(垃圾回收)

1. 什么是垃圾回收

垃圾回收(Garbage Collection,GC)是Java虚拟机(JVM)的一项重要功能,用于自动管理程序中不再使用的内存。在Java中,程序员不需要手动释放内存,因为GC会自动检测并回收不再使用的对象,从而减少内存泄漏的风险。

2. 垃圾回收的空间

Java 的自动内存管理主要是针对对象内存的回收和对象内存的分配。同时,Java 自动内存管理最核心的功能是 堆内存中对象的分配与回收。
Java 堆是垃圾收集器管理的主要区域,因此也被称作 GC 堆(Garbage Collected Heap)。
从垃圾回收的角度来说,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆被划分为了几个不同的区域,这样我们就可以根据各个区域的特点选择合适的垃圾收集算法。
记住jdk8中的垃圾回收区域就好
在这里插入图片描述

3. 垃圾回收器的分类

3.1按实现方式分类

  1. 串行垃圾回收器(Serial GC):使用单个线程进行垃圾回收,适用于单核CPU环境。可以通过 -XX:+UseSerialGC 参数启用。
  2. 并行垃圾回收器(Paraller GC):使用多个线程同时进行垃圾回收,提高回收效率,适用于多核CPU环境。可以通过 -XX:+UseParallelGC 参数启用。
  3. 并发垃圾回收器(Concurrent GC):在应用程序运行的同事进行垃圾回收,减少停顿时间,提高响应性能。可以通过 -XX:+UseConcMarkSweepGC 参数启用。
  4. G1垃圾回收器(Garbage-First Garbage Collector):一种面向服务端应用的垃圾回收器,具有高吞吐量和地停顿时间的特点,适用于大内存应用。可以通过 -XX:+UseG1GC 参数启用。
  5. ZGC:一种低延迟的垃圾回收器,适用于需要更短停顿时间的应用场景。可以通过 -XX:+UseZGC 参数启用。

Java虚拟机的默认垃圾回收器随着Java版本和虚拟机的不同而有所变化。一般来说,在较早的Java版本中(如Java 8及之前的版本),默认的垃圾回收器是串行垃圾回收器(Serial GC),适用于单核CPU环境。

从Java 9开始,默认的垃圾回收器是G1垃圾回收器(Garbage-First Garbage Collector)。G1垃圾回收器是一种面向服务端应用的垃圾回收器,具有高吞吐量和低停顿时间的特点,适用于大内存应用。

3.2 按作用范围分类

  1. 新生代垃圾收集(Minor GC/Young GC):只对新生代进行垃圾回收。新生代一般使用复杂算法进行收集,将存货的对象复制到另一个区域,并清理掉原区域中的无用对象。因为新生代的对象生命周期较短,所以新生代的垃圾回收频率比较高。
  2. 老年代垃圾收集Major GC/Old GC):只对老年代进行垃圾收集。老年代一般使用标记-清除(Mark and Sweep)或者标记-整理(Mark and Compact)算法进行收集,清理掉不再使用的对象。老年代的垃圾回收相对较少,但由于老年代中存放着长期存活的对象,因此垃圾收集的效率和停顿时间会影响到整个应用的性能。
  3. 混合收集(Mixed GC):对整个新生代和部分老年代收集。混合收集通常是为了提高垃圾收集的效率,将部分老年代中的对象以一并清理掉,减少老年代的内存占用。
  4. 整堆收集(Full GC):收集整个Java堆和方法区。在有些语境中,Major GC也可以用来指代整堆收集。整堆收集通常发生在老年代空间不足或元空间(在Java 8及之后的版本中)空间不足时,或者在执行显式的System.gc()方法时。

这些不同的垃圾收集类型在不同的情况下会被JVM自动触发,以维护Java应用程序的内存使用和性能。

- 下面是触发不同类型垃圾回收的一些情况:

  1. 新生代收集(Minor GC / Young GC)
    • 当新生代中的Eden区满时,触发Minor GC。在Minor GC中,会将存活的对象复制到Survivor区,并清理掉Eden区和其中的无用对象。
    • 当Survivor区无法容纳所有存活的对象时,存活较长时间的对象会被移动到老年代,而不是进行Minor GC。
  2. 老年代收集(Major GC / Old GC)
    • 当老年代的空间不足以存放新分配的对象时,会触发Major GC。Major GC会清理老年代中的无用对象,以释放空间给新的对象使用。
    • 在并发标记-清除算法中,当老年代的内存使用达到一定阈值时,会触发并发标记,然后在空闲时进行垃圾回收。
  3. 混合收集(Mixed GC)
    • 混合收集通常在老年代垃圾回收的同时,对新生代也进行部分回收。这种方式可以更均衡地处理新生代和老年代的内存回收,提高整体性能。
  4. 整堆收集(Full GC)
    • 在老年代空间不足时,或者在永久代(在Java 8之前的版本中)或元空间(在Java 8及之后的版本中)空间不足时,会触发整堆收集。
    • 显式调用System.gc()方法时,也可能触发整堆收集,但并不保证一定会执行。

4. 堆中对象的生命周期

  1. 加载阶段:当程序使用new关键字创建一个对象时,该对象会被加载堆内存中。它通常会被分配到新生代的Eden区,有一种特殊情况,大对象会直接分配到老年区。

大对象(LargeObject)通常会直接分配到老年代。在Java中,大对象是指占用内存较大的对象,例如大数组或大集合。由于大对象占用的内存较大,将其分配到新生代可能会导致频繁的内存复制和回收,影响程序的性能。
G1 垃圾回收器会根据 -XX:G1HeapRegionSize 参数设置的堆区域大小和-XX:G1MixedGCLiveThresholdPercent 参数设置的阈值,来决定哪些对象会直接进入老年代。
Parallel Scavenge垃圾回收器中,默认情况下,并没有一个固定的阈值(XX:ThresholdTolerance是动态调整的)来决定何时直接在老年代分配大对象。而是由虚拟机根据当前的堆内存情况和历史数据动态决定。

  1. 存活阶段:在对象被加载到堆内存后,如果该对象仍然被引用着,则认为该对象处于存活状态。如果对象不再被任何引用引用,则认为该对象是垃圾,将在下一次垃圾回收时被回收。
    如果对象在新生代经过一次Minor GC后仍然存活,它将被移动到新生代的Survivor区。在Survivor区中经过多次存活后,对象可能被晋升到老年代。
    • 怎么判断对象进入老年代
      • 年龄判断:在新生代中,每个对象被创建时都会被赋予一个年龄计数器。经过一次Minor GC,如果对象仍然存活,它的年龄就会增加。当对象的年龄达到一定阈值时(通常是15),就会晋升到老年代。
      • Survivor空间不足:Survivor空间用来存放新生代中的存活对象。如果Survivor空间不足以容纳对象,那么这些存活对象会被直接晋升到老年代。
      • 空间分配担保:在进行Minor GC时,如果新生代中的对象无法全部晋升到老年代,但是老年代的剩余空间不足以存放新生代中的所有存活对象时,JVM会进行一次Full GC,确保能够为新生代中的对象分配足够的空间。

3.垃圾回收阶段:当对象不再被引用时,JVM的垃圾回收器会识别并回收这些对象所占用的内存。垃圾回收的具体时间取决于垃圾回收器的策略和堆的使用情况。

  • 怎么判断对象可以被回收
    • 引用计数器:引用计数器是一种简单的方法,它通过在对象上维护一个引用计数器来记录对象被引用的次数。当引用计数器为0时,表示对象不再被引用,可以被回收。然而,引用计数器无法处理循环引用的情况,因此在Java中并没有使用这种方法。

    • 可达性分析:Java使用可达性分析来判断对象是否不再被引用。可达性分析是从一组称为"GC Roots"的根对象开始,递归地遍历所有对象的引用关系,标记所有被引用的对象为存活对象,未被标记的对象则被认为是垃圾。这样,不再被引用的对象最终会被垃圾收集器回收。

    • 在Java中,GC Roots就是根集合,包括:

      • 虚拟机栈(Java Stack)中的引用对象,即局部变量和参数。
      • 方法区(Method Area)中的类静态属性引用的对象。
      • 方法区中常量引用的对象。
      • 本地方法栈(Native Method Stack)中JNI(Java Native Interface)引用的对象。
    • 如何判断一个常量是废弃常量?
      假如在字符串常量池中存在字符串 “abc”,如果当前没有任何 String 对象引用该字符串常量的话,就说明常量 “abc” 就是废弃常量,如果这时发生内存回收的话而且有必要的话,“abc” 就会被系统清理出常量池了。

    • 如何判断一个类是无用的类?
      正常很难满足这三个条件的。

      • 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
      • 加载该类的 ClassLoader 已经被回收。
      • 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
  1. 终结阶段:在对象被回收之前,JVM会调用对象的finalize()方法进行清理和释放资源。但是,finalize()方法并不保证一定会被调用,因此不应该依赖于finalize()方法来释放资源。finalize()的调用时机是不确定的,不能保证一定会被调用
  2. 内存回收阶段:在垃圾回收阶段,如果对象经过finalize()方法后仍然未被引用,则会被回收,并释放其占用的内存空间。对于新生代的对象,会经过Minor GC;对于老年代的对象,会经过Major GC。

5. 对象的引用对垃圾回收的影响

  • 强引用、软引用、弱引用、虚引用
    在Java中,引用(Reference)是用来描述对象之间的关系的。Java提供了几种不同类型的引用,包括强引用、软引用、弱引用和虚引用,它们主要用于控制对象的可达性,从而影响垃圾回收的行为。
    1. 强引用(Strong Reference):强引用是最常见的引用类型。如果一个对象具有强引用,即使内存空间不足,垃圾收集器也不会回收这个对象。例如:

      Object obj = new Object(); // obj是一个强引用
      
      
    2. 软引用(Soft Reference):软引用用于描述那些还有用但并非必须的对象。在系统内存不足时,垃圾收集器会根据软引用的情况来决定是否回收对象。软引用可以通过java.lang.ref.SoftReference类来创建:

      SoftReference<Object> softRef = new SoftReference<>(new Object());
      
      

      例如,缓存中的对象可以使用软引用,当内存不足时,垃圾回收器可以根据软引用情况来回收缓存中的对象,从而释放内存。

    3. 弱引用(Weak Reference):弱引用比软引用更弱,只要垃圾回收器运行,无论内存是否足够,都会回收只被弱引用指向的对象。弱引用可以通过java.lang.ref.WeakReference类来创建:

      WeakReference<Object> weakRef = new WeakReference<>(new Object());
      
      

      弱引用通常用于实现一些缓存中的临时对象,可以随时被回收而不会占用太多内存。

    4. 虚引用(Phantom Reference):虚引用是所有引用中最弱的一种。一个持有虚引用的对象,和没有引用几乎是一样的,随时可能被垃圾回收器回收。虚引用主要用于在对象被回收时收到一个系统通知或执行一些清理操作。虚引用可以通过java.lang.ref.PhantomReference类来创建:

      ReferenceQueue<Object> queue = new ReferenceQueue<>();
      PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue);
      
      

      虚引用本身并不能阻止对象被回收,它的主要作用是在对象被回收时执行一些特定的操作,例如清理对象关联的资源或发送通知。
      以下是一个简单的示例,演示了如何使用虚引用和引用队列:

      import java.lang.ref.PhantomReference;
      import java.lang.ref.Reference;
      import java.lang.ref.ReferenceQueue;
      
      public class PhantomReferenceExample {
          public static void main(String[] args) {
              Object obj = new Object();
              ReferenceQueue<Object> queue = new ReferenceQueue<>();
              PhantomReference<Object> phantomRef = new PhantomReference<>(obj, queue);
      
              // 显示判断对象是否被回收
              System.out.println("Is object still alive? " + (phantomRef.get() != null));
      
              // 释放对象引用
              obj = null;
      
              // 强制垃圾回收
              System.gc();
      
              // 检查引用队列是否有引用对象
              Reference<?> refFromQueue = queue.poll();
              if (refFromQueue != null) {
                  System.out.println("Object is in the queue.");
              } else {
                  System.out.println("Object is not in the queue.");
              }
          }
      }
      
      

      在这个例子中,当对象被垃圾回收器回收时,phantomRef将会被添加到queue引用队列中,我们可以通过检查引用队列中是否有引用来判断对象是否被回收。

这些引用类型可以帮助开发人员更灵活地控制对象的生命周期和内存回收行为,特别是在处理大量数据或需要特定内存管理策略的情况下。

6. 垃圾回收算法

  1. 标记-清除(Mark and Sweep)算法
    标记-清除算法(Mark and Sweep Algorithm)是一种基本的垃圾回收算法,用于标记和清除不再被引用的对象。该算法分为两个阶段:标记阶段和清除阶段。

    • 标记阶段
      • 从根对象(如虚拟机栈中的引用对象、静态变量等)开始,遍历所有可以访问到的对象,并标记这些对象为活动对象。
      • 对于无法访问到的对象,即不可达对象,将其标记为待清除对象。
    • 清除阶段
      • 遍历堆内存中的所有对象,将标记为待清除的对象进行清除操作,即回收其所占用的内存。

    标记-清除算法的优点是简单高效,但也存在一些缺点:

    • 内存碎片问题:由于标记-清除算法会在清除阶段产生不连续的内存碎片,可能导致无法找到足够大的连续内存块来分配新对象。
    • 效率问题:标记和清除过程可能会耗费较长的时间,且在清除阶段会暂停应用程序的运行。
  2. 复制算法(Copying Algorithm)

    复制算法(Copying Algorithm)是一种垃圾回收算法,通常用于新生代的内存管理。它的核心思想是将内存空间分为两个大小相等的区域,通常称为"From"区和"To"区。在垃圾回收过程中,所有存活的对象都会被复制到"To"区,而非存活的对象则会被丢弃。
    复制算法的具体步骤如下:

    1. 初始分配:将新生代内存空间分为两个大小相等的区域,通常称为"From"区和"To"区。
    2. 新创建的对象首先会被分配到"From"区。
    3. 标记存活对象:从根对象开始,通过可达性分析标记所有存活的对象。
    4. 复制存活对象:将所有存活的对象复制到"To"区,同时按照对象的地址顺序依次排列,确保对象之间的地址是连续的。
    5. 更新引用:更新所有指向存活对象的引用,使其指向"To"区中的地址。
    6. 交换空间:将"From"区和"To"区的角色互换,使得下一次垃圾回收时仍然使用这两个区域,而不需要重新分配内存空间。

    复制算法的优点是简单高效,并且可以解决标记-清除算法中的内存碎片问题。但是,它也有一些缺点,主要是需要额外的内存空间来存储复制后的对象,以及在复制过程中需要暂停应用程序的运行。为了减少这些缺点,通常会将新生代划分为更多的存活区域,并使用"对象年龄"来决定何时将对象晋升到老年代。
    总结一下:
    可用内存变小:可用内存缩小为原来的一半。
    不适合老年代:如果存活对象数量比较大,复制性能会变得很差。

  3. 标记-整理算法(Mark-Sweep-Compact Algorithm)
    标记-整理算法(Mark-Sweep-Compact Algorithm)是一种垃圾回收算法,通常用于老年代的内存管理。它结合了标记-清除算法和内存整理的思想,用于解决标记-清除算法可能产生的内存碎片问题。
    标记-整理算法的主要步骤包括:

    1. 标记阶段:从根对象开始,通过可达性分析标记所有存活的对象。
    2. 清除阶段:遍历整个堆内存,将未被标记的对象清除,即回收其占用的内存空间。
    3. 整理阶段:将存活的对象向一端移动,使得所有存活对象在内存中连续排列,从而将内存空间合并为一个大的连续空间。

    标记-整理算法的优点是可以避免内存碎片问题,使得堆内存中的空闲空间更加连续,有利于提高内存分配的效率。但是,与复制算法相比,标记-整理算法需要更多的计算和移动操作,可能会影响应用程序的性能。因此,通常只在老年代等内存碎片严重的情况下使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/484166.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

param参数

param参数介绍及使用 在 Web 开发中&#xff0c;param 是指路由中的参数&#xff0c;用于捕获 URL 中的动态部分&#xff0c;并将其作为参数传递给路由处理函数。当定义包含参数的路由时&#xff0c;可以使用 param 方法来捕获 URL 中的动态部分&#xff0c;并将其传递给路由处…

用户多部门切换部门,MySQL根据多个部门id递归获取所有上级(祖级)、获取部门的全路径(全结构名称)

背景 之前做过的项目&#xff0c;都是一个用户就一个部门的&#xff0c;现在碰到个一个用户在多个部门的需求&#xff0c;而且需要可以切换不同部门查看不同数据。 就比如说一个大公司下面有多个子公司&#xff0c;每个子公司有好多部门、子部门等等&#xff0c;然后有部分用…

chrome浏览器插件extension开发中content内容脚本和background脚本通讯

有时候我们想监听页面中的数据变化&#xff0c;然后将监听到的数据传递给background脚本处理&#xff0c;比如根据不同的数据&#xff0c;来处理不同的业务逻辑&#xff0c;存储到服务器&#xff1f;或者控制浏览器显示效果&#xff1f;都可以&#xff0c;问题的重点是怎么让co…

口袋条件下的Lead优化几何深度模型-Delete 评测

Delete (Deep lead optimization enveloped in protein pocket) 是一个基于口袋的&#xff0c;3D分子生成的&#xff0c;应用于lead优化过程中侧链修饰、骨架跃迁&#xff0c;linker设计&#xff0c;片段生长的几何深度学习模型。 一、模型介绍 Delete 模型是浙江大学侯廷军老…

第一篇:概述、 目录、适用范围及术语 --- IAB/MRC《增强现实(AR)广告(效果)测量指南1.0 》

第一篇&#xff1a;概述、目录、适用范围及术语 - IAB与MRC及《增强现实广告效果测量指南1.0》 --- 我为什么要翻译美国IAB科技公司系列标准 ​​​​​​​​​​​​​​ 翻译计划 第一篇概述—IAB与MRC及《增强现实广告效果测量指南》之目录、适用范围及术语第二篇广告效…

STM32通信协议

STM32通信协议 STM32通信协议 STM32通信协议一、通信相关概念二、通信协议引脚作用三、通信方式四、采样方式五、电平信号六、通信对象 一、通信相关概念 通信接口 通信的目的&#xff1a;将一个设备的数据传送到另一个设备&#xff0c;扩展硬件系统 通信协议&#xff1a;制定…

Mapmost Alpha —— 数字孪生创作新引擎,一键开启未来视界!

文章目录 一、关于 Mapmost Alpha1.1 Mapmost Alpha 应用创作工具1.2 Mapmost 数字孪生平台 二、Mapmost Alpha 产品能力与平台优势2.1 Mapmost Alpha 产品能力2.1 Mapmost Alpha 平台优势 三、Mapmost Alpha 成功案例3.1 美食地图3.2 同城活动3.3 智慧商业楼宇管理3.4 智慧交通…

C++之移动语义与智能指针

目录 移动语义 1、几个基本概念的理解 2、复制控制语义的函数 3、移动控制语义的函数 3.1、移动构造函数: 3.2、移动赋值函数 4.区别 5、std::move函数 6.代码演示: 资源管理与智能指针 一、C语言中的问题 二、C的解决办法(RAII技术)&#xff1a; 三、四种智能指针…

水果检测15种YOLOV8

水果检测15种YOLOV8&#xff0c;只需要OPENCV&#xff0c;采用YOLOV8训练得到PT模型&#xff0c;然后转换成ONNX&#xff0c;OPENCV调用&#xff0c;支持C/PYTHON/ANDROID开发

宝塔面板优惠券在哪里领取?

随着科技的飞速发展&#xff0c;网站搭建和管理变得越来越简单。宝塔面板作为一款优秀的服务器管理面板&#xff0c;深受广大站长用户喜爱。为了帮助站长们更好地使用宝塔面板&#xff0c;本文将为大家详细介绍如何领取宝塔面板优惠券。 一、宝塔面板优惠券领取入口 领取入口&a…

Ubuntu Desktop 安装谷歌拼音输入法

Ubuntu Desktop 安装谷歌拼音输入法 1. Installation1.1. 汉语语言包​1.2. 谷歌拼音输入法1.3. 安装语言包1.4. 键盘输入方式系统1.5. 重启电脑1.6. 输入法配置 2. configuration2.1. Text Entry Settings… 3. ExecutionReferences 1. Installation 1.1. 汉语语言包 strong…

LeetCode刷题日志-34在排序数组中查找元素的第一个和最后一个位置

思路&#xff1a;用两次二分查找&#xff0c;分别查找指定元素的第一个和最后一个位置。 直接看代码 // 两次二分查找&#xff0c;分开查找第一个和最后一个 class Solution {public int[] searchRange(int[] nums, int target) {int[] reslut new int[]{-1, -1};int left 0…

virtualbox导入vdi

新建虚拟机 点击新建 输入新建属性 配置cpu和内存 虚拟硬盘 这里选择已有的vdi文件 摘要 这里点击完成 虚拟机添加成功 点击启动,启动虚拟机 注意 这个时候的ip,还是以前镜像的ip,如果两个镜像一起启动

双系统安装03--在已有麒麟KOS基础上安装Windows10

原文链接&#xff1a;双系统安装03–在已有麒麟KOS基础上安装Windows10 Hello&#xff0c;大家好啊&#xff01;继我们之前讨论的关于双系统安装的系列文章之后&#xff0c;今天我将带给大家这个系列的第三篇——在已有的麒麟桌面操作系统上安装Windows 10。对于想要在使用麒麟…

提升水库大坝安全与效率:现代技术云平台的应用

在我国&#xff0c;水库大坝的数量居世界之首&#xff0c;它们在推动国民经济发展中扮演着不可或缺的角色。然而&#xff0c;要想让这些水利工程充分发挥其价值&#xff0c;不仅需要精准的调度与高效的管理&#xff0c;更重要的是要确保其安全无虞。一旦发生事故&#xff0c;后…

阿里云倚天服务器是什么?倚天服务器c8y、g8y和r8y详细介绍

阿里云倚天云服务器CPU采用倚天710处理器&#xff0c;租用倚天服务器c8y、g8y和r8y可以享受优惠价格&#xff0c;阿里云服务器网aliyunfuwuqi.com整理倚天云服务器详细介绍、倚天710处理器性能测评、CIPU架构优势、倚天服务器使用场景及生态支持&#xff1a; 阿里云倚天云服务…

海外媒体发稿:7款爆款标题生成器解析-华媒舍

科普文章是将科学知识普及给大众的一种方式&#xff0c;而一个引人入胜的标题往往是吸引读者的第一步。在科普领域中&#xff0c;标题的吸引力至关重要。以下将介绍7款风靡科普界的爆款标题生成器&#xff0c;帮助你创作出引人入胜的科普文章。 1. 情感引爆 情感是人类行为中的…

CSS时钟案例

文章目录 1. 演示效果2. 分析思路3. 代码实现 1. 演示效果 2. 分析思路 背景是表盘&#xff0c;不用自己制作然后用CSS的定位做时针&#xff0c;分针和秒针黑点用伪元素::after生成转动用animation实现 3. 代码实现 <!DOCTYPE html> <html lang"en">&…

【数据结构】快速排序(用递归)

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解快速排序&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 目录 一. 基本思想二. 快速排序2.1 hoare版本2.2 挖坑法2.3 前后指针法2.4 快速排序优化三数取中法…

弥合认知差距,扫除IT服务交付中的“拦路虎”

文章目录 编辑推荐内容简介作者简介作者简介&#xff1a;译者简介&#xff1a; 精彩书评目录 按需交付服务从来都不容易。成功的交付是以一种符合客户预期的一致性、可靠性、安全性、隐私性和成本效益的方式交付客户所需的服务。无论服务提供商提供的是 IT 服务&#xff0c;还是…