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

【Java】刚刚!突然!紧急通知!垃圾回收!

【Java】刚刚!突然!紧急通知!垃圾回收!

文章目录

  • 【Java】刚刚!突然!紧急通知!垃圾回收!
    • 从C语言的内存管理引入:手动回收
    • Java的垃圾回收机制
      • 引用计数器
        • 循环引用问题
      • 可达性分析法与GC Root
        • GC Root的典型例子
      • 标记-清除算法
        • 优点
        • 缺点
      • 复制算法
        • 工作原理
        • 优点
        • 缺点
      • 标记-整理算法
        • 工作原理
        • 优点
        • 缺点
      • 应用场景
      • 分代回收机制
        • 堆内存的分代
        • 各代的垃圾回收策略
          • 新生代垃圾回收(Minor GC)
          • 老年代垃圾回收(Major GC 或 Full GC)
      • 结语

本文将先简要介绍C语言的手动内存回收机制,然后深入探讨Java的垃圾回收(GC)机制,包括引用计数器、可达性分析法、GC root、标记-清除算法、复制算法、标记-整理算法以及分代回收机制。

从C语言的内存管理引入:手动回收

在C语言中,程序员需要手动管理内存的分配和释放。这主要通过malloccallocreallocfree函数来实现:

  • malloc:用于动态分配内存。
  • calloc:类似于malloc,但它会初始化分配的内存块为零。
  • realloc:用于调整先前分配的内存块的大小。
  • free:用于释放动态分配的内存。

虽然这种手动管理提供了很大的灵活性,但也容易导致内存泄漏(未释放不再使用的内存)和悬挂指针(指向已释放内存的指针)。这就需要程序员特别小心,确保每一块动态分配的内存都能被适时释放。

例如:

img

我们使用malloc,为结点结构体的指针分配内存。

而在删除节点时,我们采用free函数来进行内存的释放。

image-20240530215713904

Java的垃圾回收机制

与C语言不同,Java提供了自动内存管理功能。Java的垃圾回收机制旨在自动回收不再使用的对象所占用的内存,从而减轻程序员的负担并提高内存管理的安全性。

引用计数器

最简单的垃圾回收机制之一是引用计数器。它通过维护每个对象的引用计数来跟踪对象是否可以被回收:

  • 引用计数增加:每当一个新的引用指向对象时,引用计数加1。
  • 引用计数减少:每当一个引用被销毁或被设置为指向另一个对象时,引用计数减1。
  • 回收对象:当对象的引用计数变为0时,说明该对象不再被使用,可以被回收。

然而,引用计数器存在一个明显的缺点,即无法处理循环引用(两个对象相互引用)。

循环引用问题

引用计数器的一大缺点是无法处理循环引用。如下示例:

class Node {Node next;
}Node a = new Node();
Node b = new Node();
a.next = b;
b.next = a;

在这个例子中,即使ab对象的引用离开了作用域,它们的引用计数器仍然不为0(因为它们互相引用),导致内存泄漏。

因此,Java并不使用这种方法作为其主要的垃圾回收策略。

可达性分析法与GC Root

Java采用的是更为先进的可达性分析法。它通过一组被称为GC Root的根对象作为起点,沿着这些根对象的引用链进行搜索。如果一个对象能从GC Root到达,那么它就是可达的(alive),否则就是不可达的,可以被回收。

  • GC Root:通常包括当前在栈中引用的对象、静态变量引用的对象以及JNI(Java Native Interface)引用的对象等。
  • 可达对象:从GC Root开始,所有可以通过引用链访问到的对象都是可达的。
  • 不可达对象:如果一个对象没有从GC Root出发的任何引用链到达,则认为该对象是不可达的,可以被回收。

image-20240531191720169

GC Root的典型例子
  • 虚拟机栈中的引用对象:如栈帧中的局部变量和输入参数。
  • 方法区中的静态引用:如类的静态属性。
  • 方法区中的常量引用:如常量池中的引用。

标记-清除算法

image-20240531191636409

标记-清除算法是最早且最基本的垃圾回收算法之一。标记-清除算法分为两个阶段:标记阶段和清除阶段。

  1. 标记阶段
    • 从GC Root集合开始,遍历对象引用图,标记所有可达的对象。
    • 标记过程通常是递归的,沿着对象引用链进行,直到所有可达的对象都被标记。
  2. 清除阶段
    • 遍历堆中的所有对象,回收未被标记的对象。
    • 未标记的对象被认为是不可达的,可以被垃圾回收器回收。

image-20240531191652410

优点
  • 简单直接:算法简单,易于实现。
  • 无需移动对象:对象在内存中的位置不会改变,减少了对象移动的开销。
缺点
  • 内存碎片:清除阶段后,未被回收的对象会在堆中留下许多空闲区域,导致内存碎片。频繁的内存碎片会降低内存分配效率。
  • 标记和清除过程需要遍历所有对象:在大堆内存中,遍历所有对象可能导致较长的暂停时间。

复制算法

工作原理

复制算法将堆内存分为两部分,通常是等大小的两个半区:From空间和To空间。垃圾回收时,仅使用其中一个半区,另一个半区作为备用空间。

  1. 分配阶段

    • 对象只在From空间中分配内存。
    • To空间为空闲的,等待垃圾回收。
  2. 复制和清理阶段

    • 从GC Root开始,遍历所有可达的对象,并将它们复制到To空间。
    • 复制过程中,保持对象的引用关系。
    • 完成复制后,From空间中的所有对象被认为是不可达的,可以被回收。
    • 交换From和To空间的角色,下一次分配和垃圾回收使用新的From空间。
  3. 示例

    假设有A块等待垃圾回收:

    image-20240531191812980

    回收之后会变成~:

    image-20240531191937607

优点
  • 无内存碎片:对象被紧凑地复制到新的空间,不会留下内存碎片。
  • 分配速度快:由于始终从一个连续的空闲区域分配内存,分配速度很快。
缺点
  • 内存利用率低:由于堆内存被划分为两个半区,同时只使用一半内存,导致内存利用率低。
  • 对象复制开销:复制对象到新的空间需要额外的开销,特别是当对象较多时。

标记-整理算法

标记-整理算法(Mark-Compact Algorithm)是一种改进的垃圾回收算法,用于解决标记-清除算法产生的内存碎片问题。它结合了标记-清除和复制算法的优点,通过整理内存来提高内存分配效率。下面将详细分析标记-整理算法的工作原理、优缺点及其适用场景。

工作原理

标记-整理算法也分为两个主要阶段:标记阶段和整理阶段。

  1. 标记阶段

    • 从GC Root集合开始,遍历对象引用图,标记所有可达的对象。
    • 这一步与标记-清除算法中的标记阶段相同,标记过程是递归的,沿着对象引用链进行,直到所有可达的对象都被标记。

    image-20240531192004795

  2. 整理阶段

    • 遍历整个堆,将所有存活的对象向一端移动(通常是堆的起始位置),保持对象之间的紧密排列。
    • 更新所有对象的引用,以反映它们的新位置。
    • 移动完成后,释放未被标记对象的内存,未被标记的对象被回收,形成一块连续的空闲区域。

    image-20240531192017499

优点
  • 无内存碎片:对象被紧密排列在一起,没有内存碎片,提高了内存利用率。
  • 高效的内存分配:由于所有存活对象被移动到堆的一端,剩下的内存是连续的,内存分配速度更快。
  • 适用于长生命周期对象:尤其适合老年代(Old Generation)的垃圾回收,因为老年代对象生命周期较长,不需要频繁移动。
缺点
  • 对象移动开销:整理阶段需要移动对象,并更新引用,增加了额外的开销,尤其是在老年代中存活对象较多时。
  • 暂停时间长:标记和整理过程会导致应用暂停,可能影响实时性要求较高的应用。

标记-整理算法减少了内存碎片化,同时避免了复制算法中需要双倍内存的缺点。

应用场景

  • 标记-清除算法:适用于内存紧张、不希望频繁移动对象的场景,如老年代(Old Generation)的垃圾回收。
  • 复制算法:适用于对象生命周期短、需要快速回收的场景,如新生代(Young Generation)的垃圾回收。JVM中的新生代垃圾回收器通常使用复制算法。
  • 标记-整理算法:对象较大且数量较多的场景,当对象较大且数量较多时,标记-整理算法可以通过紧凑排列对象,减少内存浪费,提高内存利用率。

分代回收机制

分代回收机制的核心思想是将堆内存划分为几个代,根据对象的生命周期长短来进行不同的管理和回收。大多数对象的生命周期很短,少数对象存活时间较长。通过这种划分,可以有针对性地采用不同的垃圾回收算法,提高回收效率和性能。

堆内存的分代

JVM中的堆内存通常划分为以下几代:

新生代(Young Generation)

  • Eden区:所有新创建的对象首先分配在Eden区。
  • 两个Survivor区(S0和S1):用于在新生代中进行对象复制和存活对象的管理。每次垃圾回收时,存活的对象从Eden区和一个Survivor区复制到另一个Survivor区。

老年代(Old Generation):存活时间较长、从新生代晋升的对象存放在老年代。

永久代(PermGen)或元空间(Metaspace):存储类的元数据(方法、类结构等)。在Java 8之前为永久代(PermGen),Java 8及之后为元空间(Metaspace)。

各代的垃圾回收策略
新生代垃圾回收(Minor GC)

新生代的垃圾回收频繁,通常采用复制算法(Copying Algorithm)。当Eden区填满时,触发Minor GC:

  1. 存活对象复制:存活的对象从Eden区和一个Survivor区复制到另一个Survivor区。
  2. 对象晋升:当对象经过多次Minor GC后依然存活(达到一定年龄),或Survivor区空间不足时,存活对象晋升到老年代。
  3. Eden区清空:所有存活对象复制后,Eden区和一个Survivor区被清空,另一个Survivor区保留存活对象。

Minor GC的频率较高,但由于新生代对象生命周期短,存活对象少,因此回收速度较快。

老年代垃圾回收(Major GC 或 Full GC)

老年代的垃圾回收通常采用标记-整理算法(Mark-Compact Algorithm) 或 标记-清除算法(Mark-Sweep Algorithm)

  1. 标记阶段:从GC Root开始,标记所有可达的对象。
  2. 整理阶段(标记-整理算法):将所有存活对象向一端移动,保持对象之间的紧密排列,释放未标记对象的内存。
  3. 清除阶段(标记-清除算法):回收未标记对象的内存,但可能产生内存碎片。

Major GC或Full GC的频率较低,但由于老年代对象较多且存活时间长,回收过程较慢,可能导致较长的暂停时间。

结语

Java的垃圾回收机制通过自动化内存管理,极大地减轻了开发者的负担,同时提升了程序的安全性和稳定性。尽管Java的GC机制复杂多样,但其核心思想都是为了更高效地管理内存,避免内存泄漏和碎片化。

许多现代编程语言,如Java、C#、Python等,都内置了垃圾回收机制。通过学习垃圾回收,可以更深入地理解这些语言的设计思想和实现细节。不同语言的垃圾回收机制有所不同,了解这些差异可以帮助我们在具体语言中应用最佳实践,编写高效的代码。

此后,笔者还会介绍Java中的垃圾回收器相关知识,以及有可能的JVM调优。

参考资料与文献:

20、垃圾回收算法之可达性分析法和GC Roots是什么?_哔哩哔哩_bilibili

【JVM】万字长文!深入详解Java垃圾回收(GC)机制_java gc-CSDN博客

相关文章:

【Java】刚刚!突然!紧急通知!垃圾回收!

【Java】刚刚!突然!紧急通知!垃圾回收! 文章目录 【Java】刚刚!突然!紧急通知!垃圾回收!从C语言的内存管理引入:手动回收Java的垃圾回收机制引用计数器循环引用问题 可达…...

[Algorithm][动态规划][子序列问题][最长递增子序列][摆动序列]详细讲解

目录 0.子序列 vs 子数组1.最长递增子序列1.题目链接2.算法原理详解3.代码实现 2.摆动序列1.题目链接2.题目链接3.代码实现 0.子序列 vs 子数组 子序列: 相对顺序是跟源字符串/数组是一致的但是元素和元素之间,在源字符串/数组中可以是不连续的一般时间…...

【稳定检索】2024年心理学与现代化教育、媒体国际会议(PMEM 2024)

2024年心理学与现代化教育、媒体国际会议 2024 International Conference on Psychology and Modern Education and Media 【1】会议简介 2024年心理学与现代化教育、媒体国际会议即将召开,这是一场汇聚全球心理学、教育及媒体领域精英的学术盛宴。 本次会议将深入探…...

深入了解diffusion model

diffusion model是如何运作的 会输入当时noise的严重程度,根据我们的输入来确定在第几个step,并做出不同的回应。 Denoise模组内部实际做的事情 产生一张图片和产生noise难度是不一样的,若denoise 模块产生一只带噪声的猫说明这个模块已经会…...

TransmittableThreadLocal原理

1、原理 TransmittableThreadLocal(简称TTL)是阿里巴巴开源的一个Java库,用于解决线程池中线程本地变量传递的问题。其底层原理主要是基于Java的ThreadLocal机制并对其进行扩展,以支持在父子线程间以及线程池中任务切换时&#x…...

华为昇腾310B初体验,OrangePi AIpro开发板使用测评

0、写在前面 很高兴收到官方的OrangePi AIpro开发板测试邀请,在过去的几年中,我在自己的博客写了一系列有关搭载嵌入式Linux系统的SBC(单板计算机)的博文,包括树莓派4系列、2K1000龙芯教育派、Radxa Rock5B、BeagleBo…...

GPTQ 量化大模型

GPTQ 量化大模型 GPTQ 算法 GPTQ 算法由 Frantar 等人 (2023) 提出,它从 OBQ 方法中汲取灵感,但进行了重大改进,可以将其扩展到(非常)大型的语言模型。 步骤 1:任意顺序量化 OBQ 方法选择权重按特定顺序…...

【GD32】05 - PWM 脉冲宽度调制

PWM PWM (Pulse Width Modulation) 是一种模拟信号电平的方法,它通过使用数字信号(通常是方波)来近似地表示模拟信号。在PWM中,信号的占空比(即高电平时间占整个周期的比例)被用来控制平均输出电压或电流。…...

JVM思维导图

帮助我们快速整理和总结JVM相关知识,有结构化认识和整体的思维模型 JVM相关详细知识和面试题...

Ollama+OpenWebUI+Phi3本地大模型入门

文章目录 Ollama+OpenWebUI+Phi3本地大模型入门一、基础环境二、Ollama三、OpenWebUI + Phi3Ollama+OpenWebUI+Phi3本地大模型入门 完全不懂大模型的请绕道,相信我李一舟的课程比较适合 Ollama提供大模型运行环境,OpenWebUI提供UI,Phi3就是那个大模型。 当然,Ollama支持超级…...

实战15:bert 命名实体识别、地址解析、人名电话地址抽取系统-完整代码数据

直接看项目视频演示: bert 命名实体识别、关系抽取、人物抽取、地址解析、人名电话地址提取系统-完整代码数据_哔哩哔哩_bilibili 项目演示: 代码: import re from transformers import BertTokenizer, BertForTokenClassification, pipeline import os import torch im…...

js 表格添加|删除一行交互

一、需求 二、实现 <div style"margin-bottom: 55px"><form action"" method"post" enctype"multipart/form-data" id"reportForm" name"sjf" style"margin-left: 25px;margin-bottom: 50px;&quo…...

如何选择合适的服务器硬件和配置?

业务需求 了解您的业务需求和负载。这将帮助您确定需要哪种类型的服务器&#xff08;如文件服务器、数据库服务器、Web服务器等&#xff09;以及所需的处理能力、内存、存储和网络性能。...

Prometheus + Grafana + Alertmanager 系统监控

PrometheusGrafana 系统监控 1. 简介1.1 Prometheus 普罗 米修斯1.2 Grafana 2. 快速试用2.1 Prometheus 普罗 米修斯2.2 Prometheus 配置文件2.3 Grafana 2. 使用 Docker-Compose脚本部署监控服务3. Grafana 配置3.1 配置数据源 Prometheus3.2 使用模板ID 配置监控模板3.3 使用…...

5.23R语言-参数假设检验

理论 方差分析&#xff08;ANOVA, Analysis of Variance&#xff09;是统计学中用来比较多个样本均值之间差异的一种方法。它通过将总变异分解为不同来源的变异来检测因子对响应变量的影响。方差分析广泛应用于实验设计、质量控制、医学研究等领域。 方差分析的基本模型 方差…...

rnn 和lstm源码学习笔记

目录 rnn学习笔记 lstm学习笔记 rnn学习笔记 import torchdef rnn(inputs, state, params):# inputs的形状: (时间步数量, 批次大小, 词表大小)W_xh, W_hh, b_h, W_hq, b_q paramsH stateoutputs []# 遍历每个时间步for X in inputs:# 计算隐藏状态 HH torch.tanh(torch.…...

解析Java中1000个常用类:CharSequence类,你学会了吗?

在 Java 编程中,字符串操作是最常见的任务之一。为了提供一种灵活且统一的方式来处理不同类型的字符序列,Java 引入了 CharSequence 接口。 通过实现 CharSequence 接口,各种字符序列类可以提供一致的 API,增强了代码的灵活性和可扩展性。 本文将深入探讨 CharSequence 接…...

微服务远程调用之拦截器实战

微服务远程调用之拦截器实战 前言&#xff1a; 在我们开发过程中&#xff0c;很可能是项目是从0到1开发&#xff0c;或者在原有基础上做二次开发&#xff0c;这次是根据已有代码做二次开发&#xff0c;需要在我们微服务一【这里方便举例&#xff0c;我们后面叫模版微服务】调用…...

德人合科技——天锐绿盾内网安全管理软件 | -文档透明加密模块

天锐绿盾文档加密功能能够为各种模式的电子文档提供高强度加密保护&#xff0c;丰富的权限控制以及灵活的应用管理&#xff0c;帮助企业构建更严密的立体保密体系。 PC地址&#xff1a; https://isite.baidu.com/site/wjz012xr/2eae091d-1b97-4276-90bc-6757c5dfedee ————…...

超融合架构下,虚拟机高可用机制如何构建?

作者&#xff1a;SmartX 产品部 钟锦锌 虚拟机高可用&#xff08;High Availability&#xff0c;简称 HA&#xff09;是虚拟化/超融合平台最常用、关键的功能之一&#xff0c;可在服务器发生故障时通过重建业务虚拟机以降低故障对业务带来的影响。因此&#xff0c;为了充分保障…...

生成xcframework

打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式&#xff0c;可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具

第2章 虚拟机性能监控&#xff0c;故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令&#xff1a;jps [options] [hostid] 功能&#xff1a;本地虚拟机进程显示进程ID&#xff08;与ps相同&#xff09;&#xff0c;可同时显示主类&#x…...

如何在最短时间内提升打ctf(web)的水平?

刚刚刷完2遍 bugku 的 web 题&#xff0c;前来答题。 每个人对刷题理解是不同&#xff0c;有的人是看了writeup就等于刷了&#xff0c;有的人是收藏了writeup就等于刷了&#xff0c;有的人是跟着writeup做了一遍就等于刷了&#xff0c;还有的人是独立思考做了一遍就等于刷了。…...

蓝桥杯 冶炼金属

原题目链接 &#x1f527; 冶炼金属转换率推测题解 &#x1f4dc; 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V&#xff0c;是一个正整数&#xff0c;表示每 V V V 个普通金属 O O O 可以冶炼出 …...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中&#xff0c;附加包含目录、附加库目录和附加依赖项是三个至关重要的设置&#xff0c;它们相互配合&#xff0c;确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中&#xff0c;这些概念容易让人混淆&#xff0c;但深入理解它们的作用和联…...