垃圾回收(GC

  • 1 什么是垃圾回收(GC)
  • 2 为什么要进行垃圾回收
  • 3 垃圾回收的优点/缺点是什么
  • 4 垃圾回收要回收的内存有哪些
  • 5 垃圾回收到底是怎么回收的
  • 6 垃圾对象的判断算法
    • 6.1 引用计数算法
    • 6.2 可达性分析
      • 6.2.1 核心思想
      • 6.2.2 有关四种引用
  • 7 垃圾回收算法, 具体是怎么回收的
    • 7.1 标记 - 清除算法
    • 7.2 复制算法
    • 7.3 标记 - 整理算法
    • 7.4 分代算法
      • 7.4.1 分代算法概述及核心思想
      • 7.4.2 分代算法的过程
    • 7.5 Minor GC 与 Full GC 的区别

1 什么是垃圾回收(GC)

 举个例子来说明: 例如积攒了一天的各种各样的垃圾我放到了一个袋子里面, 这个时候我把这些垃圾分类好放到对应的垃圾桶里面, 这就是相当于 C 语言的手动回收内存; 但是呢有时候我比较懒, 我只是把所有垃圾直接扔到楼下的垃圾桶旁边, 当我走后, 保洁员在收拾小区卫生的时候就会对垃圾桶旁边的这些垃圾进行归类, 分类扔进不同的垃圾桶里面, 这就相当于垃圾回收机制, 也就是说谁扔垃圾都可以, 但是呢会有个统一负责的人来进行归类回收. 对于 Java 来说, 代码中的任何地方都可以申请内存, 然后由 JVM 统一进行释放, 具体来说 JVM 内部的一组专门负责垃圾回收的线程来进行这样的工作.简言之,垃圾回收就是回收内存的过程, 我们都知道其实 JVM 就是个 Java 进程, 但是一个进程会持有很多的硬件资源, 如 CPU, 内存, 硬盘以及带宽资源; 但是系统的内存总量是一定的, 并且内存还要同时给很多的进程来使用, 例如我们电脑是16G内存,它是一定的,程序在使用内存的时候必须先申请才能使用, 用完之后还要记得释放掉.

2 为什么要进行垃圾回收

 从代码编写的角度来看, 内存的申请时机是非常明确的, 但是内存的释放时机有些时候却是不太明确的, 这个时候就给内存的释放带来了一些困难, 典型的问题就是: 这个内存我是否还要继续使用? 关于内存泄露这件事一旦出现是比较难调试的, 我们很难找到是哪个地方出现了内存泄露; 当出现在生产环境中时, 如果不能第一时间暴露出问题, 而是逐渐积累达到一定程度后, 这时候爆发出来将会造成毁灭性的问题, 因此有效的垃圾回收是非常有必要的!

3 垃圾回收的优点/缺点是什么

优点: 可以更大程度的保证不出现内存泄露的情况, 但是需要注意的是这里不是 100% 保证, 当程序猿作死的时候 JVM 是救不了的!!!
缺点:
1) 需要消耗额外的系统资源;
2) 内存的释放可能存在延迟, 也就是说某个内存不用了可能不会第一时间去释放掉内存, 而是稍后释放;
3) 可能会出现 STW 问题.

4 垃圾回收要回收的内存有哪些

  1. 堆: 垃圾回收要释放的内容主要是在堆区;
  2. 方法区: 方法区里面大部分存的是"类对象", 通过类加载生成的, 对方法区进行垃圾回收就相当于是"类卸载";
  3. 栈 / 程序计数器: 内存都是和具体的线程绑定在一起的, 这两部分的内容都是自动释放的, 当代码块结束/线程结束的时候, 内存就自动释放了. 我们日常所说的垃圾回收主要是指堆上内存的回收,因为堆所占据的内存空间是最大的, 本身就占据了一个程序绝大部分的内存.

那么关于回收堆上的内容, 具体回收的是什么呢?

由上图可以看出, 针对堆上的对象主要分为三种情况:
(1) 完全使用的; 这里的肯定不是我们回收的对象.
(2) 完全不使用的; 这才是我们要回收的内存.
(3) 一半要使用, 一半不使用的: 这种情况下不能进行回收, 因为如果回收起来的话成本会比较大, Java 中的垃圾回收是以"对象"为基本单位, 一个对象要么被回收, 要么不被回收, 不会出现回收一般的情况.

5 垃圾回收到底是怎么回收的

