GC入门超详解

GC

我们知道在整个JVM管理的内存中,程序计数器、虚拟机栈和本地方法栈都是属于线程私有的,因此这几个区域的内存分配和内存回收都具备确定性,不需要考虑内存回收,因为当方法或线程结束的时候,内存自然就被释放了

而Java堆Heap则是由垃圾收集器来管理,有着很显著的不确定性:只有程序运行期间,我们才能知道程序究竟会创建哪些对象,创建多少个对象,这部分的内存分配和回收是动态的,垃圾收集器关注的正是这部分Heap内存该如何管理

一、何为垃圾?

1、垃圾概念

在Heap空间存放着Java世界中几乎所有的对象实例,当Heap空间不足需要进行垃圾回收的时候,首先就要确定哪些对象还 “ 活着 ”,哪些对象已经 “ 死亡 ”

当没有任何引用指向的一个对象或者一堆对象,就认为这些对象不可能再被任何途径使用,也就没有存在价值,就被认定为垃圾

2、再谈引用

1、传统引用定义

如果reference类型的数据中存储的数值代表另外一块内存的起始地址,就称该reference数据时代表某块内存、某个对象的引用

这种定义并没有什么不对,但是有点狭隘和老旧,在这种定义下一个对象只有 “被引用” 和 “未被引用” 两种状态。

在目前情况下我们希望能这样描述一类对象:当内存空间还足够时,能够保留在内存之中,如果内存空间在进行垃圾回收之后仍然非常紧张,此时就可以抛弃此类对象——很多系统的缓存功能符合这样的应用场景

2、现在引用定义

在JDK1.2之后,Java对引用的概念进行了扩充,目前的引用分类为四种——强、软、弱、虚,这四种引用的强度依次逐渐减弱

  • 强引用:最传统的引用定义,是代码中普遍存在的引用赋值,即类似 “Object o = new Object()” 这种引用关 系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收被引用的对象
  • 软引用:用来描述一些还有用,但非必须的对象。只被软引用关联的对象,在系统将要发生内存溢出前,才会把这类对象列到回收范围之内进行二次回收。软引用关联的对象遇到垃圾回收的时候,未必会被回收,只有内存不够的时候才会回收软引用——软引用非常适合做缓存使用
  • 弱引用:也是用来描述一些还有用,但非必须的对象,但是强度比软引用更弱一些。软引用关联的对象遇到垃圾回收的时候,未必会被回收,只有内存不够的时候才会回收软引用,而弱引用只要遭遇到GC就会回收——弱引用非常适合用在容器里
  • 虚引用:也称 “幽灵引用” ,基本没用,了解即可

这里只是大致说明了一下强、软、弱、虚四种引用,为了不喧宾夺主,详细细节另外扩充。

二、如何定位垃圾

1、引用计数法

Reference Count——引用计数法

在对象内存中,有一块区域存放一个计数器,当有一个引用指向此对象计数器就加一,当有一个引用断开计数器就减一,当计数器为0时此对象就被认定为垃圾

在这里插入图片描述

但是看似简单的引用计数法需要大量额外处理才能保证正常工作,而且有一个引用计数法无法解决的问题——循环引用

在这里插入图片描述

由于引用计数法的局限,所以在JVM并没有使用引用计数法来管理内存

2、可达性分析算法(重要)

可达性分析算法——Reachability Analysis:当前主流的程序语言都是通过可达性分析算法来判定对象是否存活,比如JVM

这个算法的核心思路就是通过一系列称为 “ GC Roots” 的根对象作为起始节点集,从这些节点开始根据引用关系向下搜索,搜索过程所走过的路径称之为 “引用链” ,如果某个对象到GC Roots间没有任何引用链相连(即不可达),则认为此对象时不可能被再次使用,认定为垃圾

在这里插入图片描述

三、如何回收垃圾

1、标记清除算法

清除算法——Mark Sweep,最早也是最基础的垃圾收集算法

算法执行过程

算法分为 “ 标记 ” 和 “ 清除 ” 两个阶段:首先标记所有需要回收的对象,然后统一回收所有被标记的对象;也可以反过来,首先标记存活的对象,然后统一回收所有未被标记的对象

算法特点

主要缺点有两个:

  1. 执行效率不稳定:需要两遍扫描,如果大部分对象是需要被回收的,需要进行大量的标记和清除的动作,执行效率随对象数量的增长而降低
  2. 容易产生碎片:标记、清除之后会产生大量的不连续的内存碎片

在这里插入图片描述

适用于存活对象比较多的情况下

2、标记复制算法

算法执行过程

标记复制算法——Copy,为了解决标记—清除算法面对大量可回收对象时效率执行低的问题,使用 “ 半区复制” 算法,将内存分为容量大小的两块,每次只使用其中一半。当一半内存满了,只需一遍扫描筛选所有还活着的对象,并把存活对象复制到另一半,并把这一半内存整体清空。

当只有少数对象是存活的时候,只需要复制少数对象即可,而且每次都是针对整个半区进行内存回收,不会产生内存碎片

算法特点

  1. 内存使用率低:可以发现我们每次有效使用的内存只有一半,空间大量浪费
  2. 适合存活对象较少的情况,实现简单,只需要扫描一次运行高效,且不会产生内存碎片

在这里插入图片描述

由于普通标记—复制算法要求保留一半区域,造成严重的内存浪费,现代虚拟机采用了一种更优化的半区复制分代策略——“Apple式回收” :把新生代分为一块较大的Eden区和两块较小的Survivor空间,每次分配内存只使用Eden区和一块Survivor。当新生代发生垃圾收集时,将Eden和Survivor中仍然存活的

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值