2012-06-11 39 views
0

我有一个自己写的Java应用程序 - 一个小邮件监视器。它可以与MySQL数据库一起工作,该数据库有一个定期填充到别处的表。它会查看表格,并在记录出现在表格中时发送邮件。Java:多线程邮件应用程序泄漏内存

我的问题是,应用程序泄漏内存。我确信它没有,因为我使用的所有东西的范围似乎都消失了,使得所有使用垃圾的对象都可以收集。但过了一段时间(取决于-Xmx通过),应用程序停止并出现OutOfHeapSpace错误。

我不能发布整个代码,因为它不是我的了,但我试图用伪代码重新创建它。

Main: 
    Startup 
    Create .lock file (FileChannel) 
    Instantiate Main Class 

Constructor Main: 
    Class.ForName for the MySQL driver 
    Read properties file (settings) 
    Create connection object (MySQL) 
    Fetch unsent mail ids (ArrayList) 
    while(true) 
    while have more mail ids 
     new Thread(Top Mail ID, MySQL Connection object, Sleep Time, Blacklist); 
    end while 
    if have no more mail ids in ArrayList: 
     sleep for a number of seconds (usually 300) 
    end if 
    end while 

Constructor Thread: 
    Prepare Statement 
    New Thread(this).start(); 
    Sleep 

Thread run(): 
    Select Record by passed Mail ID 
    Extract everything (Sender, Receiver, Subject etc.) 
    Check Blacklist, return if matched 
    Extract Attachments as blobs 

我迄今为止尝试:

jvisualvm已经表明了我如何随时间而变化的内存。我所看到的是堆中的一条锯齿线:分配和收集内存经常发生,但是,在收集之后,总是会有比上一次收集更多的内存分配。线程的数量似乎很好,它总是下降到标准数字。

jvisualvm中的信息量对我来说太多了。有列出的线程我无法识别,我创建的线程没有列为我的类,所以很难确定什么是“我的”代码。

任何人都可以在我的伪代码中识别多线程的任何常见错误,或推荐任何工具让我更容易确定我的泄漏?

谢谢。

编辑1:通过传递给所有线程的连接对象的数据访问本身是同步的。

编辑2:我检查是不可发送的邮件的数目(如这些停留在数据库中),大约有10

编辑3:我发现的Eclipse内存分析器,安装它,它暗示在问题。看起来,在保留一个连接对象的同时使用PreparedStatements使所有PreparedStatements的HashMap始终通过此连接运行,从而将添加的所有数据添加到它。我依靠PreparedStatement超出范围并被收集。如果这能解决问题,我会重写并尝试。

+1

在这种情况下,我至少会尝试一次FindBugs。 –

+0

我不知道那个工具。现在安装。谢谢。 – 0xCAFEBABE

回答

0

如果这对任何人都有兴趣,我解决了内存问题。

应用程序实际上没有泄漏内存。当我传递相同的连接对象时,它似乎将每个准备好的语句的结果保存在一个大的散列映射中。因此,内存使用量不断增加。我通过使用jvisualvm创建并加载到Eclipse Memory Analyzer中的堆转储发现了这一点。

我重写了应用程序以使用ThreadPool(缓存),为每个线程打开一个专用连接并关闭每个线程末尾的连接。

1

从您的伪代码,它看起来好像你正在创建一个线程每个电子邮件发送,这似乎没有那么高效。我知道你提到线程的数量是“正常的”,但是代码似乎表明不是。您可以尝试使用线程池,从而您拥有有限数量的线程工作者,并通过某种作业队列将工作传递给他们。

你知道是否有一些错误,可能会导致线程不完成或让他们“挂”?这可能是一个内存泄漏,所以看看你的错误处理代码。

以前我用jprofiler取得了一些成功,也许它会是一个有用的选择。

+0

感谢您的意见。 – 0xCAFEBABE