【Java 8 GC 调优】其它考量点

本节介绍影响GC的其它情况。

Finalization 和 弱引用、软引用 及 幻象引用

有些应用程序会通过 Finalization 和 弱引用、软引用 及 幻象引用 与 GC 交互。这些特性会在 Java 编程语言级别造成性能问题。一个例子就是 依赖 Finalization 关闭文件描述符。它使得一项外部资源(描述符)依赖于 GC 的及时性。依赖 GC 来管理内存之外的资源几乎总是个坏主意。

前言中与本节相关的文档中包含了一篇文章,它深入讨论了 Finalization 的陷阱,以及避免这些陷阱的技巧。

 

显式 GC

应用程序与 GC 交互的另一种方式是通过调用 System.gc() 来显式调用 Full GC。这可能会在不需要的时候(如,当一次 Minor GC 就足够的时候),强制完成一次 Major GC。这通常是应该避免的。显式 GC 的性能影响可通过使用标识 -XX:+DisableExplicitGC 禁用它们来衡量。该标识会导致 JVM 忽略对 System.gc() 的调用。

显式 GC 最常见的一种用法是 远程方法调用(RMI)的 分布式GC(DGC)。使用 RMI 的应用程序会引用其它 JVM 中的对象。如果不偶尔调用本地堆的 GC,这些应用程序中的垃圾就无法被回收。所以 RMI 会周期性地强制执行 Full GC。可通过以下属性来控制这些 GC 的频率:

java -Dsun.rmi.dgc.client.gcInterval=3600000
     -Dsun.rmi.dgc.server.gcInterval=3600000 ...

该示例指定 每小时执行一次显式GC。默认频率是 每分钟执行一次。然而这也可能导致某些对象需要更长的时间才会被回收。这些属性的值最高可以被设置为 Long.MAX_VALUE。如果不对 DGC 活动时间设置上限,它会使得 显式GC 的实际时间间隔 为无限长。

 

软引用

软引用在 服务端VM 中存活的时间比客户端长。其清除频率可通过命令行选项 -XX:SoftRefLRUPolicyMSPerMB= 来控制。该选项指定了 对于堆中每一MB空闲空间,当软引用 不可强访问后 存活的毫秒数。其默认值是 1000ms/MB。即,对于堆中每一MB空闲空间,当其 对象的最后一个强引用被回收后,其软引用会存活 1秒。这只是一个近似的数字,因为软引用只会在 GC 时被回收,而 GC 可能只是偶尔发生。

 

类元数据

在 Java Hotspot VM 中,Java 类有一个内部表现形式,它被称为 类元数据。在 Java Hotspot VM 的早期版本中,类元数据 被分配在一个称为 “永久代” 的区域中。在 JDK 8 中,“永久代” 被删除,类元数据被分配到本地内存中(native memory)。默认情况下,可用于存储 类元数据 的本地内存(native memory)是没有限制的。可使用选项 MaxMetaspaceSize 来设置存放 类元数据 的本地内存上限。

Java Hotspot VM 会显式管理 元数据 空间。它会向操作系统申请空间,然后将这些空间分成块。类加载器 会在这些块中为 元数据 分配空间(一个块会被绑定到一个特定的类加载器。即,一个块最多只能同时对应一个类加载器)。当类被卸载后,其对应的块会被回收复用,或还给操作系统。元数据 使用 mmap 分配空间,而不是 malloc。

如果启用了 UseCompressedOops 且 使用了 UseCompressedClassesPointers,那么两个逻辑上不同的本地内存区域会被用于存放 类元数据。UseCompressedClassPointers 使用 32-bit 偏移量来表示 64-bit 进程中的 类指针。这如同 UseCompressedOops 对 Java 对象引用的作用。JVM 会分配一个区域来存放这些压缩后的 类指针。可用 CompressedClassSpaceSize 来设置该区域的大小,其默认值为 1GB。JVM 会在初始化时通过 mmap 为压缩后的类指针分配一块预留空间,并根据需要提交空间申请。MaxMetaspaceSize 是针对 已压缩的类指针 和 其它类指针 的空间总和。

类元数据 会在其对应的 Java类 卸载时被释放。Java类的卸载是通过 GC 实现的。为了卸载类并释放类元数据,可能会触发 GC。当类元数据使用的空间达到某个级别(高水位线)时,将引发 GC。GC 结束后,JVM 会根据 类元数据 释放的空间量 来提高或降低 “高水位线”。为避免过早地引发下一次 GC,“高水位线” 可能会被提高。“高水位线” 的初始值可通过命令行选项 MetaspaceSize 来设置。“高水位线” 的提高或降低会基于选项 MaxMetaspaceFreeRatio 和 MinMetaspaceFreeRatio 来确定。如果类元数据空间的当前可用占比大于 MaxMetaspaceFreeRatio,那么 “高水位线” 会被降低;如果小于 MinMetaspaceFreeRatio,那么 “高水位线” 会被提高。

为 MetaspaceSize 指定一个更高的值可以避免因 类元数据 过早引发 GC。为应用程序的类元数据分配多大的空间取决于应用程序自身特征,而且没有通用准则。MetaspaceSize 的默认值取决于平台,范围是 12MB ~ 20MB。

元数据所用空间的信息会包含在堆的打印输出中。

典型的堆打印输出:

Heap
  PSYoungGen      total 10752K, used 4419K
    [0xffffffff6ac00000, 0xffffffff6b800000, 0xffffffff6b800000)
    eden space 9216K, 47% used
      [0xffffffff6ac00000,0xffffffff6b050d68,0xffffffff6b500000)
    from space 1536K, 0% used
      [0xffffffff6b680000,0xffffffff6b680000,0xffffffff6b800000)
    to   space 1536K, 0% used
      [0xffffffff6b500000,0xffffffff6b500000,0xffffffff6b680000)
  ParOldGen       total 20480K, used 20011K
      [0xffffffff69800000, 0xffffffff6ac00000, 0xffffffff6ac00000)
    object space 20480K, 97% used 
      [0xffffffff69800000,0xffffffff6ab8add8,0xffffffff6ac00000)
  Metaspace       used 2425K, capacity 4498K, committed 4864K, reserved 1056768K
    class space   used 262K, capacity 386K, committed 512K, reserved 1048576K

在 “Metaspace” 开头的行中:

  • “used” 的值表示 已载入的类 所使用的空间;
  • “capacity” 的值表示 当前分配的 “块” 中,可用于存放元数据的剩余空间;
  • “committed” 的值表示 可用于分“块”的空间大小;
  • “reserved” 的值表示 为元数据预留的空间大小(包括已使用和未使用)。

“class space” 开头的行中各数值是与压缩后的类指针相关的相应空间大小。

 

你可能感兴趣的:(Java)