Java 休眠内存不足堆错误
我有一个Java应用程序,除其他外,它每小时都会进入我们的Active Directory服务器,并提取所有帐户的列表,并将它们转储到数据库中;这项工作是通过每小时产生一个新线程来完成的,数据库接口是通过Hibernate完成的。线程的run方法(本质上是该线程唯一做的事情)如下所示:Java 休眠内存不足堆错误,java,hibernate,Java,Hibernate,我有一个Java应用程序,除其他外,它每小时都会进入我们的Active Directory服务器,并提取所有帐户的列表,并将它们转储到数据库中;这项工作是通过每小时产生一个新线程来完成的,数据库接口是通过Hibernate完成的。线程的run方法(本质上是该线程唯一做的事情)如下所示: public void run() { try { Thread.sleep(3600000); //we run once an hour, so we sleep for an hou
public void run() {
try {
Thread.sleep(3600000); //we run once an hour, so we sleep for an hour
Thread newHourlyRunThread = new Thread(new HourlyRunThread());
newHourlyRunThread.start();
LDAPNewUsersReport report = new LDAPNewUsersReport();
Calendar calendar = Calendar.getInstance();
calendar.set(0, 0, 0, 0, 0); //We tell the report to look for everything from 12AM Jan 1 0 AD, which should be sufficient to find all created AD objects.
report.runReport(calendar.getTime(), new Date());
HashSet<LDAPEntry> allEntries = report.getAllEntries();
Iterator it = allEntries.iterator();
while (it.hasNext()) {
ContactParser.parseContact((LDAPEntry) it.next());
}
}
如果我们只运行一个实例(因此,之后不会产生新线程),这一切都会非常好地工作,但是如果我们多次运行此线程(即,我将执行速度提高到~30秒,以便可以看到问题),Hibernate会报告堆空间不足。这似乎不是一个特别密集的数据集(只有大约6K个条目),但当我们将代码转移到staging错误以准备推到生产时,我看到了相同的错误。我在编写有效线程方面没有经验,在Hibernate方面也没有经验,因此,如果有人知道什么可能会耗尽我们的堆空间(此应用程序中的另一个主要线程没有同时运行,并且占用了数百KB的内存总量),请查看代码,如有任何建议,我将不胜感激
提前感谢。是一款免费的开源强大Java堆分析器。我已经多次使用它来确定内存泄漏的来源。使用此工具,您将能够快速查看hibernate是否是要惩罚的对象;-) 您可以使用重新编写,我怀疑部分问题在于您正在创建大量
HourlyRunThread
对象,而您只需要一个
例如,此测试演示了如何安排线程每秒运行10秒
@Test(expected = TimeoutException.class)
public void testScheduledExecutorService() throws InterruptedException, ExecutionException, TimeoutException {
final AtomicInteger id = new AtomicInteger();
final ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
service.scheduleAtFixedRate(new Runnable() {
public void run() {
System.out.println("Thread" + id.incrementAndGet());
}
}, 1, 1, TimeUnit.SECONDS).get(10, TimeUnit.SECONDS);
}
这将提供运行时所期望的输出,因为此测试在10秒的运行时间内创建了近10k个线程
private static final class HourlyRunThread extends Thread {
private static final AtomicInteger id = new AtomicInteger();
private final int seconds;
private HourlyRunThread(final int seconds) {
super("Thread" + id.incrementAndGet());
this.seconds = seconds;
}
public void run() {
try {
Thread.sleep(seconds);
if (seconds < 10) {
Thread newHourlyRunThread = new Thread(new HourlyRunThread(seconds));
newHourlyRunThread.start();
}
// do stuff
System.out.println(getName());
} catch (InterruptedException e) {
}
}
}
@Test
public void testThreading() {
final Thread t = new HourlyRunThread(1);
t.start();
}
private静态最终类HourlyRunThread扩展线程{
私有静态最终AtomicInteger id=新的AtomicInteger();
私人最后整数秒;
私有HourlyRunThread(最后整数秒){
超级(“线程”+id.incrementAndGet());
这个。秒=秒;
}
公开募捐{
试一试{
睡眠(秒);
如果(秒<10){
线程newHourlyRunThread=新线程(新的HourlyRunThread(秒));
newHourlyRunThread.start();
}
//做事
System.out.println(getName());
}捕捉(中断异常e){
}
}
}
@试验
公共void testThreading(){
最终螺纹t=新的小时退螺纹(1);
t、 start();
}
看起来您正在进行批量插入或更新,在这种情况下,您应该定期刷新和清除Hibernate会话,以便会话级缓存不会占用超过分配的空间
请参阅Hibernate手册中关于的一章,以获取有关如何完成此任务的建议
此外,我强烈建议找到另一种方法在预定的时间范围内启动您的任务,或者使用Jon Freedman建议的ScheduledExecutorService,或者使用类似的库。在启动实际线程执行工作之前,让线程休眠3600000毫秒似乎是一种非常有问题(且不确定)的处理方法。谢谢大家的建议,但事实证明,我们收到的错误实际上是由本地测试和登台之间的配置错误引起的——数据库是新的,权限配置不正确,无法允许登台区域与创建的数据库对话。当使用正确的权限运行时,它就像一个符咒
我肯定会考虑为Hibernate设置批处理设置,并转移到线程调度程序,而不是我当前的黑客系统。我不小心为每个事务创建了一个新的
会话工厂。由于某种原因,GC
无法清理那些旧的sessionfactories
始终使用相同的SessionFactory
实例解决了我的问题。您尝试过分析应用程序吗?另外,查看-Xmx
和-Xms
命令行参数。有一些有用(免费)的Java内存评测软件包。@Dave Jarvis:正在尝试评测。至于增加堆大小:理想情况下,我希望尽量避免这种情况,因为这最终将运行在一个有许多其他进程的服务器上,我希望成为一个好公民,而不是为一个小应用程序需要大量内存。谢谢,我来试试。@jon freedman:谢谢你对线程启动的建议。我有一种感觉,这不是最好的方法,我会朝着比现在更好的方向努力。我也非常感谢有关Hibernate批处理的信息,我会看看这是否会有所不同。我还建议在单个事务中每小时运行一次“任务”的所有工作,而不是每次更新一个事务-这可能有助于总的执行时间,而且可能在语义上更为正确(也就是说,所有作业一次成功,或者没有一个成功)。不过,这取决于所需的隔离/原子性级别。
private static final class HourlyRunThread extends Thread {
private static final AtomicInteger id = new AtomicInteger();
private final int seconds;
private HourlyRunThread(final int seconds) {
super("Thread" + id.incrementAndGet());
this.seconds = seconds;
}
public void run() {
try {
Thread.sleep(seconds);
if (seconds < 10) {
Thread newHourlyRunThread = new Thread(new HourlyRunThread(seconds));
newHourlyRunThread.start();
}
// do stuff
System.out.println(getName());
} catch (InterruptedException e) {
}
}
}
@Test
public void testThreading() {
final Thread t = new HourlyRunThread(1);
t.start();
}