分析 Java 中 OutOfMemoryError 的堆转储

发布于 2025-01-15 21:13:59 字数 3501 浏览 1 评论 0原文

我的 Java 程序不断出现 OutOfMemoryError,我相信某处存在内存泄漏。在研究这个问题时,多个站点建议使用 Eclipse Memory Analyzer 工具,因此我在命令中添加了 -XX:+HeapDumpOnOutOfMemoryError 标志,以便在下次发生错误时获取堆转储。检查转储后,占用最多空间的对象是“17,481 个“com.couchbase.client.core.deps.org.LatencyUtils.LatencyStats”实例,由“org.springframework.boot.loader.LaunchedURLClassLoader @ 0x6c7c24510”加载。占用 1,978,652,856 (59.03%) 字节。” 17,481 个实例的“com.couchbase.client.core.deps.org.LatencyUtils.LatencyStats”,由加载“org.springframework.boot.loader.LaunchedURLClassLoader @ 0x6c7c24510”占用 1,978,652,856 (59.03%) 字节。

我认为这是记录器打印了太多日志,因为 Java Couchbase 代码打印了大量日志在 INFO 级别,所以我尝试将日志级别设置为WARN 但尝试后,结果相同。希望有任何见解或建议,谢谢。


编辑:调用 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();
        }
    }
}

以及注入的 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);
}

编辑 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);
    }
}

存储库代码:

@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();
        }
    }
}

以及调用repository方法时出现的错误截图: RequestCanceledException

my Java program is constantly getting OutOfMemoryError, and I believe there is a memory leak somewhere. 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. 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." 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.

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. Would appreciate any insight or suggestions, thank you.


EDIT: some parts of our code that calls 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:

@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:

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:
RequestCanceledException

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

守不住的情 2025-01-22 21:14:00

@kei101895

AbstractCouchbaseConfiguration 中已经定义了一个 couchbaseCluster bean。如果我没有记错的话,这就是 @Autowired 将使用的集群(我相信是因为其他 @Bean 之前需要它并且已经创建了)。

该 couchbaseCluster 使用 couchbaseClusterEnvironment bean,该 bean 指定了 destroyMethod。这将确保在 ClusterEnvironment 上调用 shutdown()

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

要为提供的 Cluster @Bean 自定义环境,可以 @Override couchbase 配置类中的 configureEnvironment(builder) 方法。

如果您确实想要/需要拥有自己的 Cluster bean,可以在 @Bean("myBeanName") 中给它一个名称,然后通过以下方式引用它:

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

@kei101895

There is already a couchbaseCluster bean defined in AbstractCouchbaseConfiguration. 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).

That couchbaseCluster uses the couchbaseClusterEnvironment bean which has a destroyMethod specified. This will ensure that shutdown() is called on the ClusterEnvironment

@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.

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:

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文