Gary

Java Garbage Collection基础

翻译自Oraclle官方文档

By Gary


Index:

1.概述

2.Java技术和JVM

3.垃圾回收

4.分代垃圾回收的过程

5.Java垃圾回收器


1.概述

目的

本教程将介绍垃圾回收如何与Hotspot JVM一起使用的基础知识。一旦了解了垃圾回收器的功能,了解如何使用Visual VM监视垃圾回收过程。最后,了解Java SE 7 Hotspot JVM中哪些垃圾回收器可用。

介绍

本OBE涵盖了Java中Java虚拟机(JVM)垃圾回收(GC)的基础知识。在OBE的第一部分中,提供了JVM的概述以及对垃圾回收和性能的介绍。接下来为学生提供了一个循序渐进的指导,了解垃圾回收如何在JVM中工作。接下来,为学习者提供了一个交流活动,尝试Java JDK中提供的一些监控工具,并将他们实践于刚刚了解的垃圾回收中的内容。最后,提供了一个部分,涵盖Hotspot JVM中可用的垃圾回收方案选项。

硬件和软件要求

以下是硬件和软件要求的列表:

先决条件

在开始本教程之前,您应该:


2.Java技术和JVM

Java概述

Java是Sun Microsystems于1995年首次发布的编程语言和计算平台。它是为Java程序(包括实用程序,游戏和业务应用程序)提供支持的基础技术。Java遍布全球超过8.5亿台个人计算机,全球数十亿台设备,包括手机和电视设备。Java由许多关键组件组成,整体而言,Java创建了Java平台。

Java运行时版本

当您下载Java时,您将获得Java运行时环境(JRE)。JRE由Java虚拟机(JVM),Java平台核心类和支持的Java平台库组成。所有这三个都需要在您的计算机上运行Java应用程序。使用Java 7,Java应用程序作为桌面应用程序从操作系统运行,作为桌面应用程序运行,但是使用Java Web Start从Web安装,或者作为浏览器中的Web Embedded应用程序(使用JavaFX)运行。

Java编程语言

Java是面向对象的编程语言,包括以下功能:

Java开发工具包

Java开发工具包(JDK)是开发Java应用程序的工具集合。使用JDK,您可以编译以Java编程语言编写的程序,并在JVM中运行它们。此外,JDK提供了用于打包和分发应用程序的工具。

JDK和JRE共享Java应用程序编程接口(Java API)。Java API是开发人员用来创建Java应用程序的预包装库的集合。Java API通过提供工具来完成许多常见的编程任务(包括字符串操作,日期/时间处理,网络和实现数据结构(例如列表,映射,堆栈和队列)),从而使开发变得更简单。

Java虚拟机

Java虚拟机(JVM)是一种抽象计算机。JVM是一个程序,看起来像写入要执行的程序的机器。这样,Java程序就被写入同一组接口和库。用于特定操作系统的每个JVM实现将Java编程指令转换为在本地操作系统上运行的指令和命令。这样,Java程序实现平台独立性。

在Sun Microsystems,Inc.完成的Java虚拟机的第一个原型实现模拟了由类似于当代个人数字助理(PDA)的手持设备托管的软件中的Java虚拟机指令集。Oracle当前的实现模拟了移动,桌面和服务器设备上的Java虚拟机,但Java虚拟机不承担任何特定的实现技术,主机硬件或主机操作系统。它不是固有的解释,但也可以通过将其指令集编译为硅CPU的指令集来实现。它也可以在微代码中或直接在硅中实现。

Java虚拟机也不知道什么是Java编程语言,只有特定的二进制格式,类文件格式。类文件包含Java虚拟机指令(或字节码)和符号表以及其他辅助信息。

为了安全起见,Java虚拟机对类文件中的代码强加了句法和结构限制。但是,具有可以用有效的类文件表达的功能的任何语言都可以由Java虚拟机托管。受到通常可用的独立于机器的平台的吸引,其他语言的实现者可以将Java虚拟机转换为其语言的传送工具。

探索JVM体系结构

Hotspot结构

HotSpot JVM具有支持强大的功能和功能基础的架构,并支持实现高性能和大规模可扩展性的能力。例如,HotSpot JVM JIT编译器生成动态优化。换句话说,它们在Java应用程序运行时进行优化决策,并生成针对底层系统架构的高性能本地机器指令。此外,通过其运行时环境和多线程垃圾回收器的成熟演进和持续工程,即使是最大的可用计算机系统,HotSpot JVM也可提供高可扩展性。

图片无法显示

JVM的主要组件包括类加载器,运行时数据区和执行引擎。

Hotspot主要组件

与性能相关的JVM的关键组件在下图中突出显示。

图片无法显示

当调整性能时,JVM有三个组件。堆是存储对象数据的地方。然后,该区域由启动时选择的垃圾回收器进行管理。大多数调优选项涉及到调整堆的大小,并为您的情况选择最合适的垃圾回收器。JIT编译器也对性能有很大的影响,但很少需要使用较新版本的JVM进行调优。

性能的基础

通常,在调整Java应用程序时,重点放在两个主要目标之一:响应性或吞吐量。随着教程的进行,我们将回顾这些概念。

响应

响应性是指应用程序或系统对所请求的数据进行响应的速度。例子包括:


3.垃圾回收

什么是自动垃圾回收?

自动垃圾回收是查看堆内存的过程,识别哪些对象正在使用,哪些对象不在,以及删除未使用的对象。使用对象或引用对象意味着程序的某些部分仍然保留指向该对象的指针。未使用的对象或未引用的对象不再由程序的任何部分引用。因此,可以回收未引用对象使用的内存。

在像C这样的编程语言中,分配和释放内存是一个手动过程。在Java中,重新分配内存的进程由垃圾回收器自动处理。基本过程可以描述如下。

步骤1:标记

该过程的第一步称为标记。这是垃圾回收器识别哪些内存块正在使用的地方,哪些不在。

图片无法显示

引用的对象以蓝色显示。未引用的对象以金色显示。所有对象在标记阶段进行扫描,以进行此确定。如果必须扫描系统中的所有对象,这可能是非常耗时的过程。

步骤2:正常删除

正常删除未引用的对象,将引用的对象和指针留给可用空间。

图片无法显示

内存分配器保存对可以分配新对象的可用空间块的引用。

步骤2a:通过压缩删除

为了进一步提高性能,除了删除未引用的对象之外,还可以压缩剩余的引用对象。通过将引用的对象一起移动,这使得新的内存分配更容易和更快。

图片无法显示

为什么要分代垃圾回收?

如前所述,标记和压缩JVM中的所有对象是低效的。随着越来越多的对象被分配,对象列表增长并且增长导致更长和更长的垃圾回收时间。然而,应用程序的实证分析表明,大多数对象生命周期都是短暂的。

以下是这些数据的示例。Y轴显示分配的字节数,X访问显示随时间改变分配的字节数。

图片无法显示

如您所见,随着时间的推移,对象数量越来越少。事实上,大多数对象的寿命非常短,如图左侧的较高值所示。

JVM代

从对象分配行为学到的信息可以用来增强JVM的性能。 因此,堆被分解成较小的部分或几代。堆分为:年轻代,老年代,永久代

图片无法显示

年轻代是所有新对象被分配和老化的地方。当年轻代填满时,这会导致少量的垃圾回收。小样本可以优化,假设对象死亡率高。一个充满死亡对象的年轻代很快就被回收起来了。一些幸存的对象被老化,最终转移到老年代。

Stop the World Event-所有小垃圾回收都是“Stop the World”事件。这意味着所有应用程序线程都将停止,直到操作完成。

老年代用于储存长寿命的物品。通常,对于年轻代对象设置一个阈值,当满足该年龄时,该对象将被移动到老年代。最终需要回收老年代。这个事件被称为主要的垃圾回收。

主要垃圾回收也是停止世界事件。通常,一个主要的回收要慢得多,因为它涉及所有活动的对象。因此,对于响应性应用程序,应尽量减少主要的垃圾回收。另请注意,主要垃圾回收的Stop World事件的时间受到老年代空间所使用的垃圾回收器的影响。

永久代包含JVM所需的元数据来描述应用程序中使用的类和方法。永久代在运行时由基于应用程序使用的类填充JVM。此外,java SE库类和方法可以被存储在这里。

如果JVM发现不再需要类,并且可能需要其他类的空间,则可以回收(卸载)类。永久代被包含在一个完整的垃圾回收中。


4.分代垃圾回收的过程

现在你明白了为什么堆被分成不同的代,现在是时候看这些空间是如何相互作用的。随后的图片将遍历JVM中的对象分配和老化过程。

1.首先,将任何新对象分配给eden空间。 两个幸存空间开始空。

图片无法显示

2.当eden空间填满时,会触发小的垃圾回收。

图片无法显示

3.被引用的对象被移动到第一幸存空间。当eden空间被清除时,未引用的对象被删除。

图片无法显示

4.在下一个小的GC中,同样的事情发生在eden空间。未引用的对象被删除,引用对象被移动到幸存空间。然而,在这种情况下,它们被移动到第二幸存空间(S1)。另外,来自第一幸存空间(S0)的最后一个小的GC的对象的年龄增加并被移动到S1。一旦所有幸存的对象都移动到S1,S0和eden都被清除。请注意,我们现在在幸存空间中具有不同的老化对象。

图片无法显示

5.在下一个小的GC中,重复相同的过程。但这次幸存空间转换。被引用的对象被移动到S0。幸存的对象是老化的。Eden和S1被清除。

图片无法显示

6.经过小的GC,当老年对象达到一定的年龄阈值(在这个例子中8),他们从年轻代变为老年代。

图片无法显示

7.年轻代继续给新的对象分配内存将继续推动对象往老年代空间的变迁。

图片无法显示

8.所以这几乎涵盖了与年轻代的整个过程。最终,一个主要的GC将在老年代进行,清理和压缩这个空间。

图片无法显示


5.Java垃圾回收器

在本节中,您将了解可用于Java的垃圾回收器以及您需要选择它们的命令行开关。

常用的堆相关的开关

有许多不同的命令行开关可以与Java一起使用。 本节介绍一些本OBE中使用的更常用的开关。

SwitchDescription
-Xms设置JVM启动时的初始堆大小。
-Xmx设置最大堆大小。
-Xmn设置年轻代的大小。
-XX:PermSize设置永久代的起始大小。
-XX:MaxPermSize设置永久代的最大大小。

串行GC

串行回收器是Java SE 5和6中客户端风格计算机的默认设置。对于串行回收器,次要和主要垃圾回收都是连续完成的(使用单个虚拟CPU)。 此外,它使用标记压缩的回收方法。此方法将较老的内存移动到堆的开头,以便在堆的末尾将新的内存分配制作为单个连续的内存块。这种内存压缩使得将更多的内存分配给堆很快。

用例

串行GC是大多数应用程序的首选垃圾回收器,它们没有低暂停时间要求并在客户端机器上运行。它仅利用单个虚拟处理器进行垃圾回收工作(因此,它的名称)。然而,在今天的硬件上,串行GC可以通过几百MB的Java堆有效地管理大量非平凡的应用程序,而相对较短的最坏情况暂停(完全垃圾回收约需要几秒钟)。

串行GC的另一个常见用途是在同一台机器上运行大量JVM(在某些情况下比可用处理器更多的JVM)的环境中。在JVM执行垃圾回收的这种环境中,最好只使用一个处理器来尽可能减少对其余JVM的干扰,即使垃圾回收可能持续更长时间。而串行GC适合这种权衡。

最后,随着嵌入式硬件的扩展,内存最少,内核少,串行GC可能会复出。

命令行开关

要使用串行回收器:

-XX:+UseSerialGC

并行GC

并行垃圾回收器使用多个线程来执行年轻代垃圾回收。默认情况下,在具有N个CPU的主机上,并行垃圾回收器在回收中使用N个垃圾回收器线程。垃圾回收器线程的数量可以通过命令行选项进行控制:

-XX:ParallelGCThreads=desired number

在具有单个CPU的主机上,即使已经请求并行垃圾回收器,也使用默认垃圾回收器。在具有两个CPU的主机上,并行垃圾回收器通常与默认垃圾回收器一样执行,并且可以预期在具有两个以上CPU的主机上可以减少年轻代回收器暂停时间。

用例

并行回收器也称为吞吐量回收器。因为它可以使用多个CPU来加快应用程序吞吐量。当需要做大量工作并且可以长时间停顿时,应该使用这个回收器。例如,批处理,或执行大量数据库查询。

-XX:+UseParallelGC

通过这个命令行选项,您可以得到一个具有单线程老年代回收器的多线程年轻代回收器。该选项也是老年代的单线程压缩。

-XX:+UseParallelOldGC

使用-XX:+ UseParallelOldGC选项,GC既是多线程的年轻代回收器又是多线程的老年代回收器。它也是一个多线程压缩回收器。HotSpot只能在老年代中进行压缩。HotSpot的年轻代被认为是一个复制回收器; 因此,不需要压缩。

压缩描述了以对象之间没有缝隙的方式移动对象的动作。垃圾回收扫描后,对象之间可能有缝隙。压缩移动对象,使得没有剩余的缝隙。垃圾回收器可能是非压缩回收器。因此,并行回收器和并行压缩回收器之间的差异可能是垃圾回收扫描之后的空间压缩。前者不会。

并发标记扫描(CMS)回收器

并发标记扫描(CMS)回收器(也称为并发低暂停回收器)回收老年代。它尝试通过与应用程序线程同时执行大部分垃圾回收工作来最小化由于垃圾回收引起的暂停。通常,并发的低暂停回收器不会复制或压缩活动对象。在不移动活动对象的情况下完成垃圾回收。如果碎片成为问题,请分配更大的堆。

注意:年轻代的CMS回收器使用与并行回收器相同的算法。

用例

CMS回收器应用于需要较短暂停时间的应用程序,并可与垃圾回收器共享资源。用于包括响应事件的桌面UI应用程序,响应请求的Web服务器或响应查询的数据库。

命令行开关

使用CMS回收器:

-XX:+UseConcMarkSweepGC

并设置线程使用次数:

-XX:ParallelCMSThreads=N

G1垃圾回收器

G1垃圾回收器在Java 7中可用,旨在成为CMS回收器的长期替代品。G1回收器是一个并行,并发和增量式压缩的低暂停垃圾回收器,与之前描述的其他垃圾回收器具有完全不同的布局。但是,详细的讨论超出了本OBE的范围。

命令行开关

使用G1回收器:

-XX:+UseG1GC