Java Top.

使用Spring 5和Spring Boot 2开始,通过学习春天课程:

>>看看这个课程

1.概述

在本教程中,我们将研究system.gc()方法位于java.lang.包裹。

明确地呼唤system.gc()被众所周知的是糟糕的做法。让我们试着了解原因,如果有任何用例调用此方法可能很有用。

2.垃圾收集

当有迹象表明这样做时,Java虚拟机决定执行垃圾收集。这些适应症不同于一个GC实施到另一个。它们基于不同的启发式。但是,当GC时,肯定会执行一些时刻:

  • 旧一代(终身空间)已满,触发次要GC
  • 新一代(Eden + Survivor0 + Survivor1空间)已满,这将触发主/全GC

独立于GC实现的唯一物品是收集垃圾的对象资格。

现在,我们来看看system.gc()方法本身。

3.system.gc()

该方法的调用很简单:

system.gc()

官方的甲骨文的文档说:

调用gc方法建议java虚拟机的努力回收未使用的对象,以使他们目前占用的内存可用于快速重用。

无法保证实际的GC将被触发

system.gc()触发主要GC。因此,根据您的垃圾收集器实现,存在在停止世界阶段度过一段时间的风险。因此,我们有一个不可靠的工具,具有潜在的重大表现罚款

显式垃圾收集调用的存在应该是每个人的严重红旗。

我们可以预防system.gc()通过使用- xx: DisableExplicitGCJVM国旗。

3.1。性能调优

在投掷之前,值得注意的是OutOfMemoryError,JVM将执行一个完整的GC。因此,显式调用system.gc()不会让我们免于失败吗

现在的垃圾清理工真的很聪明。他们拥有关于内存使用和其他统计信息的所有知识,能金宝搏官网188be够做出正确的决定。因此,我们应该相信他们。

如果内存出现问题,我们有一个一些设置我们可以通过设置所需的申请时间/ GC时间比来改变我们的应用程序 - 从选择不同的垃圾收集器开始,最后,结束了用于内存段的固定大小。

还有办法减轻由显式调用引起的Full GC的影响。我们可以使用其中一个标志:

- xx: + ExplicitGCInvokesConcurrent

或者:

-XX:+ ExplicitGcinvokesconCurrentAndUnloadSclasses

如果我们真的希望我们的应用程序正常工作,我们应该解决真正的潜在内存问题。

在下一章中,我们将看到显式调用时的一个实际示例system.gc()似乎有用。

4.使用示例

4.1。设想

让我们写一个测试应用程序。我们想在打电话时找到一个情况system.gc()可能是有用的

小垃圾收集比主要的垃圾收集更频繁。所以,我们应该专注于后者。如果它“幸存下来”,则单个物体被移动到终身空间,并且仍然可以从GC根部到达。

让我们想象一下我们有一系列巨大的物体集合。然后,在某些时候,我们正在清除对象的集合。也许这是一个很好的跑步时刻system.gc()

4.2。演示应用程序

我们将创建一个简单的控制台应用程序,允许我们模拟该方案:

public class DemoApplication {private static final Map缓存= new HashMap();public static void main(String[] args) {Scanner Scanner = new Scanner(System.in);while (scanner.hasNext()){最终字符串next = scanner.next();If ("fill".equals(next)) {for (int I = 0;我< 1000000;i++) {cache.put(randomUUID().toString(), randomUUID().toString());}} else if ("invalidate".equals(next)) {cache.clear();} else if ("gc".equals(next)) {System.gc();} else if ("exit".equals(next)) {System.exit(0);} else {System.out.println("未知"); } } } }

4.3。运行演示

让我们用一些附加的标志来运行我们的应用程序:

-XX:+PrintGCDetails - xlogggc:gclog.log -Xms100M -Xmx500M -XX:+UseConcMarkSweepGC

前两个标志需要记录GC信息。接下来的两个标志正在设置初始堆大小,然后是最大堆大小。我们希望将堆大小保持低强制GC更加活跃。最后,我们决定使用CMS - 并发标记和扫描垃圾收集器。是时候运行我们的应用程序了!

首先,让我们尝试填满终身职位。类型填满。

我们可以调查我们gclog.log文件查看发生了什么。我们会看到大约15个系列。为单个集合记录的行看起来像:

168754K->101295K(244192K), 0.0017865 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] secs

如我们所见,记忆被填满了。

接下来,让我们力量system.gc()通过打字gc。我们可以看到内存用法没有显着变化:

238.810: [CMS: 101255K->101231K(168352K);0.2634318秒]120693K->101231K(244192K), [metspace: 32186K->32186K(1079296K)], 0.2635908 secs] [Times: user=0.27 sys=0.00, real=0.26 secs]

经过几次运行,我们会看到内存大小保持在同一级别。

让我们清除缓存通过打字无效。我们应该看到没有更多的日志线gclog.log文件。

我们可以尝试更多次填充缓存,但没有GC正在发生。这是片刻我们可以智胜垃圾清理工。现在,强制GC之后,我们将看到如下一行:

262.124:[全GC(System.gc())262.124:[CMS:101523K-> 14122K(169324K);0.0975656秒] 103369K-> 14122K(245612K),[Metaspace:32203K-> 32203K(1079296K)],0.0977279秒] [次:用户= 0.10 sys = 0.00,Real = 0.10秒]

我们发布了令人印象深刻的记忆!但现在真的需要吗?发生了什么事?

根据这个例子,调用system.gc()当我们释放大物体或使缓存失效时,可能似乎很诱人。

5.其他用法

明确呼叫的原因很少system.gc()方法可能很有用。

一个可能的原因是服务器启动后清理内存- 我们启动了一份做准备的服务器或应用程序。之后,有很多物体可以最终确定。但是,这种准备后的清洁不应该是我们的责任。

另一个原因是内存泄漏分析-它比我们希望在生产代码中保持的事情更像是调试练习。打电话system.gc()看到堆空间仍然很高可能意味着内存泄漏

6.摘要

在本文中,我们研究了system.gc()方法以及它似乎有用的时候。

当涉及到我们的应用程序的正确性时,我们不应该依赖它。GC在大多数情况下更聪明,而在任何内存问题的情况下,我们应该考虑调整虚拟机,而不是制作这样的显式呼叫。

像往常一样,可以找到本文中使用的代码在GitHub

Java底部

使用Spring 5和Spring Boot 2开始,通过学习春天课程:

>>看看这个课程
本文评论关闭!