JVM的垃圾回收机制(GC)

news/2024/5/8 20:56:59/文章来源:https://blog.csdn.net/crazy_xieyi/article/details/127396406

系列文章目录

JVM的内存区域划分_crazy_xieyi的博客-CSDN博客

JVM类加载(类加载过程、双亲委派模型)_crazy_xieyi的博客-CSDN博客


文章目录

  • 一、什么是垃圾回收?
  • 二、java的垃圾回收,要回收的内存是哪些?
  • 三、回收堆上的内存,具体是回收什么?
  • 四、垃圾回收到底是怎么回收的?
  • 如何判定垃圾?
    • 1.引用计数
    • 2.可达性分析(java)
  • 六、已经知道哪些对象是垃圾了,具体怎么去回收呢?
    • 1.标记-清除
    • 2.复制算法
    • 3.标记整理
    • 4.分代回收

一、什么是垃圾回收?

垃圾回收,回收的是内存。JVM其实是一个进程,一个进程会持有很多硬件资源,比如:CPU,内存,硬盘,带宽资源等。系统的内存总量是一定的,程序在使用内存的时候,必须先得申请,才能使用,使用完毕后还要释放。

从代码编写的角度看,内存的申请时机是非常明确的,但是内存的释放时机在很多时候是不太明确的。这个时候就给内存的释放带来了一些困难,典型的问题就是,这个内存是否还要继续使用?

像C/C++这样的编程语言,内存释放,是纯手工的,全靠程序员手动释放。比如,在C语言中,如果malloc出来的对象,不手动调用free,那么这个内存就会一直持有。此时,内存的释放就全靠程序员自己来控制,如果忘了释放(在该释放的时候没有释放),那么很有可能会带来“内存泄漏”这样的问题。一直申请不释放,导致系统可用的内存越来越少,直到耗尽,如果再想要申请内存,就申请不到了。所以,内存泄漏,一向成为了程序员幸福感的头号杀手!

既然内存泄漏有这么大的弊端,肯定有相应的办法来反制。比如,在C++中,就采取了智能指针的方式,在java中采取的方案就是“垃圾回收”机制。对于java来说,代码中的任何地方都可以申请内存,然后由JVM统一进行释放,具体来说,就是由JVM内部的一组专门负责垃圾回收的线程来进行这样的工作的。

JVM垃圾回收的优点:

能够非常好的保证不出现内存泄漏的情况,但是也不是100%保证,就是再厉害的机制也顶不过程序猿自己瞎搞。

JVM垃圾回收缺点:

1.需要消耗额外的系统资源

2.内存的释放可能存在延时

3.可能会导致出现STW问题(stop the world)(在java的世界中,其实前辈们已经做出了很多的努力来改进这个问题,目前能够把STW控制在1ms内)

二、java的垃圾回收,要回收的内存是哪些?

JVM中的内存分为了好几个区域,有堆、方法区、栈和程序计数器。在这几个区域中,堆占据的内存空间是最大的,所以在java的垃圾回收机制中,咱们日常讨论的垃圾回收,主要是指堆上内存的回收。

三、回收堆上的内存,具体是回收什么?

 在堆上,是new出了很多的对象,此时针对堆上的对象,也分成三种:完全使用、完全不使用、一半要使用一半不使用。对于完全不使用,这就是我们要回收的东西。java中的垃圾回收,是以“对象”为基本单位的,一个对象要么被回收,要么不被回收,不会出现一个对象被回收一半的情况。

四、垃圾回收到底是怎么回收的?

垃圾回收的基本思想是先找出垃圾,再回收垃圾。一般情况下时把再也不会被使用到的对象进行垃圾回收,如果要是把正在使用的对象进行垃圾回收,这是一个非常可怕的结果。对于回收少了这样的问题来说,回收多了或回收错了显然是更严重的问题。对于GC来说,判定垃圾的原则,宁可放过,也不能错杀.........

如何判定垃圾?

单说GC的话,判定垃圾有两种典型的方案:引用计数、可达性分析

1.引用计数

在对象里面包含一个单独的计数器,随着引用增加,计数器就自增,随着引用减少,计数器就自减。

Test a = new Test();

此时认为new Test()这个对象就有一个引用指向它。

Test b = a;

此时就有a和b两个引用都指向这个对象。引用计数,就是通过一个变量来保存当前这个对象被几个引用来指向。 

Test a = new Test();
Test b = a;
a = null;
b = null

 当这两个引用都指向 null 的时候,然后这个对象就没有被指向了,此时这个对象就认为是垃圾了(引用计数为0)。

引用计数的优点:

规则简单,实现方便,比较高效(程序运行效率比较高)。

引用计数的缺点:

1.空间利用率比较低,针对大量的小对象,比较浪费空间。(比如,一个对象4个字节,也需要4个字节的计数器)

2.存在循环引用的问题(致命问题)

有些特殊的代码下,循环引用会导致代码的引用计数判断出现问题,从而无法回收。

class Test{Test t = null;
}Test t1 = new Test();
Test t2 = new Test();t1.t = t2;
t2.t = t1;

当执行下面这个操作:t1 = null; t2 = null;这一操作其实是销毁了两个引用,但是引用计数只减了1,没有了t1,就无法使用t1.t。

 很多编程语言,虽然使用了引用计数这种机制,但是实际上都是改进过的引用计数,比如(Python,PHP)。在java中,并没有使用引用计数的这种方式,而是使用第二种机制:可达性分析。

2.可达性分析(java)

从一组初始的位置出发(比如二叉树),向下进行深度遍历,把所有能够访问到的对象都标记成“可达”(可以被访问到),对应的,不可达的对象就是垃圾。

JVM中采取的方案是:在JVM中存在一组线程,来周期性的进行上述的遍历过程。不断的找出这些不可达的对象,由JVM进行回收。

把可达性分析的初始位置称为“GCRoot”,主要对栈上的局部变量表中的引用、常量池里面的引用指向的对象,方法区中引用类型的静态成员变量进行标记。和引用计数相比,可达性分析确实要稍微麻烦一点,而且同时可达性分析的遍历过程的开销是比较大的。虽然是开销比较大,但是后面会有一些优化的手段。但是可达性分析带来了好处就是解决了引用计数的两个缺点,内存上不需要消耗额外的空间,也没有循环引用的问题。

六、已经知道哪些对象是垃圾了,具体怎么去回收呢?

在java的垃圾回收机制中,有一些经典的策略/算法:标记-清除、复制算法、标记整理以及分代回收。

1.标记-清除

白色是正在使用的对象,灰色是已经被释放的空间。虽然这个过程可以释放掉不用的空间,但是引入了额外的问题:内存碎片。

如果内存中的内存碎片很多很多,那么此时你去申请一块小的内存还好,但是想要很大的一块连续内存空间,可能会申请失败。内存碎片问题,如果一直累积下去,就会导致出现系统上看起来空闲内存挺多的,但是实际上申请连续的内存空间是申请不到的。内存碎片问题,在“频繁申请释放”的场景中,尤为严重。 

2.复制算法

 为了解决内存碎片问题,就引入了复制算法。

复制算法把整个内存分为了两个部分,一次只用一个部分,1和3要被回收了, 于是就把剩下的2和4拷贝到另外一侧,然后再整体回收这一整块空间。

使用复制算法,可以非常有效的避免出现内存碎片问题。但是复制算法也有一些缺点:

可用的内存空间只有一半;如果要回收的对象比较少,剩下的对象比较多,那么复制的开销就比较大了。复制算法只适用于对象会被快速回收,并且整体内存不大的场景下。

3.标记整理

为了能够解决复制算法的内存空间利用率低的问题,就引入了标记整理的策略。标记整理,就有点类似于“顺序表删除元素的搬运过程”。

这样的操作,既可以有效的避免内存碎片,也可以提高空间的利用率。但是在这个搬运的过程中,也是一个很大的开销,这个开销比复制算法里面的复制对象的开销还要大。 

4.分代回收

在实际中的垃圾回收算法,是结合了以上的三种方式,取长补短,引入了分代回收的策略。

在分代回收中,把内存中的对象分成了几种情况,每一种情况采用不同的回收算法。

那么是如何进行分代的呢?

是根据对象的“年龄”的年龄来进行划分的。在JVM中,在进行垃圾回收扫描(可达性分析)也是周期性的,这个对象每次经历了一个扫描周期,就认为是长了一岁。就根据这个对象的年龄,就把整个内存进行划分。年龄短的放在一起,年龄长的放在一起。根据不同的年龄对象,就可以采用不同的回收算法了。

分代回收的过程:

1.一个新的对象,诞生于伊甸区。

2.如果活到一岁的对象(该对象经历了一轮GC还没回收)就拷贝到幸存区。根据经验规律,绝大部分对象都是熬不过一轮GC的,所以进入幸存区的对象不是很大,这里的拷贝开销就不是很大。

3.在幸存区中,对象也要经历若干轮GC。每一轮GC逃过的对象,都通过复制算法拷贝到另外的幸存区里。在这两个幸存区的对象经过来回拷贝,每一轮都会淘汰一批对象。

4.在幸存区中,熬过一定轮次的GC,JVM就认为这个对象未来还会更持久的存在下去,于是就把这样的对象拷贝到老年代中。

5.进入老年代的对象,JVM认为都是属于能够持久存在的对象。但是这些对象也需要使用GC来扫描,但是扫描的频次就大大降低了,老年代这里通常使用的是标记整理算法。 

其实在某些特殊情况下,如果一个对象特别大,为了避免大的开销,那么也会直接进入老年代的。

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

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

相关文章

BGP服务器

BGP服务器被称为“边界网关协议”(BGP),是一种用于在不同主机网关、 Internet或自治系统之间传输数据和信息的路由协议。 BGP是一种路径矢量协议(PVP),它维护不同主机、网络和网关的路由器的路径,并根据 BGP做出路由决定。把电信、联通、联通…

Mac 使用paralles 从零搭建hadoop集群

目录 1. 虚机的安装与配置 1.1 安装parallels 1.2 安装fedora系统 1.3 fedora的配置 1.3.1 内存和硬盘配置 1.3.2 网络配置 1.3.3 共享文件夹 1.4 虚拟机克隆 与 加载 2. 免密登录 2.1 分别查看master, slave01,slave02 的ip 2.2 查看各虚机的…

Map学习笔记——深入理解ConcurrentHashMap

ConcurrentHashMap 是我们日常开发中使用频率最高的并发容器之一了,具有如下特点: 基于JDK8分析 存储结构和HashMap一样,都是数组 链表 红黑树是线程安全的容器,底层是通过CAS自旋 sychronized 来保证的key 和 value 都不允许为空&#xf…

异步请求-AJAX

什么是同步交互 首先用户向HTTP服务器提交一个处理请求。接着服务器端接收到请求后,按照预先编写好的程序中的业务逻辑进行处理,比如和数据库服务器进行数据信息交换。最后,服务器对请求进行响应,将结果返回给客户端,返…

黑苹果之微星(MSI)主板BIOS详细设置篇

很多童鞋安装黑苹果的时候会卡住,大部分原因是cfg lock 没有关闭,以及USB端口或SATA模式设置错误。 为了避免这些安装阶段报错的情况发生,今天给大家分享一下超详细的BIOS防踩坑设置指南--微星(MSI)主板BIOS篇&#xf…

CTFHub | Refer注入

0x00 前言 CTFHub 专注网络安全、信息安全、白帽子技术的在线学习,实训平台。提供优质的赛事及学习服务,拥有完善的题目环境及配套 writeup ,降低 CTF 学习入门门槛,快速帮助选手成长,跟随主流比赛潮流。 0x01 题目描述…

天然气潮流计算matlab程序

天然气潮流计算matlab程序 1 天然气潮流计算理论 由于天然气涉及到流体的运动方程,直接计算非常复杂,因此需要提前做出一些假设来简化计算,经过研究,适当的假设对结果影响很小,因此本文对天然气系统做出如下假设&#…

MNN--初步学习

来自阿里MNN有三个贡献点: 提出了预推理机制,在线计算推理成本和最优方案优化了kernel提出后端抽象实现混合调度MNN的架构: 分离线和在线两部分。离线就是很传统的模型转换、优化、压缩、量化的那一套东西,这里mnn转出的模型文件…

springboot thymeleaf使用

导入依赖 <dependency> <groupId>org.thymeleaf</groupId> <artifactId>thymeleaf</artifactId> <version>3.0.11.RELEASE</version> </dependency> <dependency> <groupId>org.thymeleaf</groupId> <a…

人口数据集:地级市常住人口与户籍人口、人口1%抽样调查数据两大维度指标数据

一、地级市常住人口与户籍人口 1、数据来源&#xff1a;地级市常住人口数据&#xff08;主要来源于各地政府公报&#xff09;&#xff0c;户籍人口数据来源于《中国城市统计年鉴》 2、时间跨度&#xff1a;2003-2019年 3、区域范围&#xff1a;280个地级市 4、指标说明&…

牛顿法(牛顿拉夫逊)配电网潮流计算matlab程序

牛顿法配电网潮流计算matlab程序 传统牛顿—拉夫逊算法&#xff0c;简称牛顿法&#xff0c;是将潮流计算方程组F(X)0&#xff0c;进行泰勒展开。因泰勒展开有许多高阶项&#xff0c;而高阶项级数部分对计算结果影响很小&#xff0c;当忽略一阶以上部分时&#xff0c;可以简化对…

校园论坛设计(Java)——介绍篇

校园论坛设计&#xff08;Java&#xff09; 文章目录校园论坛设计&#xff08;Java&#xff09;0、写在前面1、项目介绍2、项目背景3、项目功能介绍3.1 总体设计图3.2 帖子模块3.3 学习模块3.4 个人信息模块3.5 数据报表模块3.6 校园周边模块3.7 用户管理模块3.8 登录注册模块4…

[足式机器人]Part3机构运动微分几何学分析与综合Ch02-3 平面机构离散运动鞍点综合——【读书笔记】

本文仅供学习使用 本文参考&#xff1a; 《机构运动微分几何学分析与综合》-王德伦、汪伟 《微分几何》吴大任 Ch02-3 平面机构离散运动鞍点综合2.4 鞍滑点2.4.1 鞍线与二副连架杆P-R2.4.2 鞍线误差2.4.3 三位置鞍线2.4.4 四位置鞍线2.4.5 多位置鞍线2.4.6 滑点与鞍滑点2.4 鞍滑…

问题盘点|使用 Prometheus 监控 Kafka,我们该关注哪些指标

Kafka 作为当前广泛使用的中间件产品&#xff0c;承担了重要/核心业务数据流转&#xff0c;其稳定运行关乎整个业务系统可用性。本文旨在分享阿里云 Prometheus 在阿里云 Kafka 和自建 Kafka 的监控实践。01Kafka 简介Aliware01Kafka 是什么&#xff1f;Kafka 是分布式、高吞吐…

力扣(LeetCode)88. 合并两个有序数组(C++)

朴素思想 朴素思想&#xff0c;开第三个数组&#xff0c;对 nums1nums1nums1 和 nums2nums2nums2 进行二路归并。 class Solution { public:void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {vector<int> nums3(mn);int i 0,j …

创新赋能合作伙伴,亚马逊云科技re:Invent科技盛宴

北京时间11月29号&#xff0c;亚马逊云科技年度峰会re:Invent 2022将在拉斯维加斯开幕。这场年度最重磅的云计算技术大会不仅是科技盛宴&#xff0c;也是亚马逊云科技与诸多客户交流互鉴的绝佳平台&#xff0c;今天带大家认识一下几位资深云计算用户&#xff0c;以及他们和re:I…

Pytorch 中Label Smoothing CrossEntropyLoss实现

一. 前言 一般情况下我们都是直接调用Pytorch自带的交叉熵损失函数计算loss&#xff0c;但涉及到魔改以及优化时&#xff0c;我们需要自己动手实现loss function&#xff0c;在这个过程中如果能对交叉熵损失的代码实现有一定的了解会帮助我们写出更优美的代码。 其次是标签平…

【架构设计】作为架构师你应该掌握的画图技术

1.前言 大家知道&#xff0c;架构的过程其实就是建模的过程&#xff0c;那自然离不开架构图。那么&#xff0c;我们先来看几个问题。 &#xff08;1&#xff09;什么是架构图&#xff1f; 架构图 架构 图&#xff0c;用图的形式把系统架构展示出来&#xff0c;配上简单的文…

基于C#的校园闲置物品共享系统的开发和实现(Asp.net+Web)

目 录 摘 要 I Abstract II 第1章 绪论 1 1.1选题背景 1 1.1.1校园闲置物品共享系统的开发背景 1 1.1.2学生闲置物品交易活动的现状 1 1.2 校园闲置物品共享系统的研究方向和内容 1 1.2.1研究方向 1 1.2.2研究内容 2 1.3 校园闲置物品共享系统的设计目标 2 1.4 校园闲置物品共…

多云加速云原生数仓生态,华为与 HashData 联合打造方案

多云的兴起&#xff0c;源于用户应用对于基础设施、云服务功能、安全性等的差异化需求&#xff0c;用户希望根据需求将应用、数据因“云”制宜&#xff0c;实现业务的高度灵活性和高效性。这也直接驱动着云原生数据仓库等一批云原生应用的流行&#xff0c;以及存储等基础设施加…