我正在并行运行大约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);
}
}
}
我是否必须以某种方式释放内存或在完成后删除作业?我不明白为什么内存消耗会保持那么高。
祝你好运
答案 0 :(得分:2)
从htop获取该信息并从中获取任何信息并不是一个好主意。这是因为Java内存管理。
Java从操作系统分配内存并在内部管理该内存。这些都与垃圾收集和世代记忆模型等术语相关联。
基本上,如果通过在我们的应用程序中删除对这些对象的引用来释放内存,则不会立即释放内存。仅当已经由Java分配的内存已满时,才会触发垃圾收集循环。该周期不会(必然)释放针对操作系统的内存。它将在第一步中为Java程序提供该内存,同时仍然保留相对于操作系统的内存。
如果Java VM中的启发式测试确定您分配了太多内存,它将向操作系统释放内存,但这是您不应该依赖的内容。
这就是为什么你仍然看到Java进程保留20G的原因。如果不仔细查看应用程序内部,您甚至不会知道内存是在内部释放还是填充了死对象。
如果您想更好地了解应用程序的内存占用,我建议您执行以下操作:JConsole或JVisualVM等工具(此处需要Visual GC插件)允许您检查内存的内部由Java VM分配。在那个记忆中严格要求一个称为旧或终身的记忆区域,其他一切与你的问题无关(如果你好奇的话,搜索一代内存管理一词)。如果要触发垃圾收集以删除那些已经死亡(但尚未清除)的对象,请在应用程序中显式调用System.gc()
或通过JConsole或JVisualVM触发它(两者都有一个按钮这样做)。垃圾收集后直接的内存消耗是您当前正在查找的数字。