2.垃圾回收相关算法:

2.1.垃圾标记阶段算法:

2.1.1 标记阶段的目的

垃圾标记阶段:主要是为了判断对象是否存活

1.在堆里存放着几乎所有的 Java 对象实例,在 GC 执行垃圾回收之前,首先需 要区分出内存中哪些是存活对象,哪些是已经死亡的对象。只有被标记为己经死亡的对象,GC 才会在执行垃圾回收时,释放掉其所占用的内存空间,因此这个 过程我们可以称为垃圾标记阶段

2.那么在 JVM 中究竟是如何标记一个死亡对象呢?简单来说,当一个对象已经 不再被任何的存活对象继续引用时,就可以宣判为已经死亡。

3.判断对象存活一般有两种方式:引用计数算法和可达性分析算法

(总结:
主要是来判断哪些对象不再被使用,标记为垃圾对象
判定对象为垃圾的标准:不被任何引用所指向的对象. Object obj = new Objec();
垃圾回收阶段的算法: 引用计数算法(在JVM不被使用)和可达性分析算法.
)

2.1.2引用计数算法

1.引用计数算法(Reference Counting)比较简单,对每个对象保存一个整型 的引用计数器属性。用于记录对象被引用的情况。

2.对于一个对象 A,只要有任何一个对象引用了 A,则 A 的引用计数器就加 1; 当引用失效时,引用计数器就减 1。只要对象 A 的引用计数器的值为 0,即表示 对象 A 不可能再被使用,可进行回收。

3.优点:实现简单,垃圾对象便于辨识;判定效率高,回收没有延迟性。

4.缺点:

1).它需要单独的字段存储计数器,这样的做法增加了存储空间的开销。

2).每次赋值都需要更新计数器,伴随着加法和减法操作,这增加了时间开销。

3).引用计数器有一个严重的问题,即无法处理循环引用的情况。这是一条致命缺陷,导致在.Java 的垃圾回收器中没有使用这类算法。

总结:(如果有一个引用指向此对象,那么计数器加1,如果没有引用指向,计数器为0,此时就判定为垃圾.
优点:方便使用,设计简洁
缺点:增加了计数器的存储空间,计数需要消耗时间
导致循环引用问题:好几个对象之间相互引用,但是没有其他引用指向它们,此时垃圾回收器不能回收它们,但是也没有引用指向,这就造成了内存泄漏)

2.1.2可达性分析算法(这是java目前所使用的垃圾标记算法)

可达性分析算法:也可以称为根搜索算法、追踪性垃圾收集

1.相对于引用计数算法而言,可达性分析算法不仅同样具备实现简单和执行高效等特点,更重要的是该算法可以有效地解决在引用计数算法中循环引用的问题, 防止内存泄漏的发生。

2.相较于引用计数算法,这里的可达性分析就是 Java、C#选择的。这种类型的垃圾收集通常也叫作追踪性垃圾收集(Tracing Garbage Collection).

可达性分析实现思路

所谓"GCRoots”根集合就是一组必须活跃的引用,其基本思路如下:

1.可达性分析算法是以根对象(GCRoots)为起始点,按照从上至下的方式搜索被根对象集合所连接的目标对象是否可达。

2.使用可达性分析算法后,内存中的存活对象都会被根对象集合直接或间接连接着,搜索所走过的路径称为引用链(Reference Chain)

3.如果目标对象没有任何引用链相连,则是不可达的,就意味着该对象己经死亡,可以标记为垃圾对象。

4.在可达性分析算法中,只有能够被根对象集合直接或者间接连接的对象才是存活对象。

(总结:
循环引用问题,设计简单,运行高效,防止内存泄漏
思路:
从一些活跃引用(GC Roots 根)开始,如果对象被根直接或间接引用,那么此对象不是垃圾,否则标记为垃圾对象.
)

GC Roots 可以是哪些元素?

1.虚拟机栈中引用的对象 比如:各个线程被调用的方法中使用到的参数、局部变量等。

2.本地方法栈内 JNI(通常说的本地方法)引用的对象

3.方法区中类静态属性引用的对象,比如:Java 类的引用类型静态变量

4.方法区中常量引用的对象,比如:字符串常量池(StringTable)里的引用

5.所有被同步锁 synchronized 持有的对象

6.Java 虚拟机内部的引用。

