我逐行阅读了1,3GB的文本文件。我提取并格式化内容以满足我的需要,并将其再次保存到新的文本文件中。
最初我只使用了主线程。但是提取和格式化需要大量的CPU时间,我想通过多线程来加速它。
但那是我的个人资料显示的内容:
当我开始使用多个线程时,垃圾收集器时间上升到100%。因此会抛出java.lang.OutOfMemoryError: GC overhead limit exceeded
个错误。
我有一个处理单行的函数,我在newFixedThreadPool
内执行它。如果我将一个或四个线程分配给池,则无关紧要。
使用不同的分析器,我无法找出导致问题的代码。当我只使用主线程时,我不明白为什么我的GC为0,0%。
有没有人在不看代码的情况下有想法?
更新:我试图抽象一些代码:
A.java
ExecutorService executor = Executors.newFixedThreadPool(4);
while((line = reader.readLine()) != null) {
Runnable processLine = new Runnable() {
private String line;
private Runnable init(String line) {
this.line = line;
return this;
}
@Override
public void run() {
processLine(line); // @B.java
}
}.init(line);
executor.execute(processLine);
}
B.java
public int processLine(String line) {
String[][] outputLines = new String[x][y];
String field;
for(... x ...) {
for(... y ...) {
field = extractField(line); // @C.java
...
outputLines[x][y] = formatField(field); // @C.java
}
}
write(outputLines); // write the generated lines to BufferedWriter(s)
}
C.java
public String extractField(String line) {
if(filetype.equals("csv") {
String[] splitLine = line.split(";");
return splitLine[position];
}
...
}
public String formatField(String field) {
if(trim == true) {
field = field.trim();
}
...
}
答案 0 :(得分:2)
我希望您的应用程序使用接近所有可用堆空间。这将导致越来越多的JVM时间用于运行垃圾收集器......徒劳地尝试回收空间。这正是GC开销限制设计要处理的情况。
简而言之,要么存储泄漏,要么应用程序需要更多内存。
我有一个处理单行的函数,我在newFixedThreadPool中执行它。如果我将一个或四个线程分配给池,则无关紧要。
强烈暗示(对我而言)导致问题的不是线程,而是实现多线程的方式。
<强>更新强>
我认为你问题的根本原因是:
ExecutorService executor = Executors.newFixedThreadPool(4);
请注意,javadoc表示该方法使用无界工作队列创建执行程序。
假设您的读者线程(执行A
代码的线程)可以比执行处理的线程更快地读取和排队行,可以处理,格式化和输出它们。
在这种情况下,会发生的情况是执行程序的工作队列将变得更长,更长并且......最终它将变得如此之长以至于堆接近完整的可到达对象。这将导致GC花费很长时间跟踪队列,你会得到一个OOME。
如果这种情况在实践中诞生,那么您的应用程序就会出现内存泄漏的不良方面......即使您认为它实际上并不是泄漏。
解决方案很简单,使用有界工作队列创建执行程序。您需要通过直接实例化ThreadPoolExecutor并提供适当配置的工作队列对象来实现此目的。