垃圾回收的基本思想: 先找出垃圾, 然后再回收垃圾; 应遵循宁可放过, 也不能错杀. 上面我们也说过了回收的是再也不会被使用的对象, 如果正在使用的对象也回收了的话, 那程序猿还怎么"玩". 例如去看牙医, 医生说有两颗蛀牙, 但是呢有一颗蛀牙不是特别的确定要不要拔, 那么这时候肯定是先把确定的那颗蛀牙给拔了, 不确定的先留着. 相比于回收少了这样的问题而言, 回收多了显然是更严重的问题.

6 垃圾对象的判断算法

  对于 GC, 判断垃圾主要有下面这两种典型方案.

6.1 引用计数算法

 ==算法基本思想: 给对象增加一个引用计数器, 每当有一个地方引用它时, 计数器就 + 1; 当引用时效时, 计数器就 - 1; 任何时刻计数器为 0 的对象就是不能再被使用的对象.==举例如下:

引用计数的优缺点:
优点: 规则简单, 实现方便, 程序运行效率高效;
缺点:

  • 空间利用率比较低,如果一个对象很大, 在程序中对象数目也不多,此时引用计数完全可以; 如果一个很大的对象在里面加多个计数器也没有什么负担; 但是如果一个对象很小, 在程序中对象数据也很多, 此时引用计数就会带来不可忽视的空间开销.
  • 存在循环引用的问题, 在某些特殊的代码下, 循环引用会导致代码的引用计数判断出现问题, 从而无法回收.

下面我们举个例子来解释一下这个致命的缺点是怎么回事:

由于以上错误, 我们在 Java 中并没有使用引用计数这种方式来判定垃圾, 而是使用了下面的可达性分析.

6.2 可达性分析

6.2.1 核心思想

核心思想: 通过一系列称为 " GC Roots" 的对象作为起始点, 从这些节点开始向下搜索, 搜索走过的路径称之为 " 引用链", 当一个对象到 GC Roots 没有任何的引用链时, 则证明是不可用的.如下图所示:

JVM 中采取的方案就是: 在 JVM 中存在一个 / 一组线程来周期性的进行上述的遍历过程, 不断地找出不可达的对象, 然后由 JVM 进行回收.
关于这些对象, 包含下面这三种:

  1. 栈上的局部变量表中的引用;
  2. 常量池里面的引用指向的对象;
  3. 方法区中引用类型的静态成员变量.

基于上述过程, 便可以完成垃圾对象的标记, 和引用计数相比较, 可达性分析确实是复杂了很多, 同时实现可达性分析的遍历过程的开销也是比较大的; 但是带来的好处就是解决了引用计数的两个缺点: 内存上不需要消耗额外的空间, 也没有产生循环引用的问题.

6.2.2 有关四种引用

  不管是引用计数还是可达性分析, 其判定规则都是看当前对象是否有引用来指向, 也就是说都是通过引用来进行判定对象的生死的; 引用的诞生之前只是为了用来访问对象, 但是随着时代的发展, 引用也是可以用来判定对象的生死的, 但是也有的不可以判定, 主要有如下四种引用.

  1. **强引用: 这是我们日常使用的引用, 既能够访问对象, 也能决定对象的生死;**只要强引用还存在, 垃圾回收器永远不会回收掉引用的对象实例.
  2. 软引用: 能够访问对象, 但是只能一定程度的决定对象的生死, 也就是说 JVM 会根据内存是否富裕来自行决定; 对于软引用关联着的对象, 在系统将要发生内存溢出之前, 会把这些对象列入回收范围之中进行第二次回收, 如果这次回收还是没有足够的内存, 才会抛出内存溢出异常.
  3. 弱引用: 能够访问对象, 用来描述非必须对象的, 但是其强度要弱于软引用, 被弱引用关联的对象只能生存到下一次垃圾回收发生之前, 当垃圾回收器开始进行工作的时候, 无论当前内容是否够用, 都会回收掉只被弱引用关联的对象.
  4. 虚引用: 既不能找到对象, 也不能决定对象的生死, 只能在对象临被回收前进行一些善后的工作; 其也可以称为幽灵引用或者幻影引用, 也是最弱的一种引用关系, 一个对象是否有虚引用的存在, 完全不会对其生存时间构成影响, 也无法通过虚引用来取得一个对象实例, 为一个对象设置虚引用的目的就是能在这个对象被收集器回收时收到一个系统通知.

7 垃圾回收算法, 具体是怎么回收的

7.1 标记 - 清除算法

