JVM社区不断增加新的GC,最近又添加了一个新的GC,它称为Epsilon ,是非常特殊的一个。 Epsilon仅分配内存,但不会回收任何内存。
看起来好像不执行任何垃圾回收的GC用途是什么。 这种类型的垃圾收集器有特殊用途,我们将对其进行研究。
可以在哪里使用此灵巧的GC?
性能测试
如果您正在开发对延迟要求严格且内存预算有限的解决方案,那么此GC可用于测试程序限制。
内存压力测试
想知道您的应用程序提取瞬态内存需求。 如果您要构建一些纯粹的内存中解决方案,我会发现这很有用。
基准标记算法。
很多时候,我们希望基于对BIG(O)概念的理解来测试新的酷算法的真实性能,但是垃圾收集器会在测试过程中增加噪音。
低垃圾
很多时候,我们在算法上进行了一些优化以减少产生的垃圾,而像epsilon这样的GC则有助于科学验证优化。
如何启用epsilon GC
JVM工程师特别注意不要在生产中默认启用此GC,因此要使用此GC,我们必须使用以下JVM选项
-XX:+ UnlockExperimentalVMOptions -XX:+ UseEpsilonGC -Xlog:gc
您可能会想到的一个问题是,内存耗尽时会发生什么? JVM将因内存不足错误而停止。
让我们看一些代码来测试GC
如何知道JVM进程中是否使用epsilon?
Java具有良好的管理API,可以查询正在使用的当前GC,也可以用来验证不同版本的Java中的默认GC是什么。
public class VerifyCurrentGC { public static void main(String... args) { var gcBeans = ManagementFactory.getGarbageCollectorMXBeans(); gcBeans.stream().forEach(gc -> { out.println(format( "GC Name : %s" , gc.getName())); var poolNames = gc.getMemoryPoolNames(); if (poolNames != null ) { List.of(poolNames).forEach(pool -> out.println(format( "Pool name %s" , pool))); } else { out.println( "No memory pools for " + gc.getName()); "No memory pools for " + gc.getName()); } }); } }
使用以下选项运行以上代码
-XX:+ UnlockExperimentalVMOptions -XX:+ UseEpsilonGC VerifyCurrentGC 内存耗尽时代码的行为方式。
我将使用下面的代码来展示新GC的工作方式。
public class MemoryAllocator { public static final int KB = 1024 ; static int mbToAllocate = Integer.getInteger( "mb" , 1000 ); public static void main(String[] args) { System.out.println(String.format( "Start allocation of %s MBs" , mbToAllocate)); for (var i = 0 ; i < mbToAllocate; i++) { var garbage = new byte [KB * KB]; } System.out.println( "I was Alive after allocation" ); } }
使用默认的GC运行以上代码并请求5GB分配不会引起任何问题( java -Xlog:gc -Dmb = 5024 MemoryAllocator ),并且它会产生以下输出
[0.016s] [info] [gc]使用G1
[0.041s] [info] [gc]定期GC已禁用
开始分配5024 MB [0.197s] [info] [gc] GC(0)暂停年轻(并发启动)(G1大量分配)116M-> 0M(254M)3.286ms [0.197s] [info] [gc] GC(1)并发周期 [0.203s] [info] [gc] GC(1)暂停备注20M-> 20M(70M)4.387ms [0.203s] [info] [gc] GC(1)暂停清理22M-> 22M(70M)0.043ms [1.600s] [info] [gc] GC(397)并发周期6.612ms [1.601s] [info] [gc] GC(398)暂停年轻(并发启动)(G1混合分配)52M-> 0M(117M)1.073ms [1.601s] [info] [gc] GC(399)并发周期 分配后我还活着 [1.606s] [info] [gc] GC(399)暂停备注35M-> 35M(117M)0.382ms [1.607s] [info] [gc] GC(399)暂停清理35M-> 35M(117M)0.093ms [1.607s] [info] [gc] GC(399)并发周期6.062ms
让我们添加一些内存限制( java -XX:+ UnlockExperimentalVMOptions -XX:+ UseEpsilonGC -Xlog:gc -Xmx1g -Dmb = 5024
内存分配器)
[0.011s] [info] [gc]可调整大小的堆; 从253M开始,最大:1024M,步长:128M [0.011s] [info] [gc]使用TLAB分配; 最高:4096K [0.011s] [info] [gc]启用了弹性TLAB; 弹性:1.10倍 [0.011s] [info] [gc]启用了弹性TLAB衰减; 衰减时间:1000ms [0.011s] [info] [gc]使用Epsilon 开始分配5024 MB [0.147s] [info] [gc]堆:已保留1024M,已提交253M(24.77%),已使用52640K(5.02%) [0.171s] [info] [gc]堆:已保留1024M,已承诺253M(24.77%),已使用103M(10.10%) [0.579s] [info] [gc]堆:已保留1024M,已落实1021M(99.77%),已使用935M(91.35%) [0.605s] [info] [gc]堆:已使用1024M,已承诺1021M(99.77%),已使用987M(96.43%)
由于java.lang.OutOfMemoryError而终止:Java堆空间
此特定运行导致OOM错误,可以很好地确认1GB之后该程序将崩溃。
真正的多线程程序也具有相同的行为,请参考MultiThreadMemoryAllocator.java以获取示例。
单元测试可用于测试此特殊GC的功能。
我认为Epsilon将来会发现更多用例和采用情况,这绝对是增加JVM覆盖率的好一步。
所有代码示例均可用Github回购
如果您喜欢该职位,则可以在Twitter上关注我 。
翻译自: https://www.javacodegeeks.com/2019/08/jvm-with-no-garbage-collection.html