Java 详解(JVM) 垃圾回收机制原理

2023-11-03

一、什么是垃圾。

二、如何判断垃圾。

三、垃圾回收。

一、什么是垃圾。

        首先我们要搞懂什么是垃圾。

        在 JVM 的眼中,垃圾就是指那些在堆中存在的,已经“死亡”的对象。而对于“死亡”的定义,我们可以简单的将其理解为“不可能再被任何途径使用的对象”。(通俗的的讲就是在堆中创建的对象不会再次被使用)。

二、如何判断垃圾。

        垃圾判断算法:引用判断法,可达性分析法。

        引用判断法:

        在这种算法中,假设堆中每个对象(不是引用)都有一个引用计数器。当一个对象被创建并且初始化赋值后,该对象的计数器的值就设置为 1,每当有一个地方引用它时,计数器的值就加 1,例如将对象 b 赋值给对象 a,那么 b 被引用,则将 b 引用对象的计数器累加 1。

反之,当引用失效时,例如一个对象的某个引用超过了生命周期(出作用域后)或者被设置为一个新值时,则之前被引用的对象的计数器的值就减 1。而那些引用计数为 0 的对象,就可以称之为垃圾,可以被收集。

特别地,当一个对象被当做垃圾收集时,它引用的任何对象的计数器的值都减 1。

优点:引用计数法实现起来比较简单,对程序不被长时间打断的实时环境比较有利。
缺点:需要额外的空间来存储计数器,难以检测出对象之间的循环引用。

        可达性分析法:

        可达性分析法也被称之为根搜索法,可达性是指,如果一个对象会被至少一个在程序中的变量通过直接或间接的方式被其他可达的对象引用,则称该对象就是可达的。更准确的说,一个对象只有满足下述两个条件之一,就会被判断为可达的:

        对象是属于根集中的对象
        对象被一个可达的对象引用
在这里,我们引出了一个专有名词,即根集,其是指正在执行的 Java 程序可以访问的引用变量(注意,不是对象)的集合,程序可以使用引用变量访问对象的属性和调用对象的方法。在 JVM 中,会将以下对象标记为根集中的对象,具体包括:

        虚拟机栈(栈帧中的本地变量表)中引用的对象
        方法区中的常量引用的对象
        方法区中的类静态属性引用的对象
        本地方法栈中 JNI(Native 方法)的引用对象
        活跃线程(已启动且未停止的 Java 线程)
        根集中的对象称之为GC Roots,也就是根对象。可达性分析法的基本思路是:将一系列的根对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,如果一个对象到根对象没有任何引用链相连,那么这个对象就不是可达的,也称之为不可达对象。

        

        如上图所示,形象的展示了可达对象与不可达对象的示例,其中灰色的对象都是不可达对象,表示可以被垃圾收集的对象。在可达性分析法中,对象有两种状态,那么是可达的、要么是不可达的,在判断一个对象的可达性的时候,就需要对对象进行标记。

优点:可以解决循环引用的问题,不需要占用额外的空间
缺点:多线程场景下,其他线程可能会更新已经访问过的对象的引用

        引用(Java中的四种引用类型):

        强引用(Strong Reference):如Object obj = new Object(),这类引用是 Java 程序中最普遍的。只要强引用还存在,垃圾收集器就永远不会回收掉被引用的对象。
        软引用(Soft Reference):它用来描述一些可能还有用,但并非必须的对象。在系统内存不够用时,这类引用关联的对象将被垃圾收集器回收。JDK1.2 之后提供了SoftReference类来实现软引用。
        弱引用(Weak Reference):它也是用来描述非必须对象的,但它的强度比软引用更弱些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在 JDK1.2 之后,提供了WeakReference类来实现弱引用。
        虚引用(Phantom Reference):也称为幻引用,最弱的一种引用关系,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的是希望能在这个对象被收集器回收时收到一个系统通知。JDK1.2 之后提供了PhantomReference类来实现虚引用。

