提问人:Toparvion 提问时间:7/29/2023 更新时间:7/30/2023 访问量:143
Linux OOM-Killer 和 G1 GC 内存消耗
Linux OOM-Killer and G1 GC memory consumption
问:
我有一个 Java 应用程序,在具有 24 GB RAM 的 Oracle Linux 计算机上的 Liberica JDK 8(HotSpot VM,G1 GC)上运行。该应用程序具有最大的堆大小,大量使用它(由于其负载配置文件),并且是服务器上唯一具有此类要求的进程。-Xmx15g
有时(通常在几十个小时的正常运行时间之后),应用程序会被 Linux oom-killer 杀死。为了找到根本原因,我在详细模式下启用了 JVM Native Memory Tracking (NMT),在预热后不久就建立了基线,并收集了 34 个正常运行时间小时的以下统计数据(就在进程再次终止之前):
根据 Oracle 的 NMT 内存类别文档,我预计最重的内部类别将填充类似 Direct ByteBuffers 的东西。但是,NMT 详细信息显示,这 3 GB 内部s 中几乎有 70% 由如下分配组成:
[0x00007faeb7f9b5b5] BitMap::BitMap(unsigned long, bool)+0x1d5
[0x00007faeb82ca08f] OtherRegionsTable::add_reference(void*, int)+0x57f
[0x00007faeb82e0f40] InstanceKlass::oop_oop_iterate_nv(oopDesc*, FilterOutOfRegionClosure*)+0xc0
[0x00007faeb82c3373] HeapRegion::oops_on_card_seq_iterate_careful(MemRegion, FilterOutOfRegionClosure*, signed char*)+0x163
(malloc=1154790KB type=Internal +846638KB #577395 +423319)
总共有 8 个这样的块,其方法调用在下一个堆栈帧上具有不同的类,例如:oops_on_card_seq_iterate_careful(…)
InstanceRefKlass
InstanceKlass
ObjArrayKlass
基于这些标识符,我发现这些例程是 G1 GC 的一部分。但是,我看不出有什么方法可以从相应的 JVM 选项中可用的 G1 参数来影响它们的行为(内存消耗)。
查看这个相关的 SO 答案,我试图将人体工程学计算的 4 MB 增加到手动设置 8 MB,但没有观察到显着变化。-XX:G1HeapRegionSize
所以问题是:
- 为什么NMT将纯G1相关活动记录到内部类别中?(非GC)
- 有没有办法让 G1 消耗更少的本机内存?(期望将其更改为其他 GC,在这种情况下不是一个选项)
答:
为什么NMT将纯G1相关活动记录到内部类别中?(非GC)
NMT 不知道它是与 G1 相关的活动:它不会遍历堆栈来找出分配类型,它只是使用传递给分配函数的类型。
正如您在堆栈跟踪中看到的那样,分配发生在构造函数中。这是一个通用类,用于许多地方,而不仅仅是 GC。 类具有与类型关联的分配器:BitMap
BitMap
mtInternal
ArrayAllocator<bm_word_t, mtInternal> _map_allocator;
在较新的 JDK 版本中,BitMap 具有从外部传递的变量分配类型。
有没有办法让 G1 消耗更少的本机内存?
迁移到 JDK 17 或更高版本。G1GC 得到了大量的改进,这些改进永远不会被移植到 JDK 8 中。在 JDK 8 中调优 GC 是性能顾问的金矿,但如果您关心时间和资源,升级 JDK 是最好的投资。
评论
malloc
LD_PRELOAD
评论