近期发现在Lerx的一些客户中出现 java.lang.OutOfMemoryError: Java heap space 错误。
经过测试,在少量的一些栏目页中会出现这种情况。查询资料,是内存不足的原因。分析原因,一些用户在发表文章时采用word粘贴的方式,文章正文中含有大量的Word垃圾代码,这使得文章正文的长度超长,当采用List<Article>取这结果集时,这个List可能超过了内存限制。原因是Lerx的许多客户采用的是虚拟主机,这些主机用户被空间商限制了JVM的内存,比如只有64MB。测试时,独立主机均没有这种现象发生。
两种方法可以解决这种问题。
一个将HQL的“from OBJ” 语句格式改为“select new OBJ(o.a,o.b,o.c ...)”方式,在提取时仅提取需要的字段。这种方式就需要在OBJ对象中添加构造函数。
如:
public class OBJ { private Long id; private String a; private String b; private String c; ... public OBJ{} public OBJ(Long id,String a,String b,String c){ this.id=id; this.a=a; this.b=b; this.c=c; ... } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getA() { return a; } public void setA(String a) { this.a = a; } ..... }
这种方式常常会发生问题,比如:select new OBJ(...)方括号内的参数与构造函数中的顺序或类型不一致。再者,当出现Timestamp类型是会很难处理,系统会直接报错,网上有一个转换的方法,但经测试该方法在本例中无效。
综合上述,在Lerx中,采用了另一种方法,改变一下hibernate的传统使用方法。
原先,我们在用hibernate时,提取时直接提取了对象的List,现在改一下,改为仅提取对象的id,这将会只占用极小的内存空间。
要用到对象时,再打一下就行了。
如:
final String hql = "select a.id from ArticleThread a " + hqlTmp + orderStr; HibernateCallback hc = new HibernateCallback() { @SuppressWarnings("unchecked") @Override public Object doInHibernate(org.hibernate.Session session) throws HibernateException, SQLException { int n = pageSize; if (n == 0) { n = 10; } Query query = session.createQuery(hql); query.setFirstResult((page - 1) * pageSize); query.setMaxResults(n); List<Long> list = query.list(); if (list.isEmpty()) { // System.out.println("找不到记录"); } // System.out.println("测试点5"); return list; } };