三、垃圾回收。

       垃圾回收算法:标记-清除算法、标记-整理算法、复制算法、分代收集算法。

        标记-清除算法:

        标记-清除(Tracing Collector)算法是最基础的收集算法,为了解决引用计数法的问题而提出。它使用了根集的概念,它分为“标记”和“清除”两个阶段:首先标记出所需回收的对象,在标记完成后统一回收掉所有被标记的对象,它的标记过程其实就是前面的可达性分析法中判定垃圾对象的标记过程。

优点:不需要进行对象的移动,并且仅对不存活的对象进行处理,在存活对象比较多的情况下极为高效。
缺点:标记和清除过程的效率都不高,这种方法需要使用一个空闲列表来记录所有的空闲区域以及大小,对空闲列表的管理会增加分配对象时的工作量;标记清除后会产生大量不连续的内存碎片,虽然空闲区域的大小是足够的,但却可能没有一个单一区域能够满足这次分配所需的大小,因此本次分配还是会失败,不得不触发另一次垃圾收集动作。

        标记-整理算法:

        标记-整理(Compacting Collector)算法标记的过程与“标记-清除”算法中的标记过程一样,但对标记后出的垃圾对象的处理情况有所不同,它不是直接对可回收对象进行清理,而是让所有的对象都向一端移动,然后直接清理掉端边界以外的内存。在基于“标记-整理”算法的收集器的实现中,一般增加句柄和句柄表。

优点:经过整理之后,新对象的分配只需要通过指针碰撞便能完成,比较简单;使用这种方法,空闲区域的位置是始终可知的,也不会再有碎片的问题了。
缺点:GC 暂停的时间会增长,因为你需要将所有的对象都拷贝到一个新的地方,还得更新它们的引用地址。

        复制算法:

        复制(Copying Collector)算法的提出是为了克服句柄的开销和解决堆碎片的垃圾回收。它将内存按容量分为大小相等的两块,每次只使用其中的一块(对象面),当这一块的内存用完了,就将还存活着的对象复制到另外一块内存上面(空闲面),然后再把已使用过的内存空间一次清理掉。

复制算法比较适合于新生代(短生存期的对象),在老年代(长生存期的对象)中,对象存活率比较高,如果执行较多的复制操作,效率将会变低,所以老年代一般会选用其他算法,如“标记-整理”算法。一种典型的基于复制算法的垃圾回收是stop-and-copy算法,它将堆分成对象区和空闲区,在对象区与空闲区的切换过程中,程序暂停执行。

优点:标记阶段和复制阶段可以同时进行;每次只对一块内存进行回收,运行高效;只需移动栈顶指针,按顺序分配内存即可,实现简单;内存回收时不用考虑内存碎片的出现。
缺点:需要一块能容纳下所有存活对象的额外的内存空间。因此,可一次性分配的最大内存缩小了一半。

        分代收集算法:

分代收集(Generational Collector)算法的将堆内存划分为新生代、老年代和永久代。新生代又被进一步划分为 Eden 和 Survivor 区,其中 Survivor 由 FromSpace(Survivor0)和 ToSpace(Survivor1)组成。所有通过new创建的对象的内存都在堆中分配,其大小可以通过-Xmx和-Xms来控制。分代收集,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,可以将不同生命周期的对象分代,不同的代采取不同的回收算法进行垃圾回收,以便提高回收效率。

        新生代:目的是回收那些生命周期短的对象,主要存放新产生的对象。新生代按照8:1:1分为eden区、survivor0、survivor1,大部分对象在eden区中生成,当eden满时,将存活的对象复制到survivor0,然后清空eden,当eden、survivor0都满了时,将这两个区中存活的对象复制到survivor1,然后清空eden、survivor0,当着三个区都满了时则把存货对象复制到老年代,如果老年代也满了则触发FullGC。新生代的全回收叫MinorGC,MinorGC发生频率比较高,不一定等到新生代满了时才进行。

        老年代:存放对象生命周期较长,且内存大概是新生代的两倍,老年代存活对象生命周期长,因此MajorGC发生频率较低。

        永久代:主要存放静态文件,如Java类,方法等。永久带对垃圾回收基本没有影响,当应用动态生成或者调用一些类的时候,例如反射、动态代理CGLib等bytecode框架时需要永久带来保存新生成的类。

