skip to content
Honwhy Wang

关于Netty内存管理

/ 3 min read

关于Netty内存管理

Netty使用的堆外内存不受JVM限制,可能会导致申请内存超出系统限制,从而导致os kill。

问题表现

从「阿里云开发者」公众号文章,8G的容器Java堆才4G怎么就OOM了? ,基本可以了解到Netty自己管理的堆外内容不受控制,且一般jvm监控无法发现它。

原因分析

参考死磕 Netty 之内存篇:探索那个不为人知的堆外内存 可以大致了解Netty是如何申请堆外内存的。

这里不过多介绍其他可能情况。使用Java 8且Netty配置参数默认的情况下,Netty是通过类似Unsafe#allocateMemory() API申请堆外内存的,且这些内存无Cleaner关联。

如果是通过ByteBuffer.allocateDirect()方式申请的就有Cleaner,如下图

d-1

因此,Netty的表现

  • Netty申请的堆外内存无法监控
  • 自己管理堆外内存无法通过GC进行回收
  • Netty申请的堆外内存可能会超过系统限制

实验分析

这部分简化过程处理,只说结论,参考以下代码,

// -XX:MaxDirectMemorySize=10M
// Netty和应用可以同时申请10M堆外内存
PooledByteBufAllocator allocator = PooledByteBufAllocator.DEFAULT;
allocator.directBuffer(10 * 1024 * 1024);
ByteBuffer.allocateDirect(10 * 1024 * 1024);
// Netty 堆外内存使用情况
PooledByteBufAllocatorMetric metric = allocator.metric();
metric.usedDirectMemory();

Netty和应用可以同时申请10M堆外内存,至少说明了Netty使用的堆外内存与应用使用的堆外内存是分开统计的。

另外虽然JVM无法监控的Netty的内存使用情况,但是它提供了metric,通过metric是可以暴露相关指标的。为了获取这些指标可以考虑接入jmx、Prometheus或者写一个Controller接口。

使用建议

通过设定MaxDirectMemorySize参数可以限制Netty的堆外内存使用上限。 如无必要不要定制ClassLoader加载Netty,这样会导致产生多个PooledByteBufAllocator实例(虽然PooledByteBufAllocator使用了单例模式,但是不同ClassLoader的类是不同的)。 针对Netty堆外内存使用情况进行监控,了解Netty为何需要很多堆外内存,可能与系统的吞吐量有关,可能与Socket通信消息体过大有关。