Java中的多线程垃圾收集:外部共享对象可以防止GC?

时间:2013-10-24 18:55:02

标签: java multithreading garbage-collection

考虑以下方法:

public void Parse(String[] S, Objects[] O) throws IOException {
    final int N_THREADS = Runtime.getRuntime().availableProcessors();
    BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<Runnable>(20);
    RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.CallerRunsPolicy();
    ThreadPoolExecutor service =  new ThreadPoolExecutor(N_THREADS, N_THREADS, 0L, TimeUnit.MILLISECONDS, blockingQueue, rejectedExecutionHandler);
    final SomeObject RO = new SomeObject();
    for(String s : S){
        service.execute(new Runnable() {
            public void run() {
                // initialize variables
                for (Object o : O) {
                        V ps = RO.apply(sentence);
                        //more work on ps 
                }
                File f = new File("something");
                FileWriter fw = null;
                try {
                    fw = new FileWriter(f.getAbsoluteFile());
                    BufferedWriter bw = new BufferedWriter(fw);
                } catch (IOException e) {
                    System.out.println(f.getAbsoluteFile());
                }
                BufferedWriter bw = new BufferedWriter(fw);
                for (SentenceAnnotation entry : annotations) {
                    try {
                        bw.write(entry.toString());
                        bw.newLine();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

                try {
                    bw.flush();
                    bw.close();
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }
    service.shutdown();
    while (!service.isTerminated()) {
    }
    long timeEnd = System.currentTimeMillis();
}

其中S是一个大数组(数十万),O是长度为50.我的问题是关于RO对象。如果您将通过所有线程在外部创建并“共享”。现在,当这段代码运行一段时间后,堆空间耗尽,这让我很困惑。我倾向于认为RO对象仍然保持其他已完成的Runnables活着并慢慢消耗掉内存。真的吗?我使用`free -m'监视了linux系统(最新版本的Oracle JDK)的内存消耗,我可以慢慢但肯定地看到内存消失了。我很感谢你能给我的任何建议。

3 个答案:

答案 0 :(得分:1)

 try {
   fw = new FileWriter(f.getAbsoluteFile());
   BufferedWriter bw = new BufferedWriter(fw);
 } catch (IOException e) {
   System.out.println(f.getAbsoluteFile());
 }
 BufferedWriter bw = new BufferedWriter(fw);

您在此部分代码中泄露了未公开的BufferedWriter。您在try子句的范围内创建第一个,不要关闭它。引用消失,但不释放运行时创建的本机句柄。您没有注意到,因为您之后立即为同一文件创建了新的BufferedWriter

答案 1 :(得分:0)

据我所知,您所展示的代码中没有任何可疑内容。

您最好的选择是获取应用程序的堆转储,然后检查填充内存的内容。

您可以使用 JVisualVM 生成堆转储并对其执行基本分析,该文件包含在您jdk的bin文件夹中。关于堆分析,您肯定会发现很多关于堆的问题,例如How to find a Java Memory Leak

答案 2 :(得分:0)

您似乎正在创建两次BufferedWriter。我不太清楚这里的范围问题,但在我看来,这甚至不应该正确编译。尝试在“try”块之前声明BufferedWriter,只需在没有第二次创建的情况下使用它:

        BufferedWriter bw;            
        try {
            fw = new FileWriter(f.getAbsoluteFile());
            bw = new BufferedWriter(fw);
        } catch (IOException e) {
            System.out.println(f.getAbsoluteFile());
        }
        for (SentenceAnnotation entry : annotations) {
            try {
                bw.write(entry.toString());
                bw.newLine();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

如果我是对的,那么你将不会生成“数十万”不必要的BufferedWriter对象。但不保证。

作为一个样式问题,我考虑将“try”块合并为一个并使用一个“catch”而不是两个....除非你打算提供不同的错误信息,当然。

希望有所帮助。

Achim的