深入分析GC垃圾回收机制!

前言

每每看到我的JVM虚拟机运行时,我就灰常担心我的电脑会不会炸掉,它呼呼的,这个该多占内存啊,它里面的的垃圾怎么清理的.我想了想在大上海为垃圾分类的王总,我露出了慈祥的面容,我要深入到虚拟机里,给他安排起来,于是就含泪整理了这篇垃圾回收机制GC.
在这里插入图片描述

JVM概述

对于 JVM 来说,我们都不陌生,其是 Java Virtual Machine(Java虚拟机)的缩写,它也是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。JVM有自己完善的硬件架构,如处理器、堆栈等,还具有相应的指令系统,其本质上就是一个程序,当它在命令行上启动的时候,就开始执行保存在某字节码文件中的指令。Java 语言的可移植性就是建立在 JVM 的基础之上的,任何平台只要装有针对于该平台的 Java 虚拟机,字节码文件(.class)就可以在该平台上运行,这就是“一次编译,多次运行”。
JAVA GC(Garbage Collection,垃圾回收)机制是区别C++的一个重要特征,C++需要开发者自己实现垃圾回收的逻辑,而JAVA开发者则只需要专注于业务开发,因为垃圾回收这件繁琐的事情JVM已经为我们代劳。根据JVM规范,JVM把内存划分成了如下几个区域:1.方法区(Method Area)2.堆区(Heap)3.虚拟机栈(VM Stack)4.本地方法栈(Native Method Stack)5.程序计数器(Program Counter Register),其中只有方法区和堆区需要进行垃圾回收,回收的对象就是那些不存在任何引用的对象。

引用(四种)

  • 强引用: Object object = new Object();这类引用是JAVA中是最普遍的.只要强引用还在,就不会回收.
  • 软引用:一些可能还有用,非必须的对象,在系统内存不足时,就会把这类引用回收.
  • 弱引用:比软引用更弱,被弱引用关联的对象只能生存到下次GC之前.
  • 虚引用:最弱的引用关系,完全不会对其生存时间构成影响.

啥是垃圾?

我们要回收垃圾,首先要知道什么是垃圾,总不能不讲武德,有用没用就一通乱回收.其实就是判断哪些对象是没有引用的,占着茅坑不拉屎的,就把它的地盘没收了.在JAVA架构里,几乎所有的对象实例都在堆中存放,所以垃圾回收是针对堆来实现的.有必要和堆中门对狙了.
然而在JVM的眼里,垃圾就是那些在队中存在且"挂掉"的对象.具体哪些是"挂掉的"就要自己整算法了.我说停停,有啥算法啊?
在这里插入图片描述

判断是垃圾的算法

引用计数法

假设每个对象都有一个引用计数器.当一个对象被创建并且初始化赋值时,就把计数器加一.eg:将对象B赋值给A时,那么B的计数器加一.然而当引用失效(某个对象的引用超过了生命周期(出作用域后)或者被设置成新值时,则之前引用的对象的计数器减一),那些计数器为零的对象,就被称为垃圾.当一个对象被当做垃圾收集时,它引用的任何对象的计数器的值都减 一。

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

可达性分析法

可达性是指,如果一个对象会被至少一个在程序中的变量通过直接或间接的方式被其他可达的对象引用,则称该对象就是可达的。
可达的条件:

  • 对象是属于根集中的对象
  • 对象被一个可达的对象引用

根集中的对象包括:
虚拟机栈(栈帧中的本地变量表)中引用的对象
方法区中的常量引用的对象
方法区中的类静态属性引用的对象
本地方法栈中 JNI(Native 方法)的引用对象
活跃线程(已启动且未停止的 Java 线程)

在可达性分析法中,对象有两种状态,那么是可达的、要么是不可达的,在判断一个对象的可达性的时候,就需要对对象进行标记。

在根搜索算法中,要真正宣告一个对象死亡,至少要经历两次标记过程:
如果对象在进行根搜索后发现没有与根对象相连接的引用链,那它会被第一次标记并且进行一次筛选。筛选的条件是此对象是否有必要执行 finalize()方法(可看作析构函数,类似于 OC 中的dealloc,Swift 中的deinit)。当对象没有覆盖finalize()方法,或finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为没有必要执行。
如果该对象被判定为有必要执行finalize()方法,那么这个对象将会被放置在一个名为F-Queue的队列中,并在稍后由一条由虚拟机自动建立的、低优先级的Finalizer线程去执行finalize()方法。finalize()方法是对象逃脱死亡命运的最后一次机会(因为一个对象的finalize()方法最多只会被系统自动调用一次),稍后 GC 将对F-Queue中的对象进行第二次小规模的标记,如果要在finalize()方法中成功拯救自己,只要在finalize()方法中让该对象重新引用链上的任何一个对象建立关联即可。而如果对象这时还没有关联到任何链上的引用,那它就会被回收掉。

垃圾怎么回收?

标记-清除算法

先标记再清除,先利用可达性分析法将所有要标记出来的对象标记出来,在标记完之后再进行回收.

  • 优点: 不需要进行对象的移动,并且仅对不存活的对象进行处理,在"挂掉"的对象较少的情况下极为高效.
  • 缺点:标记和清除过程的效率都不高,标记完之后会产生大量碎片.

标记-整理算法

标记过程还上面一样德拉,就是在对垃圾对象处理过程有所不同.它不直接清理,它折磨,它将所有的对象移动到一起,然后将直接清理以外的内存.比较适合对老年代的处理.
优点:不存在碎片问题.
缺点:需要将所有的对象拷贝到新的地方,还要更新它们的引用地址.

复制算法

首先,复制算法比较适合新生代,在老年代中,执行复制操作,效率会变低.标记仍然是可达性分析法,

  • 优点:标记阶段和复制阶段可以同时进行,只需移动栈顶指针,按顺序分配实现,不考虑碎片.

分代收集算法

  • 新生代:几乎所有的新生成的对象都是先放在新生代.
  • 老年代:大对象直接进入老年代,长期存活的对象将进入老年代,默认为 15 岁。
  • 永久代:对永久代的回收主要回收两部分内容:废弃常量和无用的类。
  • 后续我会将这块再整理成一篇.
  • 希望大家不吝赐教!

在这里插入图片描述
废话时间:

如果客官觉得食用合适可不可以给一个免费的赞!谢谢谢了!慢走客官!建议打包收藏,下次再来。店小二QQ:309021573,欢迎骚扰!

猜你喜欢

转载自blog.csdn.net/AzirBoDa/article/details/112802508
今日推荐