java.lang.OutOfMemory线程"main"异常错误:无法创建新的本机线程



我正在使用Formscanner,它在处理一些图像后给出错误:

Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:717)
at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:950)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1357)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:134)
at com.albertoborsetta.formscanner.api.FormTemplate.findPoints(FormTemplate.java:852)
at com.albertoborsetta.formscanner.model.FormScannerModel.analyzeFiles(FormScannerModel.java:562)
at com.albertoborsetta.formscanner.main.FormScanner.main(FormScanner.java:145)

查找点方法如下:

public void findPoints(BufferedImage image, int threshold, int density,
int size) throws FormScannerException {
height = image.getHeight();
width = image.getWidth();
int cores = Runtime.getRuntime().availableProcessors();
ExecutorService threadPool = Executors.newFixedThreadPool(cores - 1);
HashSet<Future<HashMap<String, FormQuestion>>> fieldDetectorThreads = new HashSet<>();
HashMap<String, FormQuestion> templateFields = template.getFields();
ArrayList<String> fieldNames = new ArrayList<>(templateFields.keySet());
Collections.sort(fieldNames);
for (String fieldName : fieldNames) {
Future<HashMap<String, FormQuestion>> future = threadPool.submit(new FieldDetector(threshold, density, size, this, templateFields.get(fieldName), image));
fieldDetectorThreads.add(future);
}
for (Future<HashMap<String, FormQuestion>> thread : fieldDetectorThreads) {
try {
HashMap<String, FormQuestion> threadFields = thread.get();
for (String fieldName : threadFields.keySet()) {
FormQuestion field = threadFields.get(fieldName);
fields.put(fieldName, field);
for (Entry<String, FormPoint> point : field.getPoints().entrySet()) {
if (point.getValue() != null) {
pointList.add(point.getValue());
}
}
}
} catch (InterruptedException | ExecutionException e) {
throw new FormScannerException(e.getCause());
}
}
threadPool.shutdown();
}

上述函数在循环中被调用,Java 进程的数量增加,并在某一时刻引发上述异常。

有没有办法在调用关闭方法后杀死这些线程。我不是Java开发人员。我做了一些研发。但我没有成功。

问题来自用于保存每个实例以供以后检查的Set<Future>

在聊天中,你告诉我你正在检查 120.000 个文件。这意味着创建了很多 Future,当池找到一个插槽时,它将创建一个Thread来执行Callable

由于Set保存每个实例,因此Thread不会被垃圾收集,这给您泄漏的原因。您需要删除每个使用过的Future,以使GC清除内存以进行下一Thread

使用迭代器而不是循环本身很简单,可以让您在使用前删除当前实例

Iterator<Future<HashMap<String, FormQuestion>>> iterator = fieldDetectorThreads.iterator();
while (iterator.hasNext()) {
//get the next instance
Future<HashMap<String, FormQuestion>> thread = iterator.next();
//Remove it from the set
iterator.remove();
//then work on that instance just like before
try {
HashMap<String, FormQuestion> threadFields = thread.get();
for (String fieldName : threadFields.keySet()) {
FormQuestion field = threadFields.get(fieldName);
fields.put(fieldName, field);
for (Entry<String, FormPoint> point : field.getPoints().entrySet()) {
if (point.getValue() != null) {
pointList.add(point.getValue());
}
}
}
} catch (InterruptedException | ExecutionException e) {
throw new FormScannerException(e.getCause());
}
}

此解决方案未经测试,但应该能够足够快地释放内存。

但是,如果提交请求的循环需要很长时间才能结束(在检查第一个请求之前生成 120k 未来),这将在每个请求发送之前中断。

在这种情况下,可能需要将该逻辑拆分为两个线程,一个用于发送请求,一个用于检查结果,直到第一个线程结束并且集合为空。


以防万一,我会在循环后添加关闭请求

threadPool.shutdown();

这应该是没有必要的,但奇怪的是,没有它,我的测试程序就不会结束......即使每个线程都已处理,它们似乎仍然存在并阻塞主线程。

最新更新