标记 - 清除算法是最基础的收集算法, 也就是说算法分为"标记"和"清除"两个阶段, 首先标记出所有要回收的对象, 在标记完成后统一回收所有被标记的对象, 后续的收集算法其实都是基于这种算法的思路.

  • 如上图所示, 灰色就是正在使用的对象, 黑色是要释放的对象, 白色是已经释放了的对象;
  • 虽然说以上的过程可以释放掉不用的内存空间, 但是却引入了额外的问题: 内存碎片; 也就是说空间的内存和正在使用的内存是交替出现的, 是无规律的, 因此如果再想要去申请一个小块内存还可以, 如果是要申请一个大块地连续内存, 此时就可能会分配失败; 毕竟大多数我们是要申请一块连续的内存空间;
  • 内存碎片问题如果一直累积下去, 就会出现系统看起来内存挺多的, 但是就是申请不了, 尤其是频繁申请释放的场景中尤为严重, 为了解决内存碎片这个问题, 复制算法就是一个很好的方案.

7.2 复制算法

复制算法将可用内存按容量划分为大小不等的两块, 每次只使用其中的一块; 当这块内存需要进行垃圾回收时, 会将此区域还存活的对象复制到另一块上面, 然后再把已经使用过的内存区域一次清理掉. 这样做的好处就是每次都是对整个半区进行内存回收, 内存分配的时候也就不需要考虑内存碎片等情况, 只需要移动堆顶指针, 按顺序分配即可.
其中: 黑色: 可回收; 白色: 未使用; 灰色: 存活对象

复制算法的缺点:
1) 可用的内存空间只有一半;
2) 如果要回收的对象比较少, 剩下的对象比较多, 那么复制的开销就会很大;

因此复制算法适用于对象会被快速回收, 并且整体内存不会很大的场景下.

7.3 标记 - 整理算法

  复制收集算法在对象存活率较高时会进行比较多的复制操作, 效率会变低; 而标记 - 整理算法可以解决内存空间利用率的问题;标记过程仍与" 标记 - 清除 "过程一致. 但后续步骤不是直接对可回收对象进行清理, 而是让所有存活对象都向一端移动, 然后直接清理掉端边界以外的内存. 其类似于 " 顺序表删除元素, 搬运 ", 这样的操作可以有效避免内存碎片, 同时提高内存的利用率.

缺点: 在这个搬运过程中, 也是一个很大的开销, 这个开销比复制算法里面复制对象的开销甚至更大.
实际实现垃圾回收算法, 要能够结合以上三种方式, 取长补短.

7.4 分代算法

7.4.1 分代算法概述及核心思想

  分代算法和上面所写的三种算法都有所不同, 分代算法是通过区域划分, 实现不同区域和不同的垃圾回收策略, 从而更好实现垃圾回收; 如果中国的一国两制方针, 对于不同的情况和地域设置更符合当地的规则, 从而实现更好的管理, 这就是分代算法的核心思想.
  如何分代?
根据对象的" 年龄 ", 来对整个内存进行分类; 把年龄短的对象放在一起, 年龄长的对象放在一起; 不同年龄的对象可以采取不同的垃圾回收算法来进行处理. 在 JVM 中, 进行垃圾回收扫描也是周期性的, 这个对象每次经历了一个扫描周期, 就认为是年龄增长了一岁, 也就是说根据年龄的长短来对整个内存进行分类, 根据不同年龄的对象, 采取不同的垃圾回收算法来进行处理.

7.4.2 分代算法的过程

7.5 Minor GC 与 Full GC 的区别

  • Minor GC 又称之为新生代 GC, 指的是发生在新生代的垃圾收集; 由于 Java 对象大多都具备朝生夕灭的特性, 因此 Minor GC (采用的是复制算法)非常频繁, 一般回收速度也比较快.
  • Full GC 又称为老年代 GC 或者 Major GC, 指的是发生在老年代的垃圾收集, 出现了 Major GC, 经常会伴随至少一次的 Major GC, 其速度一般会比 Minor GC 慢上很多倍.

