简体   繁体   English

分析 Java 中 OutOfMemoryError 的堆转储

[英]Analyzing heap dump from OutOfMemoryError in Java

my Java program is constantly getting OutOfMemoryError , and I believe there is a memory leak somewhere.我的 Java 程序不断出现OutOfMemoryError ,我相信某处存在 memory 泄漏。 While researching this issue, multiple sites suggested the Eclipse Memory Analyzer tool, so I added the -XX:+HeapDumpOnOutOfMemoryError flag to the command, to get the heap dump the next time the error occurs.在研究这个问题时,多个站点建议使用 Eclipse Memory Analyzer 工具,因此我在命令中添加了-XX:+HeapDumpOnOutOfMemoryError标志,以在下次发生错误时获取堆转储。 Upon checking the dump, the objects taking up the most space were "17,481 instances of "com.couchbase.client.core.deps.org.LatencyUtils.LatencyStats", loaded by "org.springframework.boot.loader.LaunchedURLClassLoader @ 0x6c7c24510" occupy 1,978,652,856 (59.03%) bytes."检查转储后,占用最多空间的对象是“com.couchbase.client.core.deps.org.LatencyUtils.LatencyStats 的 17,481 个实例”,由“org.springframework.boot.loader.LaunchedURLClassLoader @ 0x6c7c24510”加载占用 1,978,652,856 (59.03%) 字节。” “com.couchbase.client.core.deps.org.LatencyUtils.LatencyStats”的 17,481 个实例,由“org.springframework.boot.loader.LaunchedURLClassLoader @ 0x6c7c24510”加载,占用 1,978,652,856 (59.03%) 字节。

I thought this was the logger printing out too many logs, since the Java Couchbase code prints a LOT of logs on the INFO level, so I tried setting the log level to WARN but after trying it out, same result.我认为这是记录器打印了太多日志,因为 Java Couchbase 代码在INFO级别打印了很多日志,所以我尝试将日志级别设置为WARN ,但在尝试之后,结果相同。 Would appreciate any insight or suggestions, thank you.将不胜感激任何见解或建议,谢谢。


EDIT: some parts of our code that calls Couchbase:编辑:我们调用 Couchbase 的代码的某些部分:

@Autowired
private CouchbaseConfig couchbaseConfig;

public List<ArLedger> getBranchArLedgers(String branchId, String fromDate, String toDate) {
        
    String query = Queries.GET_AR_LEDGER_BY_BRANCH_AND_DATE_RANGE;
    query = MessageFormat.format(query, branchId, fromDate, toDate);

    Cluster cluster = null;
        
    try {
        cluster = couchbaseConfig.connectToCouchbase();
        QueryResult queryResult = cluster.query(query);
    
        return queryResult.rowsAs(ArLedger.class);
    } catch (Exception e) {
        e.printStackTrace();
        return Collections.emptyList();
    } finally {
        if (cluster != null) {
            cluster.disconnect();
        }
    }
}

And the connectToCouchbase() from the injected CouchbaseConfig:以及注入的 CouchbaseConfig 中的 connectToCouchbase():

@Value("${app.couchbase.connection-string}")
private String connectionString;

@Value("${app.couchbase.username}")
private String username;

@Value("${app.couchbase.password}")
private String password;

public Cluster connectToCouchbase() {
    return Cluster.connect(connectionString, username, password);
}

EDIT 2: Updated the code to follow dnault's suggestion, and a screenshot of the error that occurs when running the code:编辑 2:更新代码以遵循 dnault 的建议,以及运行代码时出现的错误的屏幕截图:

CouchbaseConfig:沙发底座配置:

@Configuration
public class CouchbaseConfig extends AbstractCouchbaseConfiguration {

    @Autowired
    private ApplicationContext context;

    @Value("${app.couchbase.connection-string}")
    private String connectionString;

    @Value("${app.couchbase.username}")
    private String username;

    @Value("${app.couchbase.password}")
    private String password;

    @Bean
    public Cluster couchbaseCluster() {
        return Cluster.connect(connectionString, username, password);
    }
}

The repository code:存储库代码:

@Repository
public class ArLedgerRepository {
    
    @Autowired
    private Cluster couchbaseCluster;

    public List<ArLedger> getAllBranchArLedgers(String branchId, String fromDate, String toDate) {
        
        String query = Queries.GET_ALL_AR_LEDGERS_BY_BRANCH_AND_DATE_RANGE;
        query = MessageFormat.format(query, branchId, fromDate, toDate);
        try {
            
            QueryResult queryResult = couchbaseCluster.query(query);
    
            return queryResult.rowsAs(ArLedger.class);
        } catch (Exception e) {
            e.printStackTrace();
            return Collections.emptyList();
        } finally {
            couchbaseCluster.disconnect();
        }
    }
}

And the screenshot of the error that occurs when the repository method is called:以及调用repository方法时出现的错误截图: 请求取消异常

@kei101895 @kei101895

There is already a couchbaseCluster bean defined in AbstractCouchbaseConfiguration. AbstractCouchbaseConfiguration 中已经定义了一个 couchbaseCluster bean。 If I'm not mistaken, that is the Cluster that @Autowired will use (I believe because it was needed previously by other @Beans and already created).如果我没记错的话,那是@Autowired 将使用的集群(我相信是因为其他@Beans 之前需要它并且已经创建了)。

That couchbaseCluster uses the couchbaseClusterEnvironment bean which has a destroyMethod specified.该 couchbaseCluster 使用指定了 destroyMethod 的 couchbaseClusterEnvironment bean。 This will ensure that shutdown() is called on the ClusterEnvironment这将确保在 ClusterEnvironment 上调用 shutdown()

@Bean(destroyMethod = "shutdown")
public ClusterEnvironment couchbaseClusterEnvironment() {...

To customize the environment for the provided Cluster @Bean, one can @Override the configureEnvironment(builder) method in the couchbase config class.要为提供的 Cluster @Bean 自定义环境,可以在 couchbase 配置 class 中 @Override configureEnvironment(builder) 方法。

If you really want/need to have your own Cluster bean, you can give it a name in @Bean("myBeanName") and then reference it with:如果您真的想要/需要拥有自己的 Cluster bean,您可以在 @Bean("myBeanName") 中给它起一个名字,然后用以下方法引用它:

ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class); ApplicationContext ac = new AnnotationConfigApplicationContext(Config.class); myCluster = (Cluster) ac.getBean("myBeanName"); myCluster = (Cluster) ac.getBean("myBeanName");

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM