Spring Batch Jobs不释放内存

时间:2017-10-06 07:50:30

标签: java spring memory-management spring-batch java-memory-leaks

我正在并行运行大约18.000个弹簧作业,每个作业都有一步。 每一步都包括从文件中读取,转换和操作这些值并将它们写入Mongo和MySql数据库,没有什么不寻常之处。 完成所有作业后,内存消耗将保持在20GB USED 并保持不变。 我按如下方式构建我的弹簧批成员:

@Autowired
public ArchiveImportManager(final JobRepository jobRepository, final BlobStorageConfiguration blobConfiguration,
        final JobBuilderFactory jobBuilderFactory, final StepBuilderFactory stepBuilderFactory,
        final ArchiveImportSettings settings) {
    this.jobBuilderFactory = jobBuilderFactory;
    this.stepBuilderFactory = stepBuilderFactory;
    this.jobLauncher = new SimpleJobLauncher();
    final ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
    threadPoolTaskExecutor.setCorePoolSize(THREAD_POOL_SIZE);
    threadPoolTaskExecutor.setMaxPoolSize(THREAD_POOL_SIZE);
    threadPoolTaskExecutor.setQueueCapacity(THREAD_POOL_QUEUE);
    threadPoolTaskExecutor.initialize();
    this.jobLauncher.setTaskExecutor(threadPoolTaskExecutor);
    this.jobLauncher.setJobRepository(jobRepository);
}

我按如下方式创建了一份工作:

private Job createImportJob(final ArchiveResource archiveResource, final int current, final int archiveSize) {

    final String name = "ImportArchiveJob[" + current + "|" + archiveSize + "]"
            + new Date(System.currentTimeMillis());
    final Step step = this.stepBuilderFactory
            .get(name)
            .<ArchiveResource, ArchiveImportSaveData> chunk(1)
            .reader(getReader(archiveResource, current, archiveSize))
            .processor(getProcessor(current, archiveSize))
            .writer(getWriter(current, archiveSize))
            .build();

    return this.jobBuilderFactory
            .get(name)
            .flow(step)
            .end()
            .build();

}

在循环中启动所有作业:

private void startImportJobs(final List<ArchiveResource> archives) {
    final int size = archives.size();
    for (int i = 0; i < size; i++) {
        final ArchiveResource ar = archives.get(i);
        final Job j = createImportJob(ar, i, size);
        try {

            this.jobLauncher.run(j, new JobParametersBuilder()
                    .addDate("startDate", new Date(System.currentTimeMillis()))
                    .addString("progress", "[" + i + "|" + size + "]")
                    .toJobParameters());
        } catch (final JobExecutionAlreadyRunningException e) {
            log.info("Already running", e);
        } catch (final JobRestartException e) {
            log.info("Restarted", e);
        } catch (final JobInstanceAlreadyCompleteException e) {
            log.info("ALready completed", e);
        } catch (final JobParametersInvalidException e) {
            log.info("Parameters invalid", e);
        }
    }
}

我是否必须以某种方式释放内存或在完成后删除作业?我不明白为什么内存消耗会保持那么高。

祝你好运

1 个答案:

答案 0 :(得分:2)

从htop获取该信息并从中获取任何信息并不是一个好主意。这是因为Java内存管理。

Java从操作系统分配内存并在内部管理该内存。这些都与垃圾收集和世代记忆模型等术语相关联。

基本上,如果通过在我们的应用程序中删除对这些对象的引用来释放内存,则不会立即释放内存。仅当已经由Java分配的内存已满时,才会触发垃圾收集循环。该周期不会(必然)释放针对操作系统的内存。它将在第一步中为Java程序提供该内存,同时仍然保留相对于操作系统的内存。

如果Java VM中的启发式测试确定您分配了太多内存,它将向操作系统释放内存,但这是您不应该依赖的内容。

这就是为什么你仍然看到Java进程保留20G的原因。如果不仔细查看应用程序内部,您甚至不会知道内存是在内部释放还是填充了死对象。

如果您想更好地了解应用程序的内存占用,我建议您执行以下操作:JConsole或JVisualVM等工具(此处需要Visual GC插件)允许您检查内存的内部由Java VM分配。在那个记忆中严格要求一个称为旧或终身的记忆区域,其他一切与你的问题无关(如果你好奇的话,搜索一代内存管理一词)。如果要触发垃圾收集以删除那些已经死亡(但尚未清除)的对象,请在应用程序中显式调用System.gc()或通过JConsole或JVisualVM触发它(两者都有一个按钮这样做)。垃圾收集后直接的内存消耗是您当前正在查找的数字。