我有一个自己编写的 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 超出范围并被收集。如果这能解决问题,我将重写并尝试。