使用 Mongo Java 连接进行内存泄漏

2022-09-02 11:02:35

我正在以以下方式构建MongoClient Connection:

public static synchronized MongoClient getInstance(String mongoDbUri) {
        try {
            // Standard URI format: mongodb://[dbuser:dbpassword@]host:port/dbname
            if( mongoClient == null ){
                mongoClient = new MongoClient(
                              new MongoClientURI(mongoDbUri));
            }
        } catch (Exception e) {
            log.error(
                    "Error mongo connection : ",
                    e.getCause());
        }
        return mongoClient;
    }
  

        

在运行多个事务的一段时间内,我看到一些内存与未被释放的应用程序一起占用。

当分析堆转储时,看到类的内存消耗最大

com.mongodb.internal.connection.PowerOfTwoBufferPool

mongo 客户端正在尝试连接到 mongos 实例。应用程序在 3 个分片上有 3 个副本集和一个配置服务器来保存元数据。

为了添加更多细节,我有一个用@Component注释的弹簧管理豆。对于调用上述方法的 bean,有一个带有@PostConstruct的注释。在春季课程中,我们使用Mongo客户端。insert/update/create

谢谢。


答案 1

PowerOfTwoBufferPool实际上是一个缓存,所以乍一看,这可能看起来像内存泄漏。

mongodb 用户组回复包含详细信息:

...此行为是预期的,因为它是缓存。因此,它可能看起来像泄漏。PowerOfTwoBufferPool

简而言之,保存许多字节缓冲实例池,每个池包含一组大小相等的缓冲区。最小大小为 1K,最大为 16MB,两个大小的功率从 1K 增加到 16MB。每个大小相等的缓冲区池的大小不受限制,由应用程序使用情况决定。一旦缓冲区缓存在池中,它就会保持池化(或正在使用),直到MongoClient关闭。因此,完全可以预期,在 JVM 状态的内省期间,池的内容将显示为泄漏可疑,就像任何缓存一样。PowerOfTwoBufferPool

的存在是为了减少 GC 负载。众所周知,JVM中的现代垃圾回收器处理大型分配的方式与处理较小的垃圾回收器不同,因此,如果应用程序不进行任何大型对象池化(如这些缓冲区),它将具有增加GC负载的效果,因为垃圾回收器必须做更多的工作来收集这些大对象而不是较小的对象。这样做的代价是驱动程序保留可供应用程序的其他部分使用的内存。特别是,它拥有足够的内存来处理应用程序迄今为止看到的最大峰值负载。PowerOfTwoBufferPool


答案 2

添加一个 PreDestroy 方法,在其中关闭客户端:

@PreDestroy
public void preDestroy() {
    try {
       if (mongoClient != null ) {
          mongoClient.close();
       }
    } catch (Exception e) {
        log.error("Error closing mongo connection : ",
                e.getCause());
    }
}

当然,mongoClient 必须可以通过 preDestroy() 方法访问