JVM垃圾回收算法与概念

什么是垃圾?

运行程序中没有任何指针指向的对象。

为什么要gc?

不进行垃圾回收,内存迟早会被消耗殆尽,没有gc不能保证引用程序的正常优化。

内存溢出:需要创建了新的对象,没有空闲空间,并且在垃圾收集后也无法提供更多内存意思是在报OOM时之前会执行一次gc。但也不绝对,比如new一个对象的内存超过了堆空间直接报OOM。

内存泄漏严格来说,只有对象不再被程序使用,但是gc又不能回收他们的情况,叫内存泄漏。一、io流、jdbc连接、ThreadLocal使用后未关闭释放内存空间,导致可用内存变少。二、单例模式的生命周期和程序是一样长的,如果在此程序中持有对外部对象的引用的话,那么这个外部对象是不能被回收的,导致内存泄漏。

标记阶段:引用计数算法

在GC执行垃圾回收之前需要判定那些是存活对象,那些是死对象(当一个对象已经不再被任何存活的对象继续引用时,宣判死亡)。

  • 引用计数器的属性,用于记录对象被引用的情况。
    对于一个对象A,只要任何一个对象引用了A, 则A的计数器就加1,当引用失效时计数器就减1.当对象A的计数器为0时即表示对象A不可能被使用,可回收。
    优点:实现简单,垃圾对象便于辨识;判定效率高,回收没有延迟。
    缺点:
  • 它需要单独的字段存储计数器,增加了储存空间开销。
  • 每次赋值都需要重新更新计数器,增加时间开销
  • 存在无法处理循环引用的情况,这是致命缺陷,是java垃圾回收器没有采用的原因。
    在这里插入图片描述

标记阶段:可达性分析算法

又名:根搜索算法,追踪性垃圾收集
相比引用计数算法而言,可达性分析算法解决了循环引用问题,防止内存泄漏。

基本思路:
  • 可达性分析算法是以根对象集合为起始点,按照从上至下的方式搜索被根对象集合所连接的目标对象是否可达。
    在这里插入图片描述
  • 内存中存活的对象都会被根对象集合直接或间接连接着,搜索路径称为引用链
GC Roots元素:

在这里插入图片描述

  • 虚拟机栈中引用的对象
  • 本地方法栈内JNI引用的对象
  • 方法区中静态属性引用的对象
  • 方法区中常量引用的对象
  • 所有同步锁synchronized持有的对象
  • java虚拟机内部引用
  • 反映java虚拟机内部情况JMXBean、

对象的finallization机制

在垃圾回收这个对象之前,先调用这个对象的finalize()方法。
允许子类重写,用于对象被回收时进行资源释放。(比如关闭文件、套接字和数据库连接)
不要主动调用某个对象得finalize()方法,应交给垃圾回收机制调用,原因:

  • 在finalize()时可能会导致对象复活
  • finalize()方法的执行时间是没有保障的,他完全由GC线程决定,在极端情况下,若不放生gc,则finalize()方法将没有执行机会
  • 一个糟糕的finalize()会严重影响GC性能
由于finalize()方法,对象有三种状态:
  • 可触及的:从根节点开始,可以达到这个对象
  • 可复活的:对象的所有引用都被释放,但是对象又可能在finalize()中复活
  • 不可触及的:对象的finalize()被调用,并且没有复活,就会进入不可触及状态。不可触及对象是无法复活的。因为finalize()只能被调用一次
判定对象是否可回收(2次标记)

1、如果objA对象 到GC Roots 没有引用链,则进行第一次标记
2、进行筛选,判断此对象是否有必要执行finalize()方法

  • 一、objA没有重写finalize()方法,或则finalize()方法已经被虚拟机调用过,则认为没有必要执行,objA被判定是不可触及的
  • 二、如果objA重写了finalize()方法,且未执行过,那么objA会被插入到F-Queue队列中,由一个虚拟机自动创建的、低优先级的finalize线程触发finalize()方法执行。
  • 三、finalize()方法是对象逃脱死亡的最后机会,如果objA在finalize()方法中和任何对象建立联系,那么在第二次标记时,objA将被“移除即将回收”集合。但是finalize()方法只能执行一次,下次就必定死亡了。

清楚阶段:标记-清除算法(Mark Sweep)

  • 标记:collector从根节点开始遍历,标记所有被引用的对象。标记可达对象。
  • 清楚:collector对堆内存从头到尾进行线性遍历,发现没有被标记的对象将其回收
    在这里插入图片描述

缺点:
1、效率一般(原因第二次线性遍历)
2、在进行GC时,需要停止整个应用程序,用户体验差
3、这个清理出来的内存空间是不连续的,产生内存碎片。

清楚阶段:复制算法(Copying)

这里的复制算法在方法区中的年轻代的survivor区中应用,to、from,谁空谁是to。
为了解决标记-清楚算法在垃圾回收效率的问题。
在这里插入图片描述
优点:

  • 没有标记和清楚过程,实现简单,运行高效
  • 复制过去以后保证空间的连续性,不会出现“碎片”问题

缺点:

  • 需要两倍的内存空间
  • 复制而不是移动,意味GC需要维护region之间对象引用关系。

特别的:

  • 如果系统中存活对象很多,那么复制对象数量就会影响性能。

清楚阶段:标记压缩算法(Mark Compact)

  • 标记:从根节点开始遍历标记所有被引用的方法
  • 压缩:将所有存活的对象压缩到内存的一端
    在这里插入图片描述
    标记-清楚-压缩(碎片整理)

缺点:

  1. 从效率上来说,标记-整理算法要低于复制算法
  2. 移动对象的同时,如果对象被其它对象引用,则还需要调整引用的地址
  3. 移动过程中,需要全程暂停用户应用程序。STW

小结

复制算法效率最高,但消耗量太多的内存

分代收集算法(Generation Collecting)

不同对象的生命周期长短不一,分代回收提高效率。
目前所有的GC都是采用分代收集算法执行垃圾回收。

  • 年轻代:区域相对于老年代较小,对象生命周期短、存活率低,回收频繁。(复制算法)
  • 老年代:区域较大,对象生命周期长、存活率搞,回收频率低。(标记清楚与标记压缩混合使用)

增量收集算法,分区算法

以上算法在垃圾回收过程中都会停止其他线程运行(stop the world)。
增量收集算法:垃圾收集线程只收集一小片区域的内存空间,接着切换到引用程序线程。依次反复。
增量收集算法的基础还是标记清除、标记压缩算法。着重点在于处理线程间冲突的妥善处理,允许垃圾收集线程以分断的方式完成标记、清理、复制工作。
**缺点:**垃圾收集过程中间断性执行应用程序代码,能减少系统的停顿时间。但是由于线程切换和上下文的消耗使得总体成本上升,造成系统吞吐量下降

相关概念

System.gc()理解

调用System.gc()提醒jvn的垃圾回收器执行full gc不确定是否马上执行gc。但是再调用System.runFinalZation()方法会强制执行finalize()方法。

垃圾回收的并行与并发

并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且者几个程序都是在同一个cpu上运行并发不是真正意义上的“同时执行”,只是把cpu一个时间段划分成几个片段,然后在这几个片段中来回切换运行。(交替执行)
在这里插入图片描述
并行:系统有多个cpu,当一个cpu执行一个进程时,另一个cpu可以执行另外一个进程,两个进程互不抢占资源,同时进行称为并行。
在这里插入图片描述
两者对比:
并发:指多个事件,在同一个时间段同时发生,互相抢占资源,一个cpu
并行:指多个事件,在同一个时间点同时发生,不互相抢占资源,多个cpu

安全点与安全区域

safe point
程序执行时并非所有地方都能停顿下来开始 gc,只有在特定位置才能停顿下来,这些位置称为安全点。
safe point 的选择非常重要,如果太少可能会导致gc等待时间过长,如果太多可能会导致运行时性能问题。

根据让程序长时间执行的特征选着执行时间较长的指令作为safe point,如方法的调用、循环跳转、异常跳转。

如何在发生GC时,检查所有线程都跑到最近的安全点停顿下来呢?

  1. 抢断式中断:(目前虚拟机没有采用了)首先中断所有线程。如果还有线程不在安全点,就恢复线程,让线程跑到安全点。
  2. 主动式中断:设置一个中断标志,各个线程运行到safe point的时候主动轮询这个标志,如果中断标志为真则将自己进行中断挂起。

safe region
当有线程sleep状态,短时间不能走到safe point, JVM就不能进行gc,安全区域就来解决这类问题。

safe region是指在一段代码片段中,对象的引用关系不会发生变化,在这个区域中的任何位置开始GC都是安全的

安全区域发生gc依然会继续执行,要出安全区域了但gc没有结束才会等待

引用

以下引用都能触及(可达),强度依次减弱。

强引用

不回收
Object 0 = new Object();
强引用可触及(可达的),垃圾收集器永远不会回收的对象。
造成内存泄漏的原因之一。

软引用

在内存不足即回收
内存足够时不会回收
在这里插入图片描述

弱引用

发现即回收
WeakHashMap
在这里插入图片描述

虚引用

用于对象回收跟踪
由于虚引用可以将跟踪对象的回收时间,因此,也可以将一些资源释放操作放置在虚引用中去执行。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值