【问题标题】:Partial reading and writing data via Spring Batch - OutOfMemoryError: GC overhead limit exceeded通过 Spring Batch 部分读写数据 - OutOfMemoryError: GC overhead limit exceeded
【发布时间】:2018-09-21 15:07:04
【问题描述】:

我正在运行一个带有春季批处理作业的应用程序。当我尝试从一个数据源收集一些数据并将其发布到另一个数据源时,出现以下异常。

o.s.batch.core.step.AbstractStep -  Encountered an error executing step upload in job reviewsToYtBatchJob
java.lang.OutOfMemoryError: GC overhead limit exceeded
at com.mysql.jdbc.Buffer.<init>(Buffer.java:59)
at com.mysql.jdbc.MysqlIO.nextRow(MysqlIO.java:1967)
at com.mysql.jdbc.MysqlIO.readSingleRowSet(MysqlIO.java:3401)
at com.mysql.jdbc.MysqlIO.getResultSet(MysqlIO.java:483)
at com.mysql.jdbc.MysqlIO.readResultsForQueryOrUpdate(MysqlIO.java:3096)
at com.mysql.jdbc.MysqlIO.readAllResults(MysqlIO.java:2266)
at com.mysql.jdbc.ServerPreparedStatement.serverExecute(ServerPreparedStatement.java:1485)
at com.mysql.jdbc.ServerPreparedStatement.executeInternal(ServerPreparedStatement.java:856)
at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:2318)
at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52)
at com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java)
at org.springframework.batch.item.database.JdbcCursorItemReader.openCursor(JdbcCursorItemReader.java:126)

我的问题是:

  • 如何获取堆大小参数?
  • 如何获取部分数据?

它只适用于少量数据。我也试过这个:

reader.setFetchSize(CHUNK_SIZE); //JdbcCursorItemReader
uploadStep.chunk(CHUNK_SIZE);  //SimpleStepBuilder

CHUNK_SIZE 尝试从 100 到 10000 如果我将选定的数据限制为它可以工作的大小,则不会超过堆大小。

protected ItemReader<Review> reader() {
        JdbcCursorItemReader<Review> reader = new JdbcCursorItemReader<>();
        reader.setDataSource(dataScource);
        reader.setSql(
         //sql query
        );
        reader.setFetchSize(CHUNK_SIZE);
        reader.setRowMapper(
                (rs, rowNum) -> new Review(
                        rs.getLong("reviewId"),
                        //map data

                )
        );
        return reader;
    }

private ItemProcessor<Review, ReviewTo> processor() {
        return review -> new ReviewTo(
                //parameters
        );
    }
private ItemWriter<ReviewTo> writer() {
    return new ItemWriter<>(client);
}

private TaskletStep uploadStep() {
    SimpleStepBuilder<Review, ReviewTo> uploadStep = new SimpleStepBuilder<>(stepBuilderFactory.get("upload"));
    return uploadStep
            .chunk(CHUNK_SIZE)
            .reader(reader())
            .processor(processor())
            .writer(writer())
            .allowStartIfComplete(true)
            .build();
}

@Bean
public Job reviewsToYtBatchJob() {
    return jobBuilderFactory.get(JOB_NAME)
            .start(//generate table)
                    .build())
            .next(stepBuilderFactory.get("createTmpTable")
                    .tasklet(//step)
                    .build())
            .next(uploadStep())
            .next(stepBuilderFactory.get("moveTmpTableToDestination")
                    .tasklet(//step)
                    .build())
            .build();
}

【问题讨论】:

    标签: java garbage-collection jvm spring-batch jvm-arguments


    【解决方案1】:

    块处理的整个想法是不将整个数据集加载到内存中,而是以块的形式进行。所以像你一样使用面向块的步骤是要走的路。

    如何获取堆大小参数?

    嗯,那个参数就是你用-Xms-Xmx 传递给JVM 的。请参阅 JVM 文档中的默认值。

    如何获取部分数据?

    当您设置面向块的步骤时,Spring Batch 将根据您的步骤的 chunkSize 和您的JdbcCursorItemReader 的 fetchSize 自动执行此操作。顺便说一句,我看到你为这两个参数设置了相同的值,这是一件好事!匹配 fetchSize 和 chunkSize 通常会带来更好的性能。

    所以我认为你的问题并不是真正的问题,因为当增加块大小时,更多的项目会加载到内存中,直到出现 OOM 错误,这是正常的。

    【讨论】:

    • 感谢您的回答!我使用远程虚拟机,并且想知道如何不从文档中而是从实际中学习此参数的值。感谢您提供有关 chunkSize 和 fetchSize 的 cmets。看起来我只需要为 CHUNK_SIZE 选择一个合适的值。虽然我尝试了从 10 到 700000 的不同值。越大的值越快失败。我猜是 value 太大了,一次加载所有数据可能是个问题,如果 value 太小,可能会产生很多垃圾,所以 GC 无法处理使用可用内存。
    【解决方案2】:

    内存空间不足。它使用参数 CHUNK_SIZE = 100000 和 -Xmx4g。有一个带有虚拟机参数的配置文件,我可以在其中增加堆大小。

    【讨论】:

      猜你喜欢
      • 2018-06-03
      • 2016-08-31
      • 2019-12-28
      • 2015-11-30
      • 2012-06-09
      • 2015-05-04
      • 2023-03-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多