小结:

        在新生代中,每次垃圾收集时都有大批对象死去,只有少量存活,那就选用复制算法。只需要付出少量存活对象的复制成本就可以完成收集。
老年代中因为对象存活率高、没有额外空间对他进行分配担保,就必须用标记-清除或者标记-整理。
        由于永久代经常会内存不够用或者发生内存泄露,JDK1.8开始废弃了永久代,取而代之的是元空间(直接存在内存中可自定义大小),主要存放类的元数据。

        

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Java 详解(JVM) 垃圾回收机制原理 的相关文章

  • Spring中如何映射Request参数?

    我正在使用 Spring MVC 当用户注册后 一封电子邮件将发送到用户帐户 这工作正常 我还将加密的用户ID发送到用户电子邮件 为此我编写了一个模板
  • 创建 RESTful WebService 并通过 Glassfish 4 提供服务

    我在 JEE6 中看到了很多关于 RESTful WebServices 的问题 所以我想与您分享这个示例解决方案 它展示了实现 RESTful Webservice 是多么容易 首先创建一个新的动态 Web 项目并将 Glassfish
  • 扩展 CrudRepository (Spring) 时是否需要 @Repository 注解?

    public interface CarRepository extends CrudRepository
  • 从 ArrayList Java 中的 HashMap 键中检索所有值

    美好的一天 这让我现在有点困惑 大脑冻结 并且似乎遗漏了一些东西 有一个 ArrayList 我用 HashMap 填充它 现在我放入 HashMap 和 arraylist Map put DATE value1 Map put VALU
  • 如何选择主题与队列

    当我们设计应用程序时如何选择Topic Queue类型实现 我知道 a 如果有多个消费者使用该消息 则使用 Topicb 如果只有一个消费者则使用Queue 请提供更多需要考虑的点 比如并发 消息持久化 负载均衡等等 Thanks Rw 如
  • 使用 org.eclipse.xsd 和 Maven2 分析 XML 模式

    我正在尝试实现示例代码本文 http help eclipse org help32 index jsp topic org eclipse xsd doc references articles dwtip1 scpw index htm
  • 在Java中,为什么equals()和hashCode()必须一致?

    如果我重写类上的任一方法 它必须确保如果A equals B true then A hashCode B hashCode也一定是真的 有人可以给我看一个简单的例子 如果违反了这一点 就会导致问题吗 我认为这与您是否使用该类作为 Hash
  • 如何将堆栈跟踪转换为字符串?

    转换结果的最简单方法是什么Throwable getStackTrace 到描述堆栈跟踪的字符串 Use Throwable printStackTrace PrintWriter pw https docs oracle com java
  • Java写入ByteArrayOutputStream内存泄漏

    我正在将图像字节写入ByteArrayOutputStream然后通过套接字发送它 问题是 当我这样做时 ImageIO write image gif byteArray 内存增加很多 有点内存泄漏 我用这个发送 ImageIO writ
  • 无法在 PHP 中接收 JSON POST 请求

    我正在将 JSON 对象从 Java 传递到 PHP 我正在使用 jdk 1 8 和 WAMPserver 下面是Java代码 import java io IOException import org apache http client
  • 自动装箱是否调用 valueOf()?

    我试图确定以下陈述是否保证为真 Boolean true Boolean TRUE Boolean true Boolean valueOf true Integer 1 Integer valueOf 1 我一直认为自动装箱相当于调用va
  • Apache HTTPClient SSLPeerUnverifiedException

    使用 Apache HttpClient 4 2 1 使用从基于表单的登录示例复制的代码 http hc apache org httpcomponents client ga examples html http hc apache or
  • Java SFTP使用JSch上传,但如何覆盖当前文件?

    我正在尝试使用 JSch 通过 SFTP 将两个文件上传到服务器 如果目录为空 则上传文件效果很好 但我想一遍又一遍地上传相同的文件 只需更改内部的 id 但我不知道如何执行此操作 JSch 中有一些名为 OVERWRITE 的静态参数 但
  • StringBuilder - 重置或创建新的

    我有一个条件 StringBuilder 不断存储与大型平面文件 数百 MB 中的模式匹配的行 但是 在达到条件后 我将 StringBuilder 变量的内容写入文本文件 现在我想知道是否应该通过重置对象来使用相同的变量 gt strin
  • 菜单项标题未显示

    菜单项的标题未显示在片段内 我在菜单文件中有两个项目 第一个是带有图标和标签的showAsAction always在工具栏中显示图标 第二个只有标题 我不知道这里出了什么问题 菜单项的所有操作均有效 例如下面 菜单 销售 xml menu
  • 在同步子句中抛出异常的副作用?

    从同步子句中抛出异常是否有任何不清楚的副作用 锁会发生什么情况 private void doSomething throws Exception synchronized lock doSomething 我没有看到任何副作用 The 锁
  • SwingWorker 和 Executor 的区别

    我正在使用 SwingWorker 在我正在制作的应用程序上执行一些重负载任务 虽然今天我遇到了 Executor 类和这个例子 Executors newCachedThreadPool execute new Runnable publ
  • 在java中访问dll方法

    我正在尝试访问java中用c 编写的dll方法 从下面的代码我试图构建已成功生成的 dll using System using Microsoft Win32 namespace CyberoamWinHelper public clas
  • JdbcTemplate queryForInt/Long 在 Spring 3.2.2 中已弃用。应该用什么来代替呢?

    JdbcTemplate 中的 queryforInt queryforLong 方法在 Spring 3 2 中已弃用 我无法找出为什么或什么被认为是使用这些方法替换现有代码的最佳实践 典型方法 int rowCount jscoreJd
  • 在 Ubuntu 上的 Tomcat 中加载共享本机库

    如何在 Ubuntu 上的 Tomcat6 中加载共享库 我创建了一个名为 libawragrids so 的库 awragrids 并将其放置在 var lib tomcat6 shared 我在调用启动 tomcat 的终端中设置了以下