JVM 系列(三) --- 详解Java垃圾回收(GC)相关推荐

  1. Java Garbage Collection基础详解------Java 垃圾回收机制技术详解

    最近还是在找工作,在面试某移动互联网公司之前认为自己对Java的GC机制已经相当了解,其他面试官问的时候也不存在问题,直到那天该公司一个做搜索的面试官问了我GC的问题,具体就是:老年代使用的是哪中垃圾 ...

  2. JVM系列之:详解java object对象在heap中的结构

    文章目录 简介 对象和其隐藏的秘密 Object对象头 数组对象头 整个对象的结构 简介 在之前的文章中,我们介绍了使用JOL这一神器来解析java类或者java实例在内存中占用的空间地址. 今天,我 ...

  3. [译]GC专家系列1:理解Java垃圾回收

    原文链接:http://www.cubrid.org/blog/de... 了解Java的垃圾回收(GC)原理能给我们带来什么好处?对于软件工程师来说,满足技术好奇心可算是一个,但重要的是理解GC能帮 ...

  4. JVM成神之路-Java垃圾回收

    Java垃圾回收机制 为什么要进行垃圾回收? 随着程序的运行,内存中存在的实例对象.变量等信息占据的内存越来越多,如果不及时进行垃圾回收,必然会带来程序性能的下降,甚至会因为可用内存不足造成一些不必要 ...

  5. python多线程详解 Python 垃圾回收机制

    文章目录 python多线程详解 一.线程介绍 什么是线程 为什么要使用多线程 总结起来,使用多线程编程具有如下几个优点: 二.线程实现 自定义线程 守护线程 主线程等待子线程结束 多线程共享全局变量 ...

  6. JVM原理之详解现代垃圾回收器 Shenandoah 和 ZGC

    Shenandoah Shenandoah 一词来自于印第安语,十九世纪四十年代有一首著名的航海歌曲在水手中广为流传,讲述一位年轻富商爱上印第安酋长 Shenandoah 的女儿的故事. 后来美国有一 ...

  7. 详解Python垃圾回收机制

    引用计数 Python默认的垃圾收集机制是"引用计数",每个对象维护了一个ob_ref字段.它的优点是机制简单,当新的引用指向该对象时,引用计数加1,当一个对象的引用被销毁时减1, ...

  8. 详解JavaScript垃圾回收机制

    垃圾回收机制 JS的垃圾回收机制是为了以防内存泄漏,内存泄漏的含义就是当已经不需要某块内存时这块内存还存在着,垃圾回收机制就是间歇的不定期的寻找到不再使用的变量,并释放掉它们所指向的内存. var a ...

  9. Java 垃圾回收(GC)

    目录 前言 什么是垃圾 可达性分析 GC Root 对象 什么时候回收 如何回收垃圾 标记清除算法(Mark and Sweep GC) 复制算法(Copying) 标记-压缩算法 (Mark-Com ...

  10. java垃圾回收GC(学习笔记)

    一.如何确定某个对象是垃圾? public class Main {public static void main(String[] args) {MyObject object1 = new MyO ...

最新文章

  1. MindSpore接口mindspore::api
  2. 用python画太阳系_用 Python 动态模拟太阳系运转
  3. 20172303 2018-2019-1《程序设计与数据结构》课程总结
  4. javamac系统通过pid获取进程名称_线上环境 Linux 系统调用追踪
  5. Hadoop Hbase适合存储哪类数据?(转)
  6. sql学习练习题_学习SQL:练习SQL查询
  7. mysql数据库优化课程---12、mysql嵌套和链接查询(查询user表中存在的所有班级的信息?)...
  8. php链接没有下划线,html超链接怎么去掉下划线
  9. CICD详解(四)——Jenkins下载与安装
  10. 远程桌面(3389)复制(拖动)文件
  11. 升级到Firefox 3.0后解决扩展版本不兼容的方法
  12. 248 中心对称数 III
  13. 部门换届推文文字_宿委会换届表彰大会!!!
  14. PLC通讯之串口转以太网/WIFI的透传模块(DTU)的专用OPC软件和DLL通讯组件
  15. Fortify白盒神器20.1.1安装教程
  16. CAD图形导入3dmax方法及注意要点
  17. 又一家云服务关停,这回是网易
  18. 手机共享电脑网络上网
  19. 银行支付接口测试怎么执行
  20. 【BRCM】博通 esdk6.5ga rootfs-rw

热门文章

  1. vscode显示错误: 不存在已注册的任务类型“Compile”。是否已错过安装提供相应任务提供程序的扩展?这是什么原因,怎么解决啊
  2. 【python】flask部署
  3. 解决window电脑使用IKE VPN登录时显示上下文已过期,不能再用了的方法。
  4. 用python模拟评委打分_如何使用php实现评委评分器
  5. 春节“恐归族”:没钱没票没有陪着我的她
  6. 【密评】商用密码应用安全性评估从业人员考核题库(四)
  7. 新手学开车,太全了,不看后悔奥
  8. 51 单片机实战教程(18 PCA模块)
  9. Linux文本使用uniq去重
  10. 剖腹产产妇坐月子期间有哪些注意事项