《JVM G1源码分析和调优》 —1 垃圾回收概述
第1章
垃圾回收概述
Java的发展已经超过了20年,已是最流行的编程语言。为了更好地了解和使用Java,越来越多的开发人员开始关注Java虚拟机(JVM)的实现技术,其中垃圾回收(也称垃圾收集)是最热门的技术点之一。目前G1作为JVM中最新、最成熟的垃圾回收器受到很多的人关注,本书从G1的原理出发,介绍新生代收集、混合收集、Full GC、并发标记、Refine、Evacuation等内容。本章先回顾Java语言的发展历程,然后介绍JVM中一些常用的概念以便与读者统一术语,随后介绍垃圾回收的主要算法以及JVM中实现了哪些垃圾回收的算法。
1.1 Java发展概述
Java平台和语言最开始是SUN公司在1990年12月进行的一个内部研究项目,我们通常所说的Java一般泛指JDK(Java Developer Kit),它既包含了Java语言和开发工具,也包含了执行Java的虚拟机(Java Virtual Machine,JVM)。从1996年1月23日开始,JDK 1.0版本正式发布,到如今Java已经经历了23个春秋。以下是Java发展历程中值得纪念的几个时间点:
1998年12月4日JDK迎来了一个里程碑版本1.2。其技术体系被分为三个方向,J2SE、J2EE、J2ME。代表技术包括EJB、Java Plug-in、Swing;虚拟机第一次内置了JIT编译器;语言上引入了Collections集合类等。
2000年5月8日,JDK1.3发布。在该版本中Hotspot正式成为默认的虚拟机,Hotspot是1997年SUN公司收购LongView Technologies公司而获得的。
2002年2月13日,JDK1.4发布。该版本是Java走向成熟的一个版本。从此之后,每一个新的版本都会增加新的特性,比如JDK5改进了内存模型、支持泛型等;JDK6增强了锁同步等;JDK7正式支持G1垃圾回收、升级类加载的架构等;JDK8支持函数式编程等。
2006年11月13日的JavaOne大会上,SUN公司宣布最终会把Java开源,由OpenJDK组织对这些源码独立管理,从此之后Java程序员多了一个研究JVM的官方渠道。
2009年4月20日,Oracle公司宣布正式以74亿美元的价格收购SUN公司,Java商标从此正式归Oracle所有,自此Oracle对Java的管理和发布进入了一个新的时期。
随着时间的推移,JDK 9和JDK 10也已经正式发布,但是JDK 9和JDK 10并不是Oracle长期支持的版本(Long Term Support),这意味着JDK 9和JDK 10只是JDK 11的一个过渡版本,它们只用于整合新的特性,当下一个版本发布之后,这些过渡版本将不再更新维护。2018年9月25日JDK 11正式发布,随着新版本的发布,Oracle公司未来对JDK的支持也会变化。按照现在的声明,从2019年1月起对于商业用户,Oracle公司对JDK 8不再提供公共的更新,从2020年12月起对个人用户也不再提供公共的更新。
G1作为CMS的替代者,一直吸引着众多Java开发者的目光,自从JDK 7正式推出以来,G1不断地增强,并从JDK 8开始越来越成熟,在JDK 9、JDK 10、JDK 11中都成为默认的垃圾回收器。实际上也有越来越多的公司开始在生产环境中使用G1作为垃圾回收器,有一篇文章描述了JDK 9中GC的基准测试(benchmark),表明G1已经优于其他的GC。可以预见随着JDK 11的推出,会有越来越多的公司和个人使用G1作为生产环境中的垃圾回收器。
G1的目标是在满足短时间停顿的同时达到一个高的吞吐量,适用于多核处理器、大内存容量的系统。其实现特点为:
短停顿时间且可控:G1对内存进行分区,可以应用在大内存系统中;设计了基于部分内存回收的新生代收集和混合收集。
高吞吐量:优化GC工作,使其尽可能与Mutator并发工作。设计了新的并发标记线程,用于并发标记内存;设计了Refine线程并发处理分区之间的引用关系,加快垃圾回收的速度。
新生代收集指针对全部新生代分区进行垃圾回收;混合收集指不仅仅回收新生代分区,同时回收一部分老生代分区,这通常发生在并发标记之后;Full GC指内存不足时需要对全部内存进行垃圾回收。
并发标记是G1新引入的部分,指的是在Mutator运行的同时标记哪些对象是垃圾,看到这里大家一定非常好奇G1到底是怎么实现的,举一个简单的例子。比如你的妈妈正在打扫房间,扫房房间需要识别哪些物品有用哪些无用,无用的物品就是垃圾。同时你正在房间活动,活动的同时你可能往房间增加了新的物品,也可能把房间的物品重新组合,也可能产生新的无用物品。最简单的垃圾回收器如串行回收器的做法就是在打扫房间标识物品的时候,你要暂停一切活动,这个时候你的妈妈就能完美地识别哪些物品有用哪些无用。但最大的问题就是需要你暂停一切活动直到房间里面的物品识别完毕,在实际系统中意味着这段时间应用程序不能提供服务。G1的并发标记就是在打扫房间识别物品有用或者无用的同时,你还可以继续活动,怎么正确做标记呢?一个简单的办法就是在打扫房间识别垃圾物品开始的时候记录你增加了哪些物品,动过哪些物品。然后在物品标记结束的时候对这些变更过的物品重新标记一次,当然在这一次标记时需要你暂停一切活动,否则永远也没有尽头,这通常称为再标记(Remark)。这个就是所谓的增量并发标记,在G1中具体的算法是Snapshot-At-The-Beginning(SATB),关于这个算法我们会在第6章详细介绍。Refine线程也是G1新引入的,它的目的是为了在进行部分收集的时候加速识别活跃对象,具体介绍参见第4章。
本书依托于jdk8u的源代码来介绍JVM如何实现G1,通过源代码的分析理解算法以及了解G1提供的参数的具体意义;最后还会给出一些例子,通过日志,分析该如何调整参数以达到性能优化。
这里提到的jdk8u是指OpenJDK的代码,OpenJDK是SUN公司(现Oracle)推出的JDK开源代码,因为标准的JDK(这里指Oracle版的JDK)会有一些内部功能的代码,那些代码在开源的时候并未公开。在2017年9月Oracle公司宣布Oracle JDK和OpenJDK将能自由切换,Oracle JDK也会依赖OpenJDK的代码进行构建,所以通常都是使用OpenJDK的代码进行分析和研究。读者可以自行到OpenJDK的官网上下载源代码,值得一提的是,JDK的代码会随着bug修复不断改变,所以为了保持阅读的一致性,我把本书使用的代码推送到GitHub上,也使用该版本进行编译调试。
- 点赞
- 收藏
- 关注作者
评论(0)