基 本 数 据 类 型 对 应 的 Class 对 象 ,一 些 常 驻 的 异 常 对 象 ( 如 : NullPointerException、OutofMemoryError),系统类加载器。

总结

简单一句话就是,除了堆空间的周边,比如:虚拟机栈、本地方法栈、方法区、 字符串常量池等地方对堆空间进行引用的,都可以作为 GC Roots 进行可达性分析。

概述::(那些引用被用来当做根:
虚拟机栈中引用的对象(方法中引用的对象);
本地方法栈引用的对象;
静态变量所引用的对象;
方法区中常量引用的对象;
被synchronized 当做锁的对象;
虚拟机内部引用的对象.
总结: 栈中引用的(正在使用的)方法区,常量池中(生命周期较长的),被synchronized 当做锁的对象.
)

2.2 对象的 finalization 机制

finalize() 方法机制

对象销毁前的回调函数:finalize();

Java 语言提供了对象终止(finalization)机制来允许开发人员提供对象被销毁 之前的自定义处理逻辑。

当垃圾回收器发现没有引用指向一个对象,即:垃圾回收此对象之前,总会先调 用这个对象的 finalize()方法,一个对象的 finalize()方法只被调用一次。

finalize() 方法允许在子类中被重写,用于在对象被回收时进行资源释放。通常在这个方法中进行一些资源释放和清理的工作,比如关闭文件、套接字和数据库连接等

Object 类中 finalize() 源码
protected void finalize() throws Throwable { }
永远不要主动调用某个对象的 finalize()方法,应该交给垃圾回收机制调用。理由包括下面三点:

1.在 finalize()时可能会导致对象复活。

2.finalize()方法的执行时间是没有保障的,它完全由 GC 线程决定,极端情况下, 若不发生 GC,则 finalize()方法将没有执行机会。

3.一个糟糕的 finalize()会严重影响 GC 的性能。比如 finalize 是个死循环。

概述:final(关键字),finally(代码块),finalize()(方法).
是Object类中的一个方法,在对象被回收之前调用,只调用一次.
finalize()方法机制:
java允许对象在销毁前去调用finalize(),去处理一些逻辑,一般不使用(不建议使用).
不要显示的去调用finalize()方法,在里面写代码一定要慎重:
在 finalize()时可能会导致对象复活。
finalize()是由垃圾回收器调用的,没有固定的时间.
一个糟糕的 finalize()会严重影响 GC 的性能。比如 finalize 是个死循环。

生存还是死亡?

由于 finalize()方法的存在,虚拟机中的对象一般处于三种可能的状态。

如果从所有的根节点都无法访问到某个对象,说明对象己经不再使用了。一般来说,此对象需要被回收。但事实上,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段。一个无法触及的对象有可能在某一个条件下“复活”自己, 如果这样,那么对它立即进行回收就是不合理的。为此,定义虚拟机中的对象可能的三种状态。如下:

可触及的:从根节点开始,可以到达这个对象。

可复活的:对象的所有引用都被释放,但是对象有可能在 finalize()中复活。

不可触及的:对象的 finalize()被调用,并且没有复活,那么就会进入不可触及 状态。不可触及的对象不可能被复活,因为 finalize()只会被调用一次。

以上 3 种状态中,是由于 finalize()方法的存在,进行的区分。只有在对象不可触及时才可以被回收.

具体过程

判定一个对象 objA 是否可回收,至少要经历两次标记过程:

1.如果对象 objA 到 GC Roots 没有引用链,则进行第一次标记。

2.进行筛选,判断此对象是否有必要执行 finalize()方法

如果对象 objA 没有重写 finalize()方法,或者 finalize()方法已经被虚拟机调用过,则虚拟机视为“没有必要执行”,objA 被判定为不可触及的。

如果对象 objA 重写了 finalize()方法,且还未执行过,那么 objA 会被插 入到 F-Queue 队列中,由一个虚拟机自动创建的、低优先级的 Finalizer 线程触 发其 finalize()方法执行。

finalize()方法是对象逃脱死亡的最后机会,稍后 GC 会对 F-Queue 队列 中的对象进行第二次标记。如果 objA 在 finalize()方法中与引用链上的任何一个 对象建立了联系,那么在第二次标记时,objA 会被移出“即将回收”集合。之 后,对象会再次出现没有引用存在的情况。在这个情况下,finalize()方法不会被 再次调用,对象会直接变成不可触及的状态,也就是说,一个对象的 finalize() 方法只会被调用一次。

代码演示 finalize() 方法可复活对象

public class CanReliveObj {public static CanReliveObj obj;//类变量,属于 GC Root//此方法只能被调用一次@Overrideprotected void finalize() throws Throwable {//super.finalize();System.out.println("调用当前类重写的finalize()方法");obj = this;//当前待回收的对象在finalize()方法中与引用链上的一个对象obj建立了联系}public static void main(String[] args) {try {obj = new CanReliveObj();// 对象第一次成功拯救自己obj = null;System.gc();//调用垃圾回收器,触发FULL GC  也不是调用后立刻就回收的,因为线程的执行权在操作系统System.out.println("第1次 gc");// 因为Finalizer线程优先级很低,暂停2秒,以等待它Thread.sleep(2000);if (obj == null) {System.out.println("obj is dead");} else {System.out.println("obj is still alive");}System.out.println("第2次 gc");// 下面这段代码与上面的完全相同,但是这次自救却失败了obj = null;System.gc();// 因为Finalizer线程优先级很低,暂停2秒,以等待它Thread.sleep(2000);if (obj == null) {System.out.println("obj is dead");} else {System.out.println("obj is still alive");}} catch (InterruptedException e) {e.printStackTrace();}}}

运行结果:

第一次自救成功,但由于 finalize() 方法只会执行一次,所以第二次自救失败。

2.3.内存溢出与内存泄漏:

内存溢出

内存溢出相对于内存泄漏来说,尽管更容易被理解,但是同样的,内存溢出也是引发程序崩溃的罪魁祸首之一。
由于 GC 一直在发展,所有一般情况下,除非应用程序占用的内存增长速度非 常快,造成垃圾回收已经跟不上内存消耗的速度,否则不太容易出现 OOM 的情况。
大多数情况下,GC 会进行各种年龄段的垃圾回收,实在不行了就放大招,来一次独占式的 Full GC 操作,这时候会回收大量的内存,供应用程序继续使用。
Javadoc 中对 OutofMemoryError 的解释是,没有空闲内存,并且垃圾收集器也无法提供更多内存。

(内存不够用了)

内存泄漏

内存泄漏也称作“存储渗漏”。严格来说,只有对象不会再被程序用到了,但 是 GC 又不能回收他们的情况,才叫内存泄漏。
但实际情况很多时候一些不太好的实践(或疏忽)会导致对象的生命周期变得 很长甚至导致 OOM,也可以叫做宽泛意义上的“内存泄漏”。
尽管内存泄漏并不会立刻引起程序崩溃,但是一旦发生内存泄漏,程序中的可用内存就会被逐步蚕食,直至耗尽所有内存,最终出现 OutofMemory 异常, 导致程序崩溃。

注意,这里的存储空间并不是指物理内存,而是指虚拟内存大小,这个虚拟内 存大小取决于磁盘交换区设定的大小。

常见例子:

单例模式

单例的生命周期和应用程序是一样长的,所以在单例程序中,如果持有对外部 对象的引用的话,那么这个外部对象是不能被回收的,则会导致内存泄漏的产生.

一些提供 close()的资源未关闭导致内存泄漏

数据库连接 dataSourse.getConnection(),网络连接 socket 和 io 连接必须 手动 close,否则是不能被回收的。

(有些对象已经不被使用了,但是垃圾回收机制并不能判定其为垃圾对象,不能将其回收掉,这样的对象越积越多,长久也会导致内存不够用,例如:
与数据库连接完之后,需要关闭连接通道,但是没有关闭;
IO读写完成后,没有关闭)

JVM - 垃圾回收(垃圾标记阶段算法,内存泄漏与溢出)(2)相关推荐

  1. JVM虚拟机(四):JVM 垃圾回收机制概念及其算法

    垃圾回收概念和其算法 谈到垃圾回收(Garbage Collection)GC,需要先澄清什么是垃圾,类比日常生活中的垃圾,我们会把他们丢入垃圾箱,然后倒掉.GC中的垃圾,特指存于内存中.不会再被使用 ...

  2. 【JVM进阶之路】垃圾回收机制和GC算法之三色标记(三)

    JVM往期文章 [JVM进阶之路]内存结构(一) [JVM进阶之路]玩转JVM中的对象(二) 上篇文章中讲到JVM中的对象以及判断对象的存活,那么对于"已死"的对象应该如何处理,怎 ...

  3. JVM—7—垃圾回收概述及相关算法

    文章目录 一.垃圾回收概述 1.概念 2.什么是垃圾 2.1 什么是垃圾? 2.2 磁盘碎片整理 2.3 大厂面试题 3.为什么需要GC 4.早期垃圾回收 5.Java垃圾回收机制 5.1 优点 5. ...

  4. 内存管理篇(三):Go垃圾回收之三色标记算法

    三色标记法介绍: 三色标记法(tricolor mark-and-sweep algorithm)是传统 Mark-Sweep 的一个改进,它是一个并发的 GC 算法,在Golang中被用作垃圾回收的 ...

  5. 第七篇章——垃圾回收概念及相关算法

    垃圾回收--概述 本专栏学习内容来自尚硅谷宋红康老师的视频以及<深入理解JVM虚拟机>第三版 有兴趣的小伙伴可以点击视频地址观看,也可以点击下载电子书 垃圾回收概述 垃圾回收不是Java语 ...

  6. 垃圾回收器之标记擦除法

    垃圾回收器之标记擦除法 这一次我们来简单的了解一下标记擦除法(mark-and-sweep),标记擦除法是第一个被广泛使用的,并且可以解决循环引用问题的垃圾回收算法: 使用标记擦除法的时候,垃圾对象并 ...

  7. 垃圾回收及涉及的算法

    一.什么是垃圾? 程序在运行过程中,长时间会有一些无引用且仍然存活动对象,就好多比,你有一套很大的房子,有好几个房间,自己又是个吃货,专门搞了一个房间用于存放零食.吃货每次去超市都会买一些东西回来放在 ...

  8. js垃圾回收的标记清理与引用计数

    js是使用垃圾回收的语言,也就是说执行环境负责在代码执行时管理内存.js为开发者卸下跟踪内存这个负担,通过自动内存管理实现内存分配和闲置资源回收.基本思路很简单:确定哪个变量不会再使用,然后释放它所占 ...

  9. 垃圾回收③---垃圾回收器

    本篇目录 1.GC的分类与性能指标 1.1 GC分类 1.2 评估GC的性能指标 1.2.1 吞吐量 1.2.2 暂停时间 2.不同垃圾回收器概述 2.1 垃圾收集器发展史 2.2 7款经典的垃圾收集 ...

最新文章

  1. SpringBoot源码分析之@Scheduled
  2. python没用_大部分Python资料都没有说到的重点-用实战教你解决问题的思路
  3. mac on linux,Mac- on- Linux
  4. Java设计模式(五):单例设计模式
  5. Dynamics CRM 提示“操作无效”
  6. 【Tools】Bandicam录屏软件概述
  7. 用友t3服务器文件丢失,服务器系统损坏数据恢复
  8. “中国智造”为System x提供创新源动力
  9. 使用SWig出现调用异常的情况
  10. TFS在项目中DevOps落地进程(下)
  11. 线性表--算法设计题2.29
  12. UI设计素材模板|完美日期选择器
  13. SpringBoot报错:If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
  14. bzoj 3437 小p的农场
  15. RAID - 提升IO性能及数据安全
  16. 基于在线学习行为的评价模型的设计与实现
  17. win2003系统优化[全集,很详细]
  18. 简单的汇率转换工具---初试AJAX
  19. 阿里云服务器配置远程打印机,电脑启用打印服务器配置
  20. codeforces 1238

热门文章

  1. 凯盛融英在港招股书“失效”:遭证监会严格发问,需补充披露
  2. [leetcode每日一题2021/5/8]1723. 完成所有工作的最短时间
  3. Civil 3d-快速转换C3D图形为CAD图形
  4. 信息系统项目管理师必背核心考点(四十)干系人管理
  5. C++学习8——菜鸟教程自学
  6. CH9121串口转以太网配置调试
  7. 基于图像的火焰识别调研总结
  8. |羊城之夏2019市民文化節遇見藝術高雅藝術公益講座開講海报廣州市文化館原創發佈於2019年7月9日星期二之粤语文稿
  9. Postman-APIs是干什么的?
  10. tolua, luaframework环境搭建