Java垃圾收集算法[直到Java 9],
Java垃圾收集算法[直到Java 9],
垃圾收集(GC)一直是Java流行的重要特征之一。垃圾收集是Java中用于释放未使用内存的机制。本质上,它正在追踪仍然使用的所有对象,并将其余对象标记为垃圾。Java的垃圾收集被认为是一种自动内存管理模式,因为程序员不必将对象指定为可以解除分配。垃圾收集在低优先级线程上运行。
在本教程中,我们将介绍与内存分配/解除分配相关的各种概念,在场景后运行的算法以及自定义此行为的选项。
目录
对象生命周期
垃圾收集算法
标记和清除
并发标记扫描(CMS)垃圾收集
串行垃圾收集
并行垃圾收集
G1垃圾收集
自定义选项
摘要
对象生命周期
java的对象生命周期可以分为3个阶段:
-
对象创建
要创建对象,通常我们使用
new
关键字。例如Object obj = new Object();
创建对象时,会分配特定数量的内存来存储该对象。分配的内存量可能因架构和JVM而异。
-
正在使用的对象
到时候,对象由应用程序的其他对象使用(其他活动对象具有指向它的引用)。在使用过程中,对象驻留在内存中,可能包含对其他对象的引用。
-
对象破坏
垃圾收集系统监视对象,并在可行的情况下计算每个对象的引用数。如果没有对象的引用,则无法使用当前运行的代码到达它,因此释放相关内存非常有意义。
垃圾收集算法
对象创建由您编写的代码完成; 和用于使用其提供的功能的框架。作为java开发人员,我们不需要释放内存或取消引用对象。它由Gargabe收集器在JVM级别自动完成。自从java开始以来,已经有很多关于在场景后面运行以释放内存的算法的更新。我们来看看它们是如何工作的?
标记和扫描
它是初始和非常基本的算法,分两个阶段运行:
- 标记活动对象 - 找出所有仍然存活的对象。
- 删除无法访问的对象 - 摆脱其他一切 - 所谓的死亡和未使用的对象。
首先,GC将一些特定对象定义为垃圾收集根。例如,当前正在执行的方法的局部变量和输入参数,活动线程,加载类的静态字段和JNI引用。现在,GC遍历内存中的整个对象图,从这些根开始,然后跟随从根到其他对象的引用。GC访问的每个对象都标记为活动。
需要停止应用程序线程以便标记发生,因为如果它不断变化,它就无法真正遍历图形。它被称为停止世界暂停。
第二阶段是摆脱未使用的对象以释放内存。这可以通过多种方式完成,例如
- 正常删除 - 正常删除会删除未引用的对象以释放空间并保留引用的对象和指针。内存分配器(哈希表类型)保存对可以分配新对象的可用空间块的引用。
它经常作为
mark-sweep
算法重新编写。正常删除 - 标记和扫描
- 使用压缩删除 - 仅删除未使用的对象效率不高,因为可用内存块分散在存储区域中并导致OutOfMemoryError,如果创建的对象足够大且找不到足够大的内存块。
要解决此问题,在删除未引用的对象后,将对剩余的引用对象进行压缩。这里压缩是指将引用对象移动到一起的过程。这使得新内存分配更容易,更快捷。
它经常作为
mark-sweep-compact
算法重新编写。通过压缩删除
- 复制删除 - 它与标记和比较方法非常相似,因为它们也会重新定位所有活动对象。重要的区别在于重定位的目标是不同的内存区域。
它经常作为
mark-copy
算法重新编写。复制删除 - 标记和扫描
在进一步阅读之前,我将真诚地建议您先阅读java内存管理。它详细讨论了年轻一代,老一代和永久世代。
并发标记扫描(CMS)垃圾收集
CMS垃圾收集本质上是一种升级的标记和扫描方法。它使用多个线程扫描堆内存。它被修改为利用更快的系统并具有性能增强功能。
它尝试通过与应用程序线程同时执行大部分垃圾收集工作来最小化由于垃圾收集而导致的暂停。它使用Young Generation中的并行stop-the-world 标记复制算法和旧一代中大多数并发的标记扫描算法。
要使用CMS GC,请使用以下JVM参数:
<span >-XX:+UseConcMarkSweepGC</span>
CMS GC优化选项
旗 | 描述 |
---|---|
-XX:+ UseCMSInitiating \ OccupancyOnly | 表示您只想将占用率作为启动CMS收集操作的标准。 |
-XX:CMSInitiating \ OccupancyFraction = 70 | 设置CMS生成占用百分比以启动CMS收集周期。 |
-XX:CMSTriggerRatio = 70 | 这是在MinHeapFreeRatio CMS周期开始之前分配的CMS生成的百分比。 |
-XX:CMSTriggerPermRatio = 90 | 设置MinHeapFreeRatio 在启动CMS收集周期之前分配的CMS永久生成中的百分比。 |
-XX:CMSWaitDuration = 2000 | 使用该参数指定允许CMS等待年轻收集的时间。 |
-XX:+ UseParNewGC | 选择使用并行算法进行年轻空间收集。 |
-XX:+ CMSConcurrentMTEnabled | 允许为并发阶段使用多个线程。 |
-XX:ConcGCThreads = 2 | 设置用于并发阶段的并行线程数。 |
-XX:ParallelGCThreads = 2 | 设置要用于世界各地阶段的并行线程数。 |
-XX:+ CMSIncrementalMode | 启用增量CMS(iCMS)模式。 |
-XX:+ CMSClassUnloadingEnabled | 如果未启用此选项,CMS将不会清除永久空间。 |
-XX:+ ExplicitGCInvokes \并发 | 这允许System.gc() 触发并发收集而不是完整的垃圾收集周期。 |
串行垃圾收集
该算法使用年轻代的标记复制和旧代的标记扫描紧凑。它适用于单个线程。执行时,它会冻结所有其他线程,直到垃圾收集操作结束。
由于串行垃圾收集的线程冻结性质,它仅适用于非常小的程序。
要使用串行GC,请使用以下JVM参数:
<span >-XX:+UseSerialGC</span>
并行垃圾收集
Simimar连续GC,它用于mark-copy
Young Generation和mark-sweep-compact
Old Generation。多个并发线程用于标记和复制/压缩阶段。您可以使用-XX:ParallelGCThreads=N
选项配置线程数。
并行垃圾收集器适用于多核计算机,如果您的主要目标是通过有效使用现有系统资源来提高吞吐量。使用这种方法,GC循环时间可以大大减少。
直到Java 8,我们已经看到Parallel GC是默认的垃圾收集器。从Java 9开始,G1是32位和64位服务器配置的默认垃圾收集器。- JEP [248]
要使用并行GC,请使用以下JVM参数:
<span >-XX:+UseParallelGC</span>
G1垃圾收集
G1(Garbage First)垃圾收集器在Java 7中可用,旨在成为CMS收集器的长期替代品。G1收集器是并行,并发和递增压缩的低暂停垃圾收集器。
此方法涉及将内存堆分段为多个小区域(通常为2048)。每个区域都标记为年轻一代(进一步划分为伊甸园区域或幸存区域)或老一代。这允许GC避免一次收集整个堆,而是逐步处理问题。这意味着一次只考虑一部分区域。
记忆区域标记为 - G1
G1跟踪每个区域包含的实时数据量。该信息用于确定包含最多垃圾的区域; 所以他们先收集。这就是为什么它是名字垃圾优先收集。
不幸的是,就像其他算法一样,压缩操作使用Stop the World方法进行。但根据设计目标,您可以为其设定具体的性能目标。您可以配置暂停持续时间,例如在任何给定秒内不超过10毫秒。Garbage-First GC将尽最大努力以高概率实现这一目标(但不确定,由于操作系统级别的线程管理而难以实时)。
如果要在Java 7或Java 8计算机中使用,请使用JVM参数,如下所示:
<span >-XX:+UseG1GC</span>
G1优化选项
旗 | 描述 |
---|---|
-XX:G1HeapRegionSize =16米 | 堆区域的大小。该值为2的幂,范围从1MB到32MB。目标是根据最小Java堆大小拥有大约2048个区域。 |
-XX:MaxGCPauseMillis = 200 | 设置所需最大暂停时间的目标值。默认值为200毫秒。指定的值不适合您的堆大小。 |
-XX:G1ReservePercent = 5 | 这决定了堆中的最小保留。 |
-XX:G1ConfidencePercent = 75 | 这是置信系数暂停预测启发式。 |
-XX:GCPauseIntervalMillis = 200 | 这是每MMU的暂停间隔时间片,以毫秒为单位。 |
GC自定义选项
GC配置标志
旗 | 描述 |
---|---|
-Xms2048m -Xmx3g | 设置初始和最大堆大小(年轻空间加上终身空间)。 |
-XX:+ DisableExplicitGC | 这将导致JVM忽略应用程序的任何System.gc()方法调用。 |
-XX:+ UseGCOverheadLimit | 这是用于限制在抛出OutOfMemory错误之前在垃圾收集中花费的时间的使用策略。 |
-XX:GCTimeLimit = 95 | 这限制了在OutOfMemory 抛出错误之前在垃圾收集中花费的时间比例。这用于GCHeapFreeLimit 。 |
-XX:GCHeapFreeLimit = 5 | 这会在OutOfMemory 抛出错误之前设置完全垃圾回收后的最小可用空间百分比。这用于GCTimeLimit 。 |
-XX:InitialHeapSize =3克 | 设置初始堆大小(年轻空间加上终身空间)。 |
-XX:MaxHeapSize =3克 | 设置最大堆大小(年轻空间加上终身空间)。 |
-XX:新尺寸=128米 | 设置年轻空间的初始大小。 |
-XX:MaxNewSize =128米 | 设置年轻空间的最大大小。 |
-XX:SurvivorRatio = 15 | 将单个幸存者空间的大小设置为伊甸园空间大小的一部分。 |
-XX:PermSize =512米 | 设置永久空间的初始大小。 |
-XX:MaxPermSize参数=512米 | 设置永久空间的最大大小。 |
-Xss512k | 设置专用于每个线程的堆栈区域的大小(以字节为单位)。 |
GC日志标记
旗 | 描述 |
---|---|
-verbose:gc或-XX:+ PrintGC | 这将打印基本垃圾回收信息。 |
-XX:+ PrintGCDetails | 这将打印更详细的垃圾收集信息。 |
-XX:+ PrintGCTimeStamps | 您可以为每个垃圾收集事件打印时间戳。秒是顺序的,从JVM开始时间开始。 |
-XX:+ PrintGCDateStamps | 您可以为每个垃圾收集事件打印日期戳。 |
-Xloggc: | 使用此方法,您可以将垃圾收集输出重定向到文件而不是控制台。 |
-XX:+打印\ TenuringDistribution | 您可以在每个收集周期后打印有关年轻空间的详细信息。 |
-XX:+ PrintTLAB | 您可以使用此标志来打印TLAB分配统计信息。 |
-XX:+ PrintReferenceGC | 使用此标志,您可以在停止世界暂停期间打印参考处理的时间(即弱,柔和等)。 |
-XX:+ HEAPDUMP \ OnOutOfMemoryError | 这会在内存不足的情况下创建堆转储文件。 |
摘要
所以在这个java垃圾收集教程中,我们学习了以下内容 -
- 对象生命周期分为3个阶段,即对象创建,使用对象和对象破坏。
- 如何
mark-sweep
,mark-sweep-compact
以及mark-copy
机制炒菜锅。 - 不同的单线程和并发GC算法。
- 直到java 8,并行GC是默认算法。
- 从java 9开始,G1已被设置为默认的GC算法。
- 此外,还有各种标志来控制垃圾收集算法的行为并记录任何应用程序的有用信息。
相关文章
- 暂无相关文章
用户点评