我们正在运行grpc-java
版本0.8.0服务器来处理远程调用。服务器逻辑非常简单,它只是从DB中获取由给定ID指定的数据并将它们设置为redis
缓存。
JVM参数配置为-Xss256k -Xmx8G
。一旦服务器启动并处理大约400 QPS,JVM就占用了4GB(RES top
)内存。我发出了jmap命令jmap -histo:live <pid>
并获得了以下转储文件:
num #instances #bytes class name
----------------------------------------------
1: 4998400 119961600 io.netty.buffer.PoolThreadCache$MemoryRegionCache$Entry
2: 212415 23503536 [B
3: 11076 20170816 [Lio.netty.buffer.PoolThreadCache$MemoryRegionCache$Entry;
4: 70853 10010904 [C
5: 28819 2518640 [Lio.netty.util.Recycler$DefaultHandle;
6: 31232 1998848 com.mysql.jdbc.ConnectionPropertiesImpl$BooleanConnectionProperty
7: 7287 1764136 [I
8: 70000 1680000 java.lang.String
9: 45766 1464512 java.util.Hashtable$Entry
10: 134 1291992 [D
11: 14376 1265088 io.netty.buffer.PooledUnsafeDirectByteBuf
12: 5527 1160200 [Ljava.util.HashMap$Node;
13: 16340 1116584 [[B
从中我们可以看到,有大量的io.netty.buffer.PoolThreadCache$MemoryRegionCache$Entry
实例占用了大约100MB的内存。 (请注意,grpc
正在使用netty
作为其通信层。)这似乎是异常的。即使这样,堆内对象也不能占用4GB内存。这必须归功于堆外存储器的使用,例如netty
使用的直接缓冲区。
是否存在堆外内存泄漏?
为什么会发生这种情况?以及如何解决或诊断这个问题?
答案 0 :(得分:1)
升级到grpc-java 0.9.0或更高版本(引入Netty 4.1 Beta6或更高版本)可以解决此问题。目前还不清楚问题是由于在不同的线程上释放缓冲区而导致的错误或效率低下而不是分配它们。