我有一个自编的Java应用程序 - 一个小邮件监视器。它适用于MySQL数据库,该数据库有一个表,它在其他地方正常填充。它会查看表格,并在记录显示在表格中时发送邮件。
我的问题是,应用程序泄漏了内存。我很确定它没有,因为我使用的所有东西的范围似乎消失了,使得所有对象都被垃圾收集。但过了一段时间(取决于我传递的-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 Memory Analyzer,安装了它,它暗示了这个问题。似乎在保留一个连接对象的同时使用PreparedStatements会使所有PreparedStatements的HashMap始终在其中的此连接上运行,从而将所有选定的数据添加到其中。我依靠PreparedStatement超出范围并被收集。如果这可以解决问题,我会重写并尝试。
答案 0 :(得分:1)
从您的伪代码中,看起来好像您要为每封要发送的电子邮件创建一个帖子,这看起来效率不高。我知道你提到线程数是"正常",但代码似乎表示不同。您是否可以尝试使用线程池,这样您就拥有了有限数量的线程工作者,并通过某种作业队列将工作传递给它们。
你知道是否有一些错误可能导致线程无法完成或者让他们离开#34;挂起"?这可能是内存泄漏,所以请查看您的错误处理代码。
在过去,我使用jprofiler
取得了一些成功,也许这将是一个有用的选择。
答案 1 :(得分:0)
如果这对任何人都有兴趣,我修复了记忆问题。
应用程序实际上没有泄漏内存。当我传递相同的连接对象时,似乎它保存了在大型hashmap中针对它运行的每个预处理语句的结果。因此,内存使用率不断上升。我通过使用使用jvisualvm创建的堆转储并将其加载到Eclipse Memory Analyzer中来发现这一点。
我重写了应用程序以使用ThreadPool(缓存),为每个线程打开一个专用连接,并在每个线程的末尾关闭连接。