Java 垃圾回收

文章目录

  • 1 Java 垃圾回收
    • 1.1 JVM
    • 1.2 Java 对象生命周期
  • 2 如何判断一个对象可被回收
    • 2.1 引用计数算法
    • 2.2 可达性分析算法
  • 3 垃圾回收过程
    • 3.1 总体过程
    • 3.2 为什么要进行世代垃圾回收?
    • 3.3 分代垃圾回收过程

在 C 和 C++ 中,许多对象要求程序员声明他们后为其分配资源,然后才能安全地使用对象。使用完后,则需要程序员将这些资源释放到自由内存池。如果资源得不到释放,则认为代码泄露内存。然而,如果过早地释放,又可能发生数据丢失、Null指针等问题。

Java 和 C# 都有单独的管理应用程序管理对象的生存期并进行垃圾回收 (Garbage Collection - GC) ,这样程序员可以不再关心内存释放问题,保证系统的性能和稳定性。

这里记录 Java 垃圾回收相关知识点,后面会再记录 C# 垃圾回收。

部分借鉴网络一些总结文章

1 Java 垃圾回收

Java 中,JVM 通过跟踪已经分配资源的引用来释放不再使用的堆存。只要 JVM 检测到对象不再被引用,垃圾回收器就会在适当的时候回收对象。

1.1 JVM

下载 Java 的时候,我们会得到 JRE (Java Runtime Environment),JRE 中包括了 JVM (Java Virtual Machine) 和 类库 (Java platform core classesJava platform libraries)。

Java 语言的一个重要特点就是与平台无关,即得益于 JVM 的引入,Java在不同平台上运行时不需要重新编译,秩序生成在 JVM 上运行的目标代码,就可以多平台运行。
在这里插入图片描述

JVM 定义了程序执行期间是用的数据区域,如程序计数器、堆、栈、方法区、运行时常量池等。这些数据区域中的一些在JVM启动时创建,JVM退出时销毁。

1.2 Java 对象生命周期

Java 中,对象的生命周期包括:
在这里插入图片描述

public class ObjectLifecycle {
    public static void main(String[] args) {
        ObjectLifecycle obj = new ObjectLifecycle();  // 创建阶段
        // 使用阶段
        obj = null;  // 不可达阶段
        System.gc();  // 触发垃圾回收,进入回收阶段
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        System.out.println("垃圾回收前调用finalize方法");
    }
}

2 如何判断一个对象可被回收

2.1 引用计数算法

给对象添加一个引用计数器,对象每增加一个引用,计数器加1,引用失效计数器减1,引用计数为0的对象可以被回收。

假如两个对象互相引用,那么引用计数器永远不会为0,导致对象无法回收,因此 JVM 不再使用引用计数算法:

public class ReferenceCountingGC {

    public Object instance = null;

    public static void main(String[] args) {
        ReferenceCountingGC objectA = new ReferenceCountingGC();
        ReferenceCountingGC objectB = new ReferenceCountingGC();
        objectA.instance = objectB;
        objectB.instance = objectA;
    }
}

2.2 可达性分析算法

通过 GC Roots 作为起始点进行搜索,所有不可达的对象即为可被回收的对象。
在这里插入图片描述
Java 虚拟机使用该算法来判断对象是否可被回收,在 Java 中 GC Roots 一般包含以下内容:

在虚拟机栈(栈帧中的本地变量表)中引用的对象:

Java public void method() { Object localVariable = new Object(); // localVariable是GC Roots }

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

Java public class MyClass { private static Object staticObject = new Object(); // staticObject是GC Roots }

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

Java public class MyClass { private static final String CONSTANT_STRING = “constant”; // CONSTANT_STRING是GC Roots }

在本地方法栈中JNI(即通常所说的Native方法)引用的对象:

Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。

所有被同步锁(synchronized关键字)持有的对象:

Javapublic synchronized void synchronizedMethod() { // 当前对象(this)在执行同步方法时是GC Roots }

所有被同步锁(synchronized关键字)持有的对象:

3 垃圾回收过程

3.1 总体过程

Step1. 标记 (Marking)
GC 在这个阶段辨别内存是否被使用,被引用的对象这里展示为蓝色,没有被引用的为橙色。这个过程效率不高
在这里插入图片描述

Step2: 标准清理 (Normal Deletion)
清理时,直接把可以回收的对象内存释放,留下被引用的对象和指向可用空间的指针。内存碎片会导致没有足够连续的空间给大对象分配内存
在这里插入图片描述
Step3: 压缩清理 (Deletion with Compacting)

通过把被引用的对象都移动到一起,后续为新对象分配内存则会更加容易和快速。
在这里插入图片描述

3.2 为什么要进行世代垃圾回收?

如上所述,标记和压缩 JVM 中所有对象是相当低效的,随着分配的对象越来越多,对象表越来越到,导致垃圾回收时间越来越长。同时,又有大量实证表明,大多数对象生命都是短暂的。

下面是数据示例图,Y周显示分配的字节数存活时间,X是随着时间推移分配的字节数。

即随着时间推移,分配的对象越来越少对。
在这里插入图片描述
因此,从对象分配的行为中,为了提高JVM 性能,堆被分为了更小的部分(或成为代),分别是:新生代(Young Gneration)、老年代 (Old Generation) 和 永久代(Permanent Generation).
在这里插入图片描述
新生代:新对象首先被分配到这里,当年轻代内存用尽,会触发一次 minor garbage colletion。如果对象的死亡率高,那么这次 minor garbage collection 则会有效的优化内存。新生代会被分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用一块 Eden 和一块 Survivor。回收时,会将 Eden 和 Survivor 中还存活得对象复制到另一块 Survivor空间中,最后清理 Eden 和使用过的那一块 Survivor。

这个过程会快速地完成,当另一块 Survivor不足以存放 Eden 和 Survivor 存活对象时,所有幸存对象 (surviving objects) 会被标记年龄并移动到 old generation

Minor GC - Stop the world EventMinor GCStop the world 的行为,即所有的应用县城都要停止,直到整个操作完成。

老年代: 一般来说,新生代对象的age达到一定的阈值,就会被移动到老年代,而老年代的回收称为 major garbage collection

Major GC - Stop the world Event: Major GC 也是 Stop the world 的行为,通常会更慢一些,所以对于响应式应用程序,应该尽量减少这类GC,由于 Java 虚拟机提供了多种垃圾回收器,这个GC的长度受垃圾回收器所影响。

永久代: 这里面包括了 JVM 描述应用进程中是用的类和方法所需要的静态文件。永久代也被称为方法区,方法区的回收内容主要是 废弃常量和无用的类。

3.3 分代垃圾回收过程

上面我们理解了为什么要分代回收和回收的基本操作,下面展示具体回收过程:

1. 一个新的对象分配内存并记录在 Eden 区域 在这里插入图片描述
2. 当 Eden 区域占满,触发一次 minor GC
在这里插入图片描述
3. 被引用对象移动到第一块 Survivor 区域(S0),没有被引用的对象则被删除
在这里插入图片描述
4. 在下一次 Minor GC,类似上一步没有被引用的对象被释放,Eden 和 引用对象则会被一起移动到第二块 Survivor 区域(S1),移动后,EdenS0 被清空,可以看到现在 S1 区域有不同age的对象。
在这里插入图片描述

5. 下次 Minor GC,同样的流程重复。但是这次 S0S1 互换。引用的对象会被移动到 S0,幸存的对象 age 继续增加,EdenS1 都被清空。
在这里插入图片描述
6. 经过几次 Minor GC 之后,对象age达到阈值(这里为8),他们就会被移动到老年代
在这里插入图片描述
7.随着Minor GC 不停地进行,幸存的对象持续promote到老年代
在这里插入图片描述
8. 随着老年代 的对象不断增多,最终会触发 Major GC 来释放更多的内存空间。
在这里插入图片描述

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

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

相关文章

微电子加速迈向后摩尔时代!复旦大学梅永丰课题组集成 DNN 与纳米薄膜技术,精准分析入射光角度

「二维纳米膜自组装成三维微结构」被认为是制造下一代微电子器件的重要途径,对于即将到来的先进电子和光电子应用至关重要。然而,二维纳米膜最终几何形状的形成受到蚀刻轨迹、化学反应、高宽比以及其他复杂因素影响,导致自组装器件在制造过程…

【调试笔记-20240604-Linux-为 OpenWrt LuCI 界面添加多语言支持】

调试笔记-系列文章目录 调试笔记-20240604-Linux-为 OpenWrt LuCI 界面添加多语言支持 文章目录 调试笔记-系列文章目录调试笔记-20240604-Linux-为 OpenWrt LuCI 界面添加多语言支持 前言一、调试环境操作系统:Ubuntu 22.04.4 LTS编译环境调试目标 二、调试步骤预…

【机器学习】GBDT (Gradient Boosting Decision Tree) 深入解析

🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 ​💫个人格言: "如无必要,勿增实体" 文章目录 GBDT (Gradient Boosting Decision Tree) 深入解析引言一、GBDT基础理论1.1 梯度…

SpringBoot整合Skywalking

下载Java Agent 官网:https://skywalking.apache.org/downloads/ 提示:Agent最好到网上找一找之前的版本,新版本可能有bug,如果出现了并且网上也几乎没有这个版本的解决方法那么就切换之前的版本 本地启动时 -javaagent:d:\opt\…

全志D1s软件入门之Tina Linux编译教程

编译 Tina Linux 在搭建好编译环境并下载好源码后,即可对源码进行编译,编译打包好后,即可将打包好的固件烧写到设备中去。本文主要介绍编译和烧写的方法。 Tina Linux 编译 Tina Linux 的编译大致分为以下流程: (1) source bu…

php7.3安装phalcon扩展

php7安装3.4版本的phalcon扩展 适用于Centos6.x和Centos7.x系统,php使用7.1版本,wlnmp一键包已支持该扩展 phalcon扩展包地址:https://github.com/phalcon/cphalcon (git clone 有可能连接不上) 1、安装所需依赖&a…

《深入浅出存储引擎》不同数据库背后的数据存储方案

在大数据和AI时代,数据库成为各类应用不可或缺的重要组成部分。而数据库中的数据依赖存储引擎进行管理,包括数据的存储、查询、更新和删除等。因此,在设计系统时,选择正确的数据库存储引擎方案变得尤为重要。这篇文章将以关系型、…

Intersection Observer API---交叉观察器 API

Intersection Observer API 交叉观察器 API(Intersection Observer API)提供了一种异步检测目标元素与祖先元素或顶级文档的视口相交情况变化的方法,例如,可以观察判断一个div(有大小的)盒子在是否出现在窗口&#xff…

贰[2],VisionMaster/.NetCore的WPF应用程序调用控件

1,环境 VisionMaster4.2 VisualStudio2022 WPF/.Net6.0 2,记录原因 .NetFrameWork的WPF应用程序调用添加例程.NetFrameWork的Winform应用程序相应的库,不会出现报错,界面也能正常显示操作,但是.NetCore的程序却总是报错。 2.1,.NetFrameWork的WPF应用程序 注:但是.…

uniadmin引入iconfont报错

当在uniadmin中引入iconfont后,出现错误: [plugin:vite:css] [postcss] Cannot find module ‘E:/UniAdmin/uniAdmin/static/fonts/iconfont.woff2?t1673083050786’ from ‘E:\UniAdmin\uniAdmin\static\fonts\iconfont.css’ 这是需要更改为绝对路径…

线性代数|机器学习-P2 A的列向量空间

文章目录 1. Ax矩阵的形式2. ACR 矩阵分解2.1 rank1 矩阵分解2.2 rank2 矩阵分解2.3 ACMR,求M 3. Ax 向量 1. Ax矩阵的形式 假设我们有如下矩阵A: A x [ 2 1 3 3 1 4 5 7 12 ] [ x 1 x 2 x 3 ] \begin{equation} Ax\begin{bmatrix} 2&1&3\\\\ 3&1&am…

Java 还能不能继续搞了?

金三银四招聘季已落幕,虽说行情不是很乐观,但真正的强者从不抱怨。 在此期间,我收到众多小伙伴的宝贵反馈,整理出132道面试题,从基础到高级,有八股文,也有对某个知识点的深度解析。包括以下几部…

5 种技术,可用于系统中的大数据模型,而不会使系统崩塌

文章目录 一、说明二、第一种:批量大小三、第二种:主动学习四、第三种:增加代币数量五、第四种: 稀疏激活六、第五种:过滤器和更简单的模型后记 一、说明 以下是本文重要观点的摘要。阅读它以获取更多详细信息/获取原…

自动装车系统车辆定位-激光雷达解决方案

在自动装车系统中,激光雷达为车辆定位提供了一种高效且精确的解决方案。以下是关于这一解决方案的详细分析: 一、解决方案概述 激光雷达解决方案在自动装车系统中,通过发射激光束并接收目标反射回来的信号,来探测车辆的位置、状…

新手如何正确使用代理IP,一篇文章学会,包含实战案例

前言 一、代理IP1.1 什么是代理IP?1.2 代理ip分类1.3 代理IP的作用和优势 二、更换代理IP的方法2.1 重启路由器或光猫2.2 用拨号 vps 重拨更换动态IP代理。2.3 使用浏览器更换IP 三、IPIDEA代理的优势四、提取代理IP4.1 提取步骤4.2 浏览器使用代理IP 五、使用代理I…

【Redis数据库百万字详解】数据持久化

文章目录 一、持久化1.1、什么是持久化1.2、持久化方式1.3、RDB优缺点1.4、AOF优缺点 二、RDB持久化触发机制2.1、手动触发2.2、自动触发 三、RDB持久化配置3.1、配置文件3.2、配置查询/设置3.3、禁用持久化3.4、RDB文件恢复 四、RDB持久化案例4.1、手动持久化4.2、自动持久化案…

Hive3.1.2分区与排序(内置函数)

Hive3.1.2分区与排序(内置函数) 1、Hive分区(十分重要!!) 分区的目的:避免全表扫描,加快查询速度! 在大数据中,最常见的一种思想就是分治,我们可以把大的文件切割划分成…

【二进制部署k8s-1.29.4】十三、metrics-server的安装部署

文章目录 简介 一.metrics-server的安装 简介 本章节主要讲解metrics-server的安装,metrics-server主要是用于采集k8s中节点和pod的内存和cpu指标,在观察几点和pod的实时资源使用情况还是比较有用的,如果需要记录历史信息,建议采用…

层出不穷的大模型产品,你怎么选?

一:简介 关于大模型AIGC产品的选择与发展趋势,目前许多互联网公司都在不断投入资源和精力开发基于大规模模型的人工智能产品。这些产品通常能够处理更复杂的任务并提供更高质量的服务,如智能问答、自然语言处理、图像识别等。在产品选择上&am…

Linux下查看进程和端口信息

1, 根据进程名(这里是模糊查询)查看进程信息,以查看nginx进程名为例,查看所对应的进程id为19013(或者使用: ps -aux | grep nginx查看占用内存等信息) ps -ef | grep nginx 2, 根据进程id查看进程占用端口,查看对应端口为8080&…