浅谈垃圾回收算法

首先给你们讲一个梗,为何Java程序员愈来愈多,而C++程序员愈来愈少呢?—— 那还不是由于不用管理内存啊程序员

在C++中,本身new的对象,在不用的时候须要手动释放。而在Java里,你能够释放你的双手,将这项工做交给JVM本身管理。在JVM里面,这种机制叫作垃圾回收(Garbage Collection)机制。
算法

ps:本文不涉及垃圾收集器,主要是讲垃圾回收的思路。cdn

Java内存管理

Java的变量一共存储在三个地方、方法区、堆、栈对象

方法区

主要存放常量,静态常量、常量池,在程序编译的时候,这块内存就已经被分配出来了,伴随程序的一辈子。blog

是JVM管理的内存最大的一块,存放全部的实例对象,也是垃圾收集器管理的主要区域。也称为“GC堆”生命周期

当方法被执行的时候,方法内部的局部变量就会被存储在栈里,当方法执行完毕的时候,刚刚存储的局部变量会被自动释放。ip

回收流程

  1. 首先会判断对象是否存活
  2. 利用回收算法对对象进行回收

如何判断对象是否存活?

前面说到垃圾收集器管理的是堆,在回收以前,一般须要判断这个对象是不是存活的。内存

引用计数法

遍历对象,当这个对象被其余对象引用的时候计数器就加1,当引用失效的时候,计数器减1。当对象的计数器为0时,则表示该对象没有被引用。这种方法效率很高,可是没法解决互相引用的问题。所以主流JVM都未采用这种方法。虚拟机

可达性分析

这一算法的思想是从GC Roots做为起始点,从这些节点往下搜索,搜索的路径叫作引用链,当一个对象没有任何引用链能够到GC ROOT的时候,则证实该对象是不可用的。通常想要宣告一个对象死亡,至少要经历两次标记,即标记-筛选-标记。 it

image

垃圾回收算法

标记-清除

过程
  1. 标记出全部须要回收的对象
  2. 清除全部被标记的对象

不足:

  1. 清除和标记两个效率都不高。
  2. 会产生大量的内存碎片,会致使在分配较大对象的时候,若是找不到足够的空间会再次清除。

image

复制算法

过程:

将可用内存分为两块,每次只使用其中的一块。一块内存使用完的时候,将存活下来的对象复制到另外一块中,再清除这一块内存。这样就没什么内存碎片可言了。

优势:

效率高,解决了内存碎片的问题。

缺点:只能使用一半的空间。

image
不过现代虚拟机几乎都不是1:1分配空间,由于大部分都对象的生命周期都不长。例如HotSpot虚拟机,默认的Eden和Survivor的大小比例是8:1。

标记 — 整理算法

复制-收集算法,在对象存活率高的状况下,要进行屡次复制,效率比较低。因此说为此提出了“标记-整理”算法。

过程:

标记过程同上,而后让全部存活的对象移动到另外一端,而后清理掉边界外的内存。

image

分代收集算法

分代收集算法比较常见,通常把堆分为新生代和老年代,这样能够根据各个年代的特色采用适当的算法。

  • 新生代:新生代会有大批对象死去,少许对象存活。适合复制算法。
  • 老年代:老年代中对象存活率较高,没有额外 空间来分配担保。因此说适合采用标记-清除 或 标记-整理算法。
    ps:空间分配担保是用来检测老年代最大可用的连续空间是否大于新生代全部活着的对象的总空间。

引用类型

在JDK1.2以后,Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)四种,这四种引用强度依次逐渐减弱。

强引用(Strong reference)

就是指在程序代码之中广泛存在的,例如new出来的对象,只要强引用还在,该对象就不会被回收。

软引用(Soft Reference)

用来描述一些还有用但并不是必须的对象。在内存即将溢出以前,垃圾收集器会将这些对象进行回收。

弱引用(Weak Reference)

用户描述非必须对象的。不管当前内存是否足够,随时都有可能被回收。

虚引用(Phantom Reference)

虚引用不会影响该对象都生命周期,只会在该对象被回收的适合得到一个系统通知。