随机推荐

  • everything 很多文件搜索不到?

    everything 很多文件搜索不到 知乎链接 https www zhihu com question 42052606 直接解决办法 把 C Users Administrator AppData Local Everything 下
  • TestNG同时使用DataProvider和Parameters

    TestNG Test测试方法中同时使用DataProvider和Parameters 实践中经常会遇到需要在 Test方法中混合使用TestNG的 DataProvider和 Parameters的情形 比如 根据参数的不同使用不同的测试
  • InnoDB存储引擎——表的逻辑存储及实现

    文章目录 表的逻辑存储结构 表分区 表的逻辑存储结构 在InnoDB存储引擎上 表都是根据主键顺序组织存放的 这种存储方式的表称为索引组织表 index organized table 如果一个表没有指定主键 那么会 首先判断是否存在唯一非
  • sql优化(查询大数据量时sql执行时间过长)

    问题 Oracle数据库 sql查询的优化 成交额统计表的sql查询时间过长进行的优化 解决办法 对sql语句中使用视图的部分替换为子查询 对查询表条件字段建立索引 引发的问题 在什么情况下建立索引 及建立索引后引发的开销有哪些 经查询or
  • 分布式应用部署模式下的Quartz配置

    本来计划做一次应用的部署升级 由单机模式 改为集群模式 但是在考虑方案时 除了遇到的SpringBoot优雅退出问题 还有一个需要考虑的问题 就是Quartz定时任务的处理 单机模式下 quartz定时任务很简单 按照文档使用即可 使用RA
  • 为mysql数据库建立索引;mysql索引总结----mysql 索引类型以及创建;mysql_建立索引的优缺点

    因为欣赏所以转载 http www cnblogs com cy163 archive 2008 10 27 1320798 html http www cnblogs com lihuiyong p 5623191 html http w
  • 修改MySQL账号的加密规则plugin和命令查看外键信息

    MySQL8之前的版本中加密规则是mysql native password 而在MySQL8之后 加密规则 是caching sha2 password 现在给大家介绍怎么修改用户的加密规则 修改成旧的加密规则 ALTER USER ro
  • Redis 一些基础知识以及数据类型

    3 启动后杂项基础知识 3 1 redis benchmark Redis 自带一个叫 redis benchmark 的工具来模拟N个客户端同时发出M个请求 启动 redis benchmark 可以通过 redis benchmark
  • CTFshow 文件上传 web156

    目录 思路 总结 思路 这次发现把shell php改成png上传的时候 提示文件类型不合规 可能对文件头进行检测 可以利用图片马来写入shell https github com huntergregal PNG IDAT Payload
  • 【element】el-autocomplete的常见用法

    前言 这段时间突然发现很少写博客了 平时都在平衡工作和休息的时间 周末也没动过笔 而且更重要的是我找不到写的内容了 在经历的初始的新知识的学习阶段后 目前的阶段更加转入对于业务的理解 很多模块在不同项目中都是可以复用的 而且工作中发现开发并
  • JS扁平化(flatten)数组

    JS扁平化分类 1 对象扁平化 深度很深的对象 经过扁平化编程深度为 1 的对象 2 数组扁平化 降维过程 多维数组经过扁平化变成一维数组 首先让我们思考一个这样的题目 假如有一个数组 var arr 1 2 3 4 我们怎么能把arr变成
  • 【python 10】python 魔术方法

    文章目录 一 getitem 获取属性 二 setitem 设置属性 三 delitem 删除属性 四 len 求长度 五 call 将类变成一个可调用的函数 python 中以 开头和结尾的成员 都被称为类的特殊成员 特殊属性和方法 一
  • Linux 中的 sysctl 命令及示例

    介绍 Linux管理员使用该命令在运行时sysctl读取或修改内核参数 无需重新启动即可实时控制和修改网络 I O 操作和内存管理设置的选项对于高可用性系统至关重要 了解如何使用该sysctl命令及其选项来动态调整系统性能
  • osgi 引用不同版本的jar_如何把公共模块做成sdk给其他项目引用

    实际开发过程中 会有这么一种需求 我们写的某些代码 不仅我们这个项目需要 其他部门 其他项目可能也需要 我们怎么把我们的项目给别人使用呢 有2种方法 第一种方法 只在本机中的其他项目中使用 不方便给他人使用 也可以发jar包给他人使用 但是
  • MyBatis使用association实现一对一级联查询的几种案例

    我们平日经常会遇到需要级联查询的场景 这里通过案例给大家展示实现过程 我们要查询的用户信息里面有个角色信息 一个用户对应一个角色 我们现在要求查出用户信息的同时 关联查出用户的角色信息 那么这个时候我们可以通过级联属性的方式 将角色中的数据
  • Mybatis拦截器

    MyBatis介绍 MyBatis本是apache的一个开源项目iBatis 2010年这个项目由apache software foundation 迁移到了google code 并且改名为MyBatis 它支持普通 SQL查询 存储过
  • uniapp、vue返回上一个页面并刷新(调用上一个页面的方法)

    在写微信小程序时有个功能有点问题 需求是 在当前页面 A页面 查看列表 在A页面点击发布跳转到表单页 B页面 B页面提交成功返回上一页A 要判断如果发布成功 返回到A页面得拿到最新数据 相当于要监听返回到A页面时得刷新数据了 方法一 使用o
  • 蓝桥杯:国二选手经验贴 附蓝桥杯历年真题

    相信能看到这篇文章的你 一定是有想过参赛了 那么恭喜你呀 看到一篇宝藏参赛指南 楼主将结合自己的参赛经历 手把手教你拿到省一进国赛嗷 文章结尾有历年真题及VIP试题链接 建议收藏 楼主是参加了2022年的蓝桥杯算法竞赛Python大学A组
  • latex教程——读书笔记整理(二)——文本排版

    文本排版 目录 文本排版 断行和分页 对齐段落 断词 内置字符串 特殊字符和符号 引号 破折号和连字号 波浪号 度的符号 省略号 连字 注音符号和特殊字符 标题 章 节 交叉引用 脚注 强调 环境 Itemize Enumerate 和 D
  • Java 详解(JVM) 垃圾回收机制原理

    一 什么是垃圾 二 如何判断垃圾 三 垃圾回收 一 什么是垃圾 首先我们要搞懂什么是垃圾 在 JVM 的眼中 垃圾就是指那些在堆中存在的 已经 死亡 的对象 而对于 死亡 的定义 我们可以简单的将其理解为 不可能再被任何途径使用的对象 通俗