用Spring定时器写了个程序,查询数据库固定次数后,内存溢出
该程序是用Spring定时器执行的,定时用hibernate框架查询数据库,将查询到的结果写成文本文件。
启动定时器之后每次在生成了52个文本文件之后,就内存溢出了。
由于代码不多我直接贴代码和错误提示吧!
希望各位高手有时间帮忙看一下!
下面的是定时器执行的类和方法
public class BillInfoExporter { private static String[] LOCATIONS = { "spring-hibernate.xml", "spring-minidao.xml" }; private Map<String, String> config; protected final static Logger logger = Logger.getLogger("log4j.properties"); @SuppressWarnings("unused") public boolean billInfoExport() { ApplicationContext ctx = null; BillingService billingService = null; TextFileWriter textFileWriter = null; File file = null; List<Map> ioGoods = null; List<Map> selfGoods = null; try { if (ctx == null) { ctx = new ClassPathXmlApplicationContext(LOCATIONS); if (billingService == null) { billingService = (BillingService) ctx.getBean("billingServiceImpl"); } } StringBuffer buff = new StringBuffer(); ioGoods = billingService.billingIOGoodsMessage(); BillExporterUtils.billExport(buff, ioGoods); selfGoods = billingService.billingSelfGoodMessage(); BillExporterUtils.billExport(buff, selfGoods); file = new File(buildFileName()); textFileWriter = new TextFileWriter(file, false); textFileWriter.outPut(buff.toString()); buff = null; return true; } catch (Exception e) { String resultLog = "未能成功从数据库中查询自用物资、备案清单进出单号信息,当前系统日期为:" + CalendarUtils.formatDate(new Date()); logger.error(resultLog, e); } finally{ try { if(textFileWriter!=null){ textFileWriter.closeFile(); textFileWriter = null; } if (ioGoods != null) { ioGoods = null; } if (selfGoods != null) { selfGoods = null; } System.gc(); } catch (Exception e) { e.printStackTrace(); } } return false; } private String buildFileName(){ String fileName = null; StringBuffer sBuffer = new StringBuffer(); Date currentDate = new Date(); fileName = config.get("outputPath"); if (StringUtils.isBlank(fileName)) { fileName = System.getProperty("user.dir") + "/output"; System.out.println(fileName); } sBuffer.append(fileName).append("/"); sBuffer.append("SB_"); sBuffer.append(CalendarUtils.format(currentDate, "MM")).append('_'); sBuffer.append(currentDate.getTime()); sBuffer.append(".bi"); fileName = sBuffer.toString(); return fileName; } public Map<String, String> getConfig() { return config; } public void setConfig(Map<String, String> config) { this.config = config; } }
下面是加载Spring定时器的主函数类
public class BillInfoScheduler { public static void main(String[] args) { try { String configFile = null; if(args!=null && args.length>0){ configFile = args[0].trim(); }else{ configFile = "scheduler.xml"; } execute(configFile); } catch (Exception e) { e.printStackTrace(); } } public static void execute(String configFile){ ApplicationContext ctx = new ClassPathXmlApplicationContext(configFile); } }
下面是定时器的配置文件
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <beans> <!-- 第零步: 配置计费信息输出的路径 --> <bean id="config" class="java.util.HashMap"> <constructor-arg> <map> <entry key="outputPath" value="D:/work_dev/YanKe/Source/ECLink_packed/output" /> </map> </constructor-arg> </bean> <!-- 第一步: 配置好要定时调用的业务类 --> <bean id="billInfoExporter" class="org.broadengate.eclink.schedule.BillInfoExporter"> <property name="config" ref="config" /> </bean> <!-- 第二步: 定义好具体要使用类的哪一个业务方法 --> <bean id="printTimerJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <!-- 目标bean --> <property name="targetObject" ref="billInfoExporter" /> <!-- 要执行目标bean的哪一个业务方法 --> <property name="targetMethod" value="billInfoExport" /> <!-- 是否并发 --> <property name="concurrent" value="false" /> </bean> <!-- 第三步: 定义好调用模式: 如每隔2秒钟调用一次或每天的哪个时间调用一次等 --> <bean id="printTimerTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail" ref="printTimerJob" /> <property name="cronExpression" value="0/2 * * * * ?" /> <!-- 每月最后一日的上午10:15触发 0 15 10 L * ? --> <!-- 每天晚上23:00触发 0 0 23 * * ? --> </bean> <!-- 启动定时器 --> <!--第四步 把定义好的任务放到调度(Scheduler)工厂里面,注意这里的ref bean --> <bean id="schedulerFactoryBean" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="applicationContextSchedulerContextKey" value="applicationContext" /> <property name="triggers"> <list> <ref bean="printTimerTrigger" /> </list> </property> </bean> <!-- end --> </beans>
控制台报的错,是说我在执行定时任务的方法是时由于内存溢出而无法执行吗?
[org.quartz.core.JobRunShell]Job DEFAULT.printTimerJob threw an unhandled Exception: org.springframework.scheduling.quartz.JobMethodInvocationFailedException: Invocation of method 'billInfoExport' on target class [class org.broadengate.eclink.schedule.BillInfoExporter] failed; nested exception is java.lang.OutOfMemoryError: PermGen space at org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean$MethodInvokingJob.executeInternal(MethodInvokingJobDetailFactoryBean.java:320) at org.springframework.scheduling.quartz.QuartzJobBean.execute(QuartzJobBean.java:113) at org.quartz.core.JobRunShell.run(JobRunShell.java:216) at org.quartz.simpl.SimpleThreadPool$WorkerThread.run(SimpleThreadPool.java:549) Caused by: java.lang.OutOfMemoryError: PermGen space
请教各位大侠 哪里造成内存溢出了啊……?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
老实说我也不清楚,这个项目不是我做的。而且DAO层用的是hibernate,但是没有impl实现类。用了一套公司自己封装的DAO框架,只要写SQL语句,按照其他Service层和DAO接口类的方法定义规范来定义就可以了。 hibernate框架中关闭连接通常是怎么做的?我都不是很记得了…… 现在Service层和DAO层都是通过注解来配置成Spring的bean的。
我估计是连接的问题
你怎么关闭连接的?
你在job的执行方法里面去再一次初始化Spring容器。这样不对的,所有的Spring Bean的配置,一般只启动一个ApplicationContext
回复
嗯 昨晚在家我发现可能这里不对 今早回来,试了一下,果然是这里出问题了,创建了太多Spring的Bean实例,谢啦!
引用来自“ForEleven”的评论
这样用Spring 不出问题才怪。不是代码的问题,是Spring+Hibernate根本不是这样用的,你应该在Quartz的一个Job里面注入一个DataSource,整个数据库连接交给Spring容器管理
往Quartz里注入datasource,Quartz会默认去数据库里找一堆叫QRTZ_LOCKS的表
回复
是么?这个没注意过,一般是注入Service层的Bean。这里只是表示这个意思
这样用Spring 不出问题才怪。不是代码的问题,是Spring+Hibernate根本不是这样用的,你应该在Quartz的一个Job里面注入一个DataSource,整个数据库连接交给Spring容器管理
结一下帖:
其实问题很简单,是低级错误啦……
BillInfoExporter是定时器调用的类,billInfoExport()是定时器执行的方法。
原先,我在billInfoExport()方法中创建Spring的ApplicationContext对象,并且通过它获得我要使用的Service类对象。
这样一来,每次定时器调用该方法都去初始化Spring,创建很多Bean实例。于是就内存溢出了……
修改后的代码:
我把定时器任务要使用的Service放在static代码块中获取了。