JVM专题十:JVM中的垃圾回收机制

news2024/10/7 20:32:37

在JVM专题九:JVM分代知识点梳理中,我们主要介绍了JVM为什么采用分代算法,以及相关的概念,本篇我们将详细拆分各个算法。

垃圾回收的概念

垃圾回收(Garbage Collection,GC)确实是计算机编程中的一项重要技术,它自动化了内存管理过程,解决了手动内存管理(如在C或C++中)的复杂性和潜在错误。

在没有垃圾回收的情况下,程序员需要负责申请和释放内存,这不仅容易出错,还可能导致内存泄漏和其他资源管理问题。垃圾回收的引入,使得程序员可以更专注于程序逻辑,而不是内存细节。

正如你提到的,垃圾回收的概念最早可以追溯到1960年的Lisp语言,它是为了解决手动内存管理的繁琐和低效问题。Java语言设计时,将垃圾回收作为其核心特性之一,以简化内存管理并提高开发效率。

Java中的垃圾回收机制主要依赖于以下几个方面:

  • 自动内存管理:JVM自动追踪对象的引用情况,确定哪些对象不再被使用。
  • 垃圾收集算法:JVM使用不同的算法(如标记-清除、复制、标记-清除-整理等)来识别和回收垃圾对象。
  • 垃圾收集器:JVM提供了多种垃圾收集器,以适应不同的应用场景和性能需求。

Java的finalize方法类似于C++中的析构函数,提供了在对象被回收前执行清理操作的机会。但由于finalize的执行时机不确定,且可能影响垃圾回收的性能,因此通常不推荐依赖它来进行关键的资源回收。

总的来说,垃圾回收是现代编程语言的一个重要特性,它帮助开发者编写更安全、更高效的程序。Java作为一门高级语言,通过其垃圾回收机制,显著简化了内存管理的复杂性。

Java中的垃圾回收机制

自动内存管理

JVM自动追踪对象的引用情况,确定哪些对象不再被使用。这也是讨论垃圾回收的前提(只有知道谁是垃圾,才可以进行垃圾回收吗),常用的是引用计数法、可达性分析,垃圾判断

引用计数法

引用计数法是一种简单直接的垃圾回收算法。在这种方法中,每个对象维护一个计数器,用于记录有多少个引用指向该对象:

  1. 引用增加:当有引用指向一个对象时,对象的引用计数加一。
  2. 引用消失:当引用被清除时,对应的对象引用计数减一。
  3. 垃圾回收:当对象的引用计数为零时,表示没有任何引用指向该对象,该对象可以被垃圾回收。

优点

  • 垃圾对象可以很快被发现并回收。

缺点

  • 难以处理循环引用问题,即两个或多个对象相互引用,但对其他对象没有引用,按照引用计数法,这些对象的引用计数不为零,不会被回收。
  • 维护引用计数需要额外的开销,尤其是在大量对象和频繁更新引用的场景下。

可达性分析

可达性分析是JVM中更常用的垃圾回收算法,它基于一系列被称为GC Roots的根对象进行:

  1. GC Roots:包括Java方法栈中的局部变量静态变量本地方法栈中的引用等。
  2. 标记过程:从GC Roots开始,所有可达的对象都会被标记为存活,可达的是存活哈。
  3. 清除过程:未被标记的对象被认为是垃圾,可以被回收。

优点

  • 可以处理循环引用问题,因为可达性分析不依赖于引用计数,只要没有GC Roots可达的路径,对象就会被回收。
  • 适用于大规模的应用程序,因为它可以更精确地识别存活对象。

缺点

  • 需要暂停应用程序(Stop-the-World)来执行标记和清除过程,尤其是在Full GC时。
  • 标记和清除过程可能需要较长时间,尤其是在堆内存很大时。

STW

"Stop The World"(STW)是Java虚拟机(JVM)垃圾回收中的一个术语,指的是在执行垃圾回收时,JVM必须暂停应用程序的执行,以便在一致的状态进行内存的回收和整理。以下是STW的一些关键点:

  1. 全局一致性:STW确保了在垃圾回收期间,整个堆的状态是一致的,避免了并发执行时对内存状态的不一致性。

  2. 减少复杂性:通过暂停应用程序的执行,垃圾回收器可以简化其算法,因为不需要处理并发修改的问题。

  3. 性能影响:STW会导致应用程序的延迟增加,因为所有的应用线程都被挂起,直到垃圾回收完成。

  4. 优化目标:现代垃圾回收器的设计目标之一是减少STW的时间,以提高应用程序的响应性和吞吐量。

  5. 类型

    • Minor GC:通常很快,对新生代进行回收,STW时间较短。
    • Major GC 或 Full GC:涉及整个堆的回收,STW时间较长,对性能的影响也更大。
  6. 并发收集:一些垃圾回收器如CMS(Concurrent Mark-Sweep)和G1(Garbage-First),通过并发标记和清除阶段减少STW时间。

  7. 低延迟收集器:例如ZGC和Shenandoah,它们通过并发处理大部分GC阶段,极大地减少了STW事件。

  8. 调优:通过调整JVM参数和选择合适的垃圾回收器,可以优化STW事件,减少其对应用程序性能的影响。

  9. 监控:通过监控工具,如JConsole或VisualVM,可以观察STW事件和持续时间,以评估垃圾回收对性能的影响。

  10. 应用设计:应用程序设计时,应考虑到STW的影响,例如,避免在性能敏感的路径上进行长时间的GC。

STW是垃圾回收的一个挑战,现代JVM通过各种策略和算法来减少STW的影响,以提供更平滑的应用程序性能。

垃圾收集算法

标记-清除(Mark-Sweep)算法

标记-清除算法(Mark-Sweep)是一种基础的垃圾回收算法,如上图所示,它通过以下两个主要步骤来回收内存中的垃圾对象:

  1. 标记阶段:算法从根集合(GC Roots)开始,使用可达性分析法遍历所有可达对象,并将这些存活对象进行标记。

  2. 清除阶段:算法再次遍历内存区域,这次是为了识别那些未被标记的对象,即那些不再被引用的垃圾对象,并将它们清理掉,从而释放内存空间。

这种算法的优点在于逻辑简单,易于实现,但它也有明显的缺点,如上图回收后JVM内存图,会产生内存碎片问题:由于对象是逐个被清理的,这可能导致内存中出现许多小的空闲区域,也就是JVM内存碎片。内存碎片过多时,当程序需要分配较大的连续内存块时,可能无法找到足够的空间,即便总的空闲内存是足够的。这种情况可能导致提前触发新一轮的垃圾收集,影响程序性能。

复制(Copying)算法

复制算法(Copying)是在标记清除算法上演化而来的,用于解决标记清除算法的内存碎片问题。如上图所示:它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块,当其中一块使用完,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样就保证了内存的连续性,逻辑清晰,运行高效。

优势:

  1. 简单高效:复制算法避免了标记-清除算法中内存碎片的问题,实现简单,运行高效3。
  2. 高吞吐量:由于只处理存活对象,所以效率较高,尤其适合新生代对象存活率低的情况1。
  3. 内存分配快速:复制算法完成后,空闲内存是连续的,分配内存时只需移动堆顶指针,非常快速。

缺点:

  1. 内存利用率降低:由于需要两个内存区域,实际使用的内存只有一半。
  2. 对象复制成本:需要复制存活对象,这会带来一定的性能开销。上述缺点让人

标记-清除-整理(Mark-Sweep-Compact)算法

标记-清除-整理(Mark-Sweep-Compact)算法是一种垃圾回收算法,这个算法分为三个阶段:

  1. 标记(Mark):在这个阶段,垃圾回收器遍历所有可达对象,从根对象开始,标记所有可达的对象。这意味着垃圾回收器会识别出所有仍然在使用中的对象,以便在后续阶段不会被错误地回收。

  2. 清除(Sweep):一旦标记阶段完成,垃圾回收器将遍历堆内存,清除所有未被标记的对象。这个阶段可能会留下内存碎片,因为对象是逐个被清除的,而不是成块地清除。

  3. 整理(Compact):为了解决内存碎片问题,这个阶段会将所有存活的对象向堆的一端移动,使得它们紧密排列。然后,回收器可以简单地清理掉边界之外的所有内存。整理阶段完成后,堆内存中存活的对象是连续存放的,这有助于提高内存访问的局部性,从而提高性能。

优点:

  • 没有内存碎片:通过整理阶段,可以避免内存碎片的问题,使得内存使用更加高效。
  • 适用于老年代:由于老年代的对象存活率高,标记-清除-整理算法可以有效地管理这些对象。

缺点:

  • 效率问题:整理阶段可能需要移动大量的对象,这可能会导致相对较高的开销。
  • 暂停时间:标记和清除阶段可能会引起应用程序的暂停,尤其是在处理大量对象时。

在实际的JVM实现中,标记-清除-整理算法可能会有所变化,以适应不同的垃圾回收需求和优化性能。例如,某些实现可能会采用增量或并发的垃圾回收策略来减少应用程序的暂停时间。此外,现代JVM通常会结合使用多种垃圾回收算法,以适应不同内存区域的特点。

分代收集算法

分代收集算法(Generational Collection)是一个结合了多种基础垃圾回收算法的策略,它根据对象的生命周期特点,将堆内存划分为不同的区域,并对这些区域采用最适合的垃圾回收算法。

根据对象存活周期的不同会将内存划分为几块,一般是把 Java 堆分为新生代和老年代,新生代又可以分为Eden区和Survivor区,其中Survivor区又会分To与From区;这样就可以根据各个年代的特点采用最适当的收集算法,如上图所示。

在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。

老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用标记清理或者标记整理算法来进行回收。

Eden区
  • Eden区是新生代的一部分,大多数新创建的对象首先被分配到这里。
  • 据 IBM 公司之前的研究表明,有将近 98% 的对象是朝生夕死,正是由于对象的朝生夕死特性,Eden区经常需要进行Minor GC来回收垃圾对象。通过 Minor GC 之后,Eden 区中绝大部分对象会被回收,而那些无需回收的存活对象,将会进到 Survivor 的 From 区,如果 From 区不够,则直接进入 To 区。
Survivor区
  • Survivor区有两个,From和To,它们的作用是作为Eden区和Old区之间的缓冲。
  • 通过Minor GC,存活的对象从Eden区和From区复制到To区,这样可以减少直接晋升到老年代的对象数量,降低Major GC的频率。
  • 两个Survivor区的设计有助于避免内存碎片,并确保每次GC后都有一个Survivor区是空的,另一个是没有碎片的。

Survivor区的划分原因

  • 如果只有一个Survivor区,每次Minor GC后,存活对象和需要清除的对象都会混在一起,导致内存碎片问题。
  • 两个Survivor区可以在每次Minor GC后,将存活对象复制到空的Survivor区,从而保持内存的连续性。
Old区
  • 老年代占据了Java堆的大部分空间,主要用于存放长期存活的对象。
  • 由于老年代中对象的存活率高,通常使用标记-清除或标记-整理算法进行垃圾回收。

常用的垃圾回收器

上面介绍的两个小节自动内存管理和垃圾收集算法都是从理论上介绍了设计思路,具体的实现再落在具体的垃圾回收器上,下面罗列了目前主流的垃圾回收器。

Java虚拟机(JVM)提供了多种垃圾回收器,以适应不同的应用场景和性能要求。以下是一些常用的垃圾回收器及其特点:

  1. Serial GC:单线程,适合小内存和单核环境。
  2. ParNew GC:Serial的多线程版本,适合多核环境。
  3. Parallel Scavenge GC:追求吞吐量,适合后台处理。
  4. Serial Old GC:老年代单线程,使用标记-整理算法。
  5. Parallel Old GC:老年代多线程,与Parallel Scavenge配合。
  6. CMS GC:老年代并发收集,减少停顿时间,但可能产生内存碎片。
  7. G1 GC:堆分割为区域,追求低延迟和高吞吐量。
  8. ZGC:低延迟,几乎不牺牲吞吐量,适合大堆内存。
  9. Shenandoah:同样以低延迟为目标,由Red Hat开发。

每种垃圾回收器都有其特定的使用场景和优化目标。开发者可以根据应用程序的具体需求,选择合适的垃圾回收器来优化性能。随着Java技术的发展,新的垃圾回收器也在不断地被引入和优化。

小节

本章我们主要介绍了Java的垃圾机制,包括垃圾回收基本概念,重点介绍了垃圾回收机制中自动内存管理与垃圾收集算法。简短罗列了下常用见的垃圾回收器。下一篇我们见重点介绍各个垃圾回收器的特定、及适用的常见。 

最后还是提一个小问题:你们线上JVM的参数是什么,使用的是什么垃圾回收器呢?

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

视频录制软件哪个好用?5款简单好用软件推荐

在我们的日常生活中,都有哪些好用的视频录制软件?在很多场合中我们都会用电脑记录下重要的时刻。比如,在电脑上听老师讲解一道难题的方法时,怕自己会忘记,想要录制下来进行重复的观看。这时,选择一款好用的…

震惊!CURRENT_TIMESTAMP不能乱用

事情发生在签到和查询签到记录. 设置mysql时间默认值为CURRENT_TIMESTAMP可以随系统生成默认时间戳,即生成该数据的时间戳, 但是有些特殊场景要避免由mysql给我们生成默认时间: 1、首先签到成功之后返回给前端, 2、前端收到执行成功之后立马去查询签到记录, 3、发现并没有…

【软件工具】Xshell安装教程

1、安装软件:Xshell-5.0.1337p.exe,双击安装即可,可以选择安装到D盘; 2、在D盘安装完成后,将文件nslicense.dll拷贝到对应的安装目录下; 3、打开快捷方式即可打开应用软件。

“代码规范”这样做【高级前端必备软技能之一】

✨在前端开发领域,良好的代码规范是团队协作、项目可维护性的基石。最近在梳理我们团队关于代码规范相关的一些文档,顺便给大家分享一下我们是从哪些方面入手来从一个草台班子,到开发规范的高效团队。 ✨本文旨在提供一份详尽的前端代码规范指…

Linux的免交互

交互:我们发出指令控制程序的运行,程序在接收到指令之后按照指令的效果做出对应的反应。 免交互:间接的通过第三方的方式把指令传送给程序,不用直接的下达指令。 1、here document免交互 ere document免交互:是命令…

Studying-代码随想录训练营day20| 235.二叉搜索树的最近公共祖先、701.二叉搜索树中的插入操作、450.删除二叉搜索树中的节点

第二十天,二叉树part07,二叉树搜索树加油加油💪 目录 235.二叉搜索树的最近公共祖先 701.二叉搜索树中的插入操作 450.删除二叉搜索树中的节点 拓展:普通二叉树的删除方式 总结 235.二叉搜索树的最近公共祖先 文档讲解&…

在FlowUs息流,让知识库为你所用|如何打造个人知识库|如何打造企业知识库

🎉 在 FlowUs 的世界中,知识绽放出无限的可能!🚀 在当今信息爆炸的时代,知识的更新换代速度极快,我们每天都面临着海量的信息冲击。拥有一个属于自己的知识库变得至关重要。 首先,打造自己的知…

Linux_应用篇(27) CMake 入门与进阶

在前面章节内容中,我们编写了很多示例程序,但这些示例程序都只有一个.c 源文件,非常简单。 所以,编译这些示例代码其实都非常简单,直接使用 GCC 编译器编译即可,连 Makefile 都不需要。但是,在实…

解决IMX6ULL GPIO扩展板PWM7/8中的pwm0/period后卡死问题

前言 本篇文章主要是记录解决百问网论坛上面设置 IMX6ULL GPIO扩展板PWM7/8中的pwm0/period后卡死问题,如下图: 一、查看原理图,找出对应引脚 在这里我们如何确定哪个扩展口中的引脚输出PWM波呢?我们可以通过查看原理图。 查看…

【Java】解决Java报错:IllegalMonitorStateException in Synchronization

文章目录 引言一、IllegalMonitorStateException的定义与概述1. 什么是IllegalMonitorStateException?2. IllegalMonitorStateException的常见触发场景3. 示例代码 二、解决方案1. 确保在同步代码块或方法中调用wait()、notify()和notifyAll()2. 使用同步方法3. 使用…

办公人导航-上网导航,找网站,下软件,找资源!

办公人导航是一个专门为办公人员设计的实用导航网站,旨在帮助用户高效地找到各种优质的办公资源和工具。无论是需要查找办公软件、学习资源还是娱乐工具,在办公人导航上都能找到你需要的内容。 办公人导航-实用的办公生活导航网站!https://ww…

Linux内核测试技术

Linux 内核是Linux操作系统的核心部分,负责管理硬件资源和提供系统调用接口。随着 Linux 内核的不断发展和更新,其复杂性和代码规模也在不断增加。因此,确保内核的稳定性和可靠性变得尤为重要。内核测试技术是实现这一目标的关键手段。本文将…

Java使用Graphics2D画图,画圆,矩形,透明度等实现

背景 如上图,需要使用Java生成一个图片, 并以base64编码的形式返回给前端展示。 使用Graphics2D类,来进行画图,其中需要画方框、原型、插入图标、写入文字等,同时需要设置透明度等细节点 环境:Jdk17,springboot2.7.13 代码如下 有详细的注释 package com.demo;import c…

白帽子的海外第一单,750刀

国际惯例,给兄弟们看图 这是我们师傅挖国外SRC的部分赏金截图 就问你!挖国外漏洞赚美金香不香! 现在国内SRC越来越卷了,越来越多的白帽子开始挖海外漏洞赚美金。海外SRC真的比国内赏金高很多,不说高危漏洞&#xff0…

自编码器笔记

编码器解码器自编码器 先压缩特征,再通过特征还原。 判断还原的和原来的是否相等 encode data 在一个“潜在空间”里。它的用途是“深度学习”的核心-学习数据的特征并简化数据表示形式以寻找模式。 变分自编码器: 1. 首先、假设输入数据是符合正态分布…

《mnist_model.h5》在flask中加载mnist模型

一、在tensorflow中新建及保存模型 启动Jupyter Notebook 新建Notebook 代码 from flask import Flask, request, jsonify # type: ignore import numpy as np # type: ignore import tensorflow as tf # type: ignore import json from PIL import Image # type: i…

bigtop gradle 任务依赖关系

./gradlew deb 会编译ubuntu的所有deb包 任务deb会依赖17个任务,它们会按字母排序执行,如下: alluxio-deb bigtop-groovy-deb bigtop-jsvc-deb bigtop-utils-deb flink-deb hadoop-deb hbase-deb hive-deb kafka-deb livy-deb phoenix-deb …

React 19 新特性集合

前言:https://juejin.cn/post/7337207433868197915 新 React 版本信息 伴随 React v19 Beta 的发布,React v18.3 也一并发布。 React v18.3相比最后一个 React v18 的版本 v18.2 ,v18.3 添加了一些警告提示,便于尽早发现问题&a…

51单片机STC89C52RC——8.1 8*8 LED点阵模块(点亮一个LED)

目录 目的/效果 一,STC单片机模块 二,8*8 LED点阵模块 2.1 电路图 2.1.1 8*8 点阵模块电路图 2.1.2 74HC595(串转并)模块 电路图 2.1.3 芯片引脚 2.2 引脚电平分析 2.3 74HC595 串转并模块 2.3.1 装弹(移位…

强化学习专题:强化学习知识梳理(一)

2024/6/23: 前段时间有幸完成了大学期间的第一篇论文。在面试之前复盘一下关于自己论文中DQN的一些相关点。 浅谈主要区别(在线 or 离线) 首先,一切的开始是强化学习中时序差分方程,这体现了强化学习方法的优化策略。在…