深入理解Java虚拟机——垃圾收集器

  • 程序计数器、虚拟机栈、本地方法栈三个区域随 线程而生,随线程而灭,这几个区域的内存分配和回收都具备确定 性,在这几个区域内不需要过多考虑回收的问题

  • Java堆和方法区则不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样,我们只有 在程序处于运行期间时才能知道会创建哪些对象,这部分内存的分配和回收都是动态的,垃圾收集器所关注的是这部分内存

1. 对象已死?

堆中几乎存放着Java世界中所有的对象实例,垃圾收集器在对堆进行回收前,第一 件事情就是要确定这些对象有哪些还“存活”着,哪些已经“死去”(即不可能再 被任何途径使用的对象)。

1.1 引用计数算法

  • 给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器都为0的对象就是不可能再被使用的
  • Java语言中没有选用引用计数算法来管理内存,其中最主要的原因是它很难解决对象之间的相互循环引用的问题

1.2 根搜索算法

  • 通过一系列的名为 GC Roots 的对象作为起始点,从这些节点开始向下搜索, 搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的(可回收)。
  • 在Java语言里,可作为GC Roots的对象包括下面几种:
    • 虚拟机栈(栈帧中的本地变量表)中的引用的对象。
    • 方法区中的类静态属性引用的对象。
    • 方法区中的常量引用的对象。
    • 本地方法栈中JNI(即一般说的Native方法)的引用的对象。

1.3 四种引用

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

  • 强引用就是指在程序代码之中普遍存在的,类似“Object obj=new Object()”这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用 的对象。
  • 软引用用来描述一些还有用,但并非必需的对象。只有在内存不足时进行回收。提供了SoftReference类来实现软引用。
  • 弱引用也是用来描述非必需对象的。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象,提供了 WeakReference类来实现弱引用。
  • 虚引用也称为幽灵引用或者幻影引用,无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是希望能在这个对象被收集器回 收时收到一个系统通知。在JDK 1.2之后,提供了PhantomReference类来实现虚引 用。

1.4 生存还是死亡?

  • 在根搜索算法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑”阶段,要真正宣告一个对象死亡,至少要经历两次标记过程
    • 如果对象在进行根搜索后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且 进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将 这两种情况都视为没有必要执行
    • finalize()方法 是对象逃脱死亡命运的最后一次机会。
    • 如果对象要在finalize()中成功拯救自己—只要重新与引用链上的 任何一个对象建立关联即可
    • 这种自救的机会只有一次,因为一个对象的finalize()方法最多只会被系统 自动调用一次
  • finalize()能做的所有工作,使用tryfinally或其他方式都可以做得更好、更及时,大家完全可以忘掉Java语言中还有 这个方法的存在

1.5 回收方法区

  • 很多人认为方法区(或者HotSpot虚拟机中的永久代)是没有垃圾收集的,Java虚拟机规范中确实说过可以不要求虚拟机在方法区实现垃圾收集,而且在方法区进行垃圾收集的“性价比”一般比较低
  • 永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类
    • 回收废弃常量与回 收Java堆中的对象非常类似,例如一个字符串“abc”已经进入常量池中,到但是当前没有一个String对象引用这个“abc”常量。
    • 要判定一个类是否是“无用的类”需要同时满足下面3个条件:
      • 该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例
      • 加载该类的ClassLoader已经被回收
      • 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反 射访问该类的方法。

2.垃圾收集算法

2.1 标记-清除算法

最基础的收集算法是“标记-清除”(Mark-Sweep)算法,它的主要缺点有两个:一个是效率问题,标记和清除过程的效率都不高;另外一个是空间问题,标记清除之后会产生大 量不连续的内存碎片
在这里插入图片描述

2.2 复制算法

它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完 了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

实现简单,运行高效。只是这种算法的代价是将内存缩小为原来的一半,未免太高了一点。
在这里插入图片描述
现在的商业虚拟机都采用这种收集算法来回收新生代,只是并不需要按照1:1的比例来划分内存空间,HotSpot虚拟机默认Eden和Survivor的大小比例是8:1

当Survivor空间不够用时,需要依赖其他内存(这里指老年代)进行分配担保(Handle Promotion)。如果另外一块Survivor空间没有足够的空间存放上一 次新生代收集下来的存活对象,这些对象将直接通过分配担保机制进入老年代。

2.3 标记-整理算法

根据老年代的特点,有人提出了另外一种“标记-整理”(Mark-Compact)算法, 标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
在这里插入图片描述

2.4 分代收集算法

这种算法并没有什么新的思想,只是根据对象的存活周期的不同将内存划分为几块

新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。

老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使 用“标记-清理”或“标记-整理”算法来进行回收。

3.垃圾收集器

收集算法是内存回收的方法论,垃圾收集器就是内存回收的具体实现,不同的厂商、不同 版本的虚拟机所提供的垃圾收集器都可能会有很大的差别,直到现在为止还没有最好的收集器出现,更加没有万能的收集器,所以我们选择的只是对具体应用最合适的收集器

这里讨论的收集器基于Sun HotSpot虚拟机1.6版Update 22
在这里插入图片描述

3.1 Serial 收集器

  • 单线程:只会使用一个CPU或一条收集线程去 完成垃圾收集工作,进行垃圾收集时,必须暂停其他所有的工作线程(Stop The World),直到它收集结束
    在这里插入图片描述
  • 它依然是虚拟机运行在Client模式下的默认新生代收集器
  • 简单而高效

3.2 ParNew

  • 其实就是Serial收集器的多线程版本
  • 是许多运行在Server模式下的虚拟机中首选的新生代收集器。其中有一 个与性能无关但很重要的原因是,除了Serial收集器外,目前只有它能与CMS收集器配合工作

3.3 Parallel Scavenge

  • 是一个新生代收集器,它也是使用复制算法的收集器,又是并行的多线程收集器
  • Parallel Scavenge收集器的特点是它的关注点与其他收集器不同,CMS等收集器的关注点尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收 集器的目标则是达到一个可控制的吞吐量(Throughput)
    • 所谓吞吐量就是CPU用 于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运 行用户代码时间+垃圾收集时间)
    • 停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户的体 验;而高吞吐量则可以最高效率地利用CPU时间,尽快地完成程序的运算任务,主要 适合在后台运算而不需要太多交互的任务
  • Parallel Scavenge收集器也经常被称为“吞吐量优先”收集器
  • 自适应调节策略也是Parallel Scavenge收 集器与ParNew收集器的一个重要区别。

3.4 Serial Old

  • Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用“标记-整理”算法
  • 这个收集器的主要意义也是被Client模式下的虚拟机使用。

3.5 Parallel Old

  • Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记整理”算法。
  • 在注重吞吐量及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器

3.6 CMS

  • CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器
  • 基于“标记-清除”算法 实现的,它的运作过程相对于前面几种收集器来说要更复杂一些,整个过程分为4个步骤
    • 初始标记(CMS initial mark)
    • 并发标记(CMS concurrent mark)
    • 重新标记(CMS remark)
    • 并发清除(CMS concurrent sweep)
  • 初始标记、重新标记这两个步骤仍然需要“Stop The World”
  • 由于整个过程中耗时最长的并发标记和并发清除过程中,收集器线程都可以与用户 线程一起工作,所以总体上来说,CMS收集器的内存回收过程是与用户线程一起并发地执行的
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值