最终确定和弱,软和幻影引用

某些应用程序通过使用finalization和weak,soft或phantom引用与垃圾收集进行交互。

这些功能可以在Java编程语言级别创建性能工件。一个例子是依靠finalization来关闭文件描述符,这使得外部资源(描述符)依赖于垃圾收集的快速性。依靠垃圾收集来管理内存以外的资源几乎总是一个坏主意。

请参阅如何处理Java Finalization的内存保留问题,该问题深入讨论了最终确定的一些缺陷和避免它们的技术。

显式垃圾收集

应用程序与垃圾收集交互的另一种方法是使用System.gc()显式调用完整垃圾收集。

这可以强制在可能没有必要时进行主要收集(例如,当次要收集就足够时),因此通常应该避免。显式垃圾收集的性能影响可以通过使用标志-XX:+ DisableExplicitGC禁用它们来测量,这会导致VM忽略对System.gc()的调用。

显式垃圾收集最常遇到的用途之一是使用远程方法调用(RMI)的分布式垃圾收集(DGC)。

使用RMI的应用程序引用其他虚拟机中的对象。在没有偶尔调用本地堆的垃圾收集的情况下,无法在这些分布式应用程序中收集垃圾,因此RMI会定期强制执行完整收集。

可以使用属性控制这些集合的频率,如以下示例所示:

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

此示例每小时指定一次显式垃圾回收,而不是每分钟一次的默认速率。但是,这也可能导致某些物体需要更长的时间才能回收。如果不希望DGC活动的时效性上限,则可以将这些属性设置为与Long.MAX_VALUE一样高,以使显式集合之间的时间实际上无限。

软引用

软引用在服务器虚拟机中比在客户端中保持更长时间。

可以使用命令行选项-XX:SoftRefLRUPolicyMSPerMB = 来控制清除率,该选项指定每个兆字节的软引用将保持活动状态(一旦不再可以强烈访问)的毫秒数(ms)堆中的可用空间。默认值为每兆字节1000毫秒,这意味着对于堆中的每兆字节可用空间,软引用将在(收集对象的最后一次强引用之后)存活1秒钟。这是一个近似数字,因为软件引用仅在垃圾收集期间被清除,这可能偶尔发生。

类元数据

Java类在Java Hotspot VM中具有内部表示,并称为类元数据。

在以前版本的Java Hotspot VM中,类元数据是在所谓的永久生成中分配的。从JDK 8开始,删除了永久代,并且在本机内存中分配了类元数据。默认情况下,可用于类元数据的本机内存量是无限制的。使用选项-XX:MaxMetaspaceSize为用于类元数据的本机内存量设置上限。

Java Hotspot VM显式管理用于元数据的空间。从操作系统请求空间,然后分成块。类加载器从其块中为元数据分配空间(块被绑定到特定的类加载器)。为类加载器卸载类时,其块将被回收以供重用或返回到操作系统。元数据使用由mmap分配的空间,而不是malloc。

如果启用-XX:UseCompressedOops并使用-XX:UseCompressedClassesPointers,则将两个逻辑上不同的本机内存区域用于类元数据。 -XX:UseCompressedClassPointers使用32位偏移量来表示64位进程中的类指针,-XX:UseCompressedOops用于Java对象引用。为这些压缩类指针(32位偏移)分配区域。可以使用-XX:CompressedClassSpaceSize设置区域的大小,默认情况下为1千兆字节(GB)。压缩类指针的空间保留为初始化时由-XX:mmap分配的空间,并根据需要提交。 -XX:MaxMetaspaceSize适用于已提交的压缩类空间和其他类元数据的空间之和。

卸载相应的Java类时,将释放类元数据。由于垃圾收集而卸载Java类,并且可能导致垃圾收集以卸载类并释放类元数据。当为类元数据提交的空间达到一定水平(高水位线)时,会引发垃圾收集。在垃圾收集之后,可以根据从类元数据中释放的空间量来升高或降低高水位标记。将提高高水位标记,以免过早引起另一次垃圾收集。高水位标记最初设置为命令行选项-XX:MetaspaceSize的值。它是根据选项-XX:MaxMetaspaceFreeRatio和-XX:MinMetaspaceFreeRatio引发或降低的。如果类元数据的可用空间占类元数据总承诺空间的百分比大于-XX:MaxMetaspaceFreeRatio,则高水位标记将降低。如果它小于-XX:MinMetaspaceFreeRatio,那么高水位标记将被提升。

为-XX:MetaspaceSize选项指定更高的值,以避免为类元数据引发早期垃圾收集。为应用程序分配的类元数据量取决于应用程序,并且不存在选择-XX:MetaspaceSize的一般准则。 -XX:MetaspaceSize的默认大小取决于平台,范围从12 MB到大约20 MB。

有关用于元数据的空间的信息包含在堆的打印输出中。以下是典型输出:

[0,296s][info][gc,heap,exit] Heap
[0,296s][info][gc,heap,exit] garbage-first heap total 514048K, used 0K [0x00000005ca600000, 0x00000005ca8007d8, 0x00000007c0000000)
[0,296s][info][gc,heap,exit] region size 2048K, 1 young (2048K), 0 survivors (0K)
[0,296s][info][gc,heap,exit] Metaspace used 2575K, capacity 4480K, committed 4480K, reserved 1056768K
[0,296s][info][gc,heap,exit] class space used 238K, capacity 384K, committed 384K, reserved 1048576K

在以Metaspace开头的行中,使用的值是用于加载类的空间量。

容量值是当前分配的块中可用于元数据的空间。 提交的值是可用于块的空间量。

保留值是元数据保留(但不一定是已提交)的空间量。 以类空间开头的行包含压缩类指针的元数据的相应值。

参考资料

https://docs.oracle.com/javase/9/gctuning/other-considerations.htm