近期发现在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;
}
};