JVM垃圾回收机制

news2024/11/15 13:35:34

目录

目录

前言

一. GC (垃圾回收机制)

STW问题

二. GC 回收哪部分内存

三. 具体怎么回收

1. 先找出垃圾

a.引用计数

b. 可达性分析

2. 回收垃圾

a. 标记清除

b. 复制算法

c. 标记整理

d. 分代回收


前言

我们都知道 Java 运行时内存的各个区域. 对于程序计数器 虚拟机栈 本地方法栈 这三部分区域而言, 他的生命周期与相关线程有关, 随线程而生, 随线程而灭. 并且这三个区域的内存分配与回收具有确定性, 因为当方法结束或者线程结束时, 内存就自然跟着线程回收了.

那我们的 JVM 到底是怎么进行回收的呢? 又是怎么判断需要回收的呢?

下面我们来看看.

一. GC (垃圾回收机制)

在开始学习C语言的时候, 我们知道, 在C语言里, 创建内存有两种方式:

  1. 直接定义变量, 变量就对应了内存空间. (出来作用域, 内存释放的时机是确定的)
  2. malloc 申请内存 (动态内存申请), 还必须要通过 free 来进行内存释放, 如果不手动进行内存释放的话, 这块内存会一直持续到程序结束.

而手动释放, 问题就来了, 我们很有可能会忘记, 这就会造成内存泄露.

这个时候大佬程序猿们就想出了 GC(垃圾回收), 这是个主流方案.

程序猿只需要复制申请内存, 释放内存的工作, 就交给 JVM 来完成. JVM 会自动判定当前的内存是否需要释放, 如果 JVM 认为这个内存不需要使用了, 那么就将他释放掉.

STW问题

GC 中最大的问题, STW问题(Stop the world)

这个问题就好像, 你在家打LOL, 马上打团了, 你老妈叫你给他拿个东西给他, 你给她拿东西这段时间肯定不能操作打团啊, 等你拿完东西回来, 黄花菜都凉了, 团战输了都.

反应在用户这里的话, 就是会造成卡顿的现象.

二. GC 回收哪部分内存

JVM主要内存分为: 堆 方法区 栈 程序计数器这四个部分.

其中, GC 主要就是针对 堆 来进行垃圾回收, 方法区的话加载类对象, 加载之后也不太会回收, 对于栈来说的话, 他释放的时机是确定的, 也不需要回收, 对于程序计数器, 他有固定的内存空间, 也不需要回收.

那个红色的圆圈代表, 这个对象, 一半的内存正在使用, 一半的内存不再使用了. 这种情况不会回收, 会等待这个对象彻底不用了, 才会进行回收!!

GC 回收内存, 不是以 "字节" 为单位, 是以 "对象" 为单位.

三. 具体怎么回收

1. 先找出垃圾

如果一个对象再也不用了, 就说明是垃圾了. 在 Java 中, 对象的使用, 需要凭借 引用. 假设一个对象, 已经没有任何引用指向它了, 那么这个对象就无法再被使用了, 就能被回收了.

有两种判断对象是否存在引用的方法:

a.引用计数

引用计数法不是JVM采用的方法, Python 和 PHP 用的这个方法.

引用计数描述的方法: 给每个对象增加一个引用计数器, 每当有一个地方引用它时, 计数器就+1; 当引用失效时, 计数器就-1; 任何时刻计数器为 0 的对象就是不能再被使用的, 即对象已 "死".

很容易的就能看出引用计数的优点: 简单, 容易实现, 执行效率也高.

这么一看, 好像这方法很不错啊, 那为什么我们的JVM不采用这种方法呢?

最主要的原因就是引用计数法无法解决对象循环引用的问题, 次要原因是空间利用率低, 尤其是小对象.

举个例子: 比如计数器是个int, 如果你的对象本身里面只有一个int成员, 占4个字节就可以了, 但是现在还要存一个计数器, 需要8个字节了, 如果当对象数量过大的时候, 这样就会浪费很多内存空间. 

来看下面这段代码:

class Test {
    Test x;
}

Test A = new Test();
Test B = new Test();

现在内存空间里, 是这样的, 下面我们进行这些动作: 

a.x = b;
b.x = a;

那么现在, 计数器对应的就是两次引用.

 再然后我们这么做:

a = null;
b = null;

它本身对应的值就变成了null, 它所对应的引用计数也会-1, 变成1.

到这个时候, 虽然这俩对象引用计数唯一, 但是实际上是两个对象在相互引用, 此时, 外界的代码无法访问和使用这俩对象的, 而又由于引用计数不为0, 这俩对象又无法进行释放, 问题就出现了.

b. 可达性分析

JVM采用的这个方法.

约定一些特殊的变量, 成为 "GC roots". 每隔一段时间, 从 GC roots 出发, 进行遍历, 看当前那些变量能被访问到, 能被访问到的变量就称为 "可达", 否则称为 "不可达".

GC roots 呢, 在Java里面约定了这些:

  1. 栈上的变量.
  2. 常量池引用的对象.
  3. 方法区里, 引用类型的静态变量.

就像二叉树一样, 知道了根节点之后, 我们可以顺藤摸瓜找他的子节点. root.left 就能访问到 B, root.right 就能访问到 C. 通过这种 root. 的方式访问到的对象就是可达的, 假设这个时候我们把 A 和 C之间的连接断开, 那么 root.right 就访问不到C了, 此时C 和 F 就不可达了, 就是垃圾了. 

找到垃圾之后我们下面我们来看具体怎么回收垃圾的.

2. 回收垃圾

a. 标记清除

 假设现在有这样一块内存空间, 其中标记颜色的就是垃圾.

标记出垃圾之后, 直接把对象对应的内存空间进行释放了, 蓝色的部分就是已经释放的内存空间.

这样的方法最大的问题就是: 会产生内存碎片.

例如上图, 假设每个蓝色的区域是1k的空间, 这个时候就有4k的空闲空间, 但是由于空间是分散的, 我们申请就不能申请例如2k啊  3k的连续内存空间.

b. 复制算法

复制算法就是针对上述所说的内存碎片问题引入的方法了.

假设有这么一块内存区域, 他被划分成了两半, 其中标号的就是对象, 我们使用一侧空间的时候, 另一半空间是不使用的, 在垃圾回收的使用才使用.

假设我们使用了2号和4号内存空间之后需要回收这两块空间, 这个时候就不再是原地释放内存占用了, 而是先把"非垃圾" (还能继续使用的对象, 也就是图中未标色的三个区域) 拷贝到另一侧, 然后再把之前整个这一半给释放掉.

 这样内存碎片的问题就得到了妥善的解决, 但是呢, 复制算法的缺点一样很明显, 空间利用率更低了(用一半丢一半), 如果一轮 GC下来, 大部分的对象要保留下来, 只有少数几个对象要回收, 这个时候拷贝的开销就很大了.

c. 标记整理

 类似于 顺序表 删除元素进行的搬运操作.

假设还是蓝色的区域是需要清除的垃圾, 这里进行搬运操作就是清除垃圾区域, 然后把 "非垃圾" 区域搬运到前面区. 如下图:

 这个方法相对于上述的复制算法来说, 空间利用率提升了, 并且也能解决内存碎片的问题, 但是搬运操作也是毕竟耗费时间的.

d. 分代回收

前面的三种方法都存在一点问题, 这个时候就引入了 "分代回收" 策略, 把上面的方法综合了一下, 根据对象不同的特点, 来采取不同的回收方式.

根据对象年龄来划分的.

依据 GC 的轮次来计算的, 假设在一组线程里, 周期性的扫描代码里的对象, 如果一个对象, 经历了一次GC, 没有被回收, 就认为是年龄 +1. 

基于上述内容, 就针对对象的年龄进行了分类, 把堆里的对象分成了 新生代(年龄小的对象), 老年代(年龄大的对象). 对于新生代 GC 扫描的频率更高, 而对于老年代 GC 扫描的的频率要低一些.

规则:

 刚创建出来的新对象, 就进入伊甸区, 如果这些新对象经过一轮GC, 还没被回收, 就通过复制算法, 复制到 生存区 里去. 生存区里的对象呢, 没经过一轮GC, 继续通过复制算法拷贝到另外一个生存区里去, 只要这个对象不消亡, 那么就会在这个两个生存区之间来回拷贝, 反复横跳, 但是我们要知道的是, 每一轮拷贝, 没一轮GC都是会筛选掉一大波对象的. 如果一个对象在生存区反复横跳了很多次还没回收的话, 那么就会进入 老年代了, 进入老年代之后也会继续定期进行GC, 只是频率变低了, 这里采取的是标记整理的方法来清除老年代的对象了.

然后上述规则还有一个特殊情况, 如果对象是一个非常大的对象, 那么就直接进入老年代, 原因很简单, 你耗费这么大力气创建的对象, 肯定不是立即销毁的. 

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

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

相关文章

数据库与身份认证:在项目中操作 MySQL

在项目中操作数据库的步骤 ①安装操作 MySQL 数据库的第三方模块(mysql) ②通过 mysql 模块连接到 MySQL 数据库 ③通过 mysql 模块执行 SQL 语句 安装与配置 mysql 模块 1. 安装 mysql 模块 mysql 模块是托管于 npm 上的第三方模块。它提供了在 Nod…

智能wifi小车-RGB三色LED灯驱动

RGB三色LED灯简介 RGB指的就是三基色光,R红色,G绿色,B蓝色。LED芯片所发出的光一般都是蓝光,都是要通过红 绿 蓝这三种颜色的荧光粉去调颜色的。RGB色彩模式是工业界的一种颜色标准,是通过对红(R)、绿(G)、蓝(B)三个颜…

首创证券将在上交所上市:募资约19亿元,规模不及信达证券

12月22日,首创证券股份有限公司(下称“首创证券”,SH:601136)将在上海证券交易所主板上市。本次上市,首创证券的发行价格为7.07元/股,发行市盈率22.98倍,发行数量为2.73亿股,募资总额…

JavaSE14-数组

目录 1.数组基本用法 1.1.什么是数组 1.2.数组声明 1.3.数组的创建与初始化 1.3.1.基本类型数组 1.3.2.对象数组 1.4.数组的使用 1.4.1.获取长度 & 访问元素 1.4.2.遍历数组 2.数组作为方法的参数 2.1.基本用法 2.2.内存 2.3.引用 2.4.初识 JVM 内存区域划分…

怎么做现货白银的心理障碍

克服投资的心理障碍,是怎么做现货白银投资的关键。很多时候,技术分析是很简单的。一根K线,一条均线,就能够让人获利。但是为什么使用同样工具的人,却不能获利呢?为什么他们还要去追求一些特别复杂的分析系统…

四平方和(蓝桥杯C/C++B组真题详解)(三种做法)

目录 题目详细&#xff1a;​编辑 题目思路&#xff1a; 暴力&#xff1a; 代码详解&#xff1a; 哈希&#xff1a; 二分&#xff1a; 题目详细&#xff1a; 题目思路&#xff1a; 这个题目大家可能马上就可以想到暴力做 例如这样 暴力&#xff1a; #include<iost…

TensorFlow和Keras应如何选择?

前些年&#xff0c;深度学习领域的研究人员、开发人员和工程师必须经常做出一些选择&#xff1a; 我应该选择易于使用但自定义困难的 Keras 库&#xff1f;还是应该使用难度更大的 TensorFlow API&#xff0c;编写大量代码&#xff1f;&#xff08;更不用说一个不那么容易使用…

pyspark之sparksql数据交互

在pyspark中&#xff0c;使用sparksql进行mysql数据的读写处理&#xff0c;将程序保存为test.py #-*- coding: UTF-8 -*- # 设置python的默认编码 import sys reload(sys) sys.setdefaultencoding(utf-8) # Spark 初始化 from pyspark.sql import SQLContext, SparkSession, …

【推荐】DDD领域驱动设计和中台实践资料合集

Domain Driven Design&#xff08;简称 DDD&#xff09;&#xff0c;又称为领域驱动设计&#xff0c;起源于杰出软件建模专家Eric Evans在2003年发表的书籍《DOMAIN-DRINEN DESIGN —TACKLING COMPLEXITY IN THE HEART OF SOFTWARE》&#xff08;中文译名《领域驱动设计—软件核…

卓海科技冲刺创业板:拟募资5.47亿 相宇阳控制52.9%股权

雷递网 雷建平 12月20日无锡卓海科技股份有限公司&#xff08;简称&#xff1a;“卓海科技”&#xff09;日前递交招股书&#xff0c;准备在深交所创业板上市。卓海科技计划募资5.47亿元&#xff0c;其中&#xff0c;1.04亿元用于半导体前道量检测设备扩产项目&#xff0c;1.84…

ev_api_server:大事件node接口项目开发

Headline 大事件后台 API 项目&#xff0c;API 接口文档请参考 https://www.showdoc.cc/escook?page_id3707158761215217 1. 初始化 1.1 创建项目 新建 api_server 文件夹作为项目根目录&#xff0c;并在项目根目录中运行如下的命令&#xff0c;初始化包管理配置文件&#x…

尚医通-前端Vue学习(十)

目录&#xff1a; &#xff08;1&#xff09;node.js介绍 &#xff08;2&#xff09;npm包管理工具 &#xff08;3&#xff09;es6模块化 &#xff08;4&#xff09;babel转码器 &#xff08;5&#xff09;webpack打包工具 &#xff08;1&#xff09;node.js介绍 浏览器的…

Python使用pandas导入xlsx格式的excel文件内容

Python使用pandas导入xlsx格式的excel文件内容1. 基本导入2. 列标题与数据对齐3. 指定导入某个sheet4. 指定行索引5. 指定列索引6. 指定导入列7. 指定导入的行数8. 更多的参数1. 基本导入 在 Python中使用pandas导入.xlsx文件的方法是read_excel()。 # codingutf-8 import pa…

Windows环境下在VScode中运行开源运动规划库(zhm-real / PathPlanning)的方法

本文主要介绍Windows环境下&#xff0c;在Vscode中运行zhm-real发布的开源运动规划库PathPlanning的实现方法&#xff0c;包括环境配置及运行开源包时常见错误解决方法。    一、环境配置 &#xff08;1&#xff09;VScode 下载及安装&#xff0c;官网如下&#xff1a; http…

Flowable工作流进阶使用教程(监听器+流程变量+网关)

一、任务分配和流程变量 1.任务分配 1.1 固定分配 固定分配就是我们前面介绍的&#xff0c;在绘制流程图或者直接在流程文件中通过Assignee来指定的方式 1.2 表达式分配 Flowable使用UEL进行表达式解析。UEL代表Unified Expression Language&#xff0c;是EE6规范的一部分…

【Python】用python将html转化为pdf

其实早在去年就有做过&#xff0c;一直没有写&#xff0c;先简单记录下 1、主要用到的工具【wkhtmltopdf】 【下载地址】wkhtmltopdf 根据系统选择安装包&#xff0c;速度有点慢&#xff0c;先挂着 2、下载Python库 pip install pdfkit pip install wkhtmltopdf 3、简单代码…

CAD教程:CAD自定义之基础设置的操作技巧

在使用国产CAD软件绘制CAD图纸的过程中&#xff0c;有些时候会需要CAD自定义设置&#xff0c;那么你知道浩辰CAD建筑软件中CAD自定义之基础设置怎么使用吗&#xff1f;不知道也没关系&#xff0c;接下来的CAD教程就让小编来给大家介绍一下国产CAD软件——浩辰CAD建筑软件中CAD自…

【1799. N 次操作后的最大分数和】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给你 nums &#xff0c;它是一个大小为 2 * n 的正整数数组。你必须对这个数组执行 n 次操作。 在第 i 次操作时&#xff08;操作编号从 1 开始&#xff09;&#xff0c;你需要&#xff1a; 选择两个…

实验一 逻辑回归

一、实验目的 &#xff08;1&#xff09;学习并掌握常见的机器学习方法&#xff1b; &#xff08;2&#xff09;能够结合所学的python知识实现机器学习算法&#xff1b; &#xff08;3&#xff09;能够用所学的机器学习算法解决实际问题。 二、实验内容与要求 &#xff08…

设计模式之备忘录模式

Memento design pattern 备忘录模式的概念、备忘录模式的结构、备忘录模式的优缺点、备忘录模式的使用场景、备忘录模式的实现示例、备忘录模式的源码分析 1、备忘录模式的概念 备忘录模式&#xff0c;又称快照模式&#xff0c;即在不破坏封装的前提下&#xff0c;获取并保存一…