这是我的情况:
从我的Grails控制器中,我调用一个服务,它以只读方式查询数据库,将结果转换为JSON,然后返回结果。
规格是:JDK 1.6,Tomcat 5.5,Grails 1.3.4,DB通过JNDI
Tomcats MaxPermSize设置为256m,Xmx设置为128m 编辑:增加内存应该是最后的手段
服务方法:
String queryDB(String queryString) {
StringWriter writer = new StringWriter()
JSonBuilder json = new JSonBuilder(writer)
def queryResult = SomeDomain.findAllBySomePropIlike("%${queryString}%")
json.whatever {
results {
queryResult.eachWithIndex { qr, i ->
// insert domain w/ properties
}
}
}
queryResult = null
return writer.toString()
}
现在,当queryString =='a'时,结果集很大,我最终得到了这个:
[ERROR] 03/Nov/2010@09:46:39,604 [localhost].[/grails-app-0.1].[grails] - Servlet.service() for servlet grails threw exception
java.lang.OutOfMemoryError: GC overhead limit exceeded
at org.codehaus.groovy.util.ComplexKeyHashMap.init(ComplexKeyHashMap.java:81)
at org.codehaus.groovy.util.ComplexKeyHashMap.<init>(ComplexKeyHashMap.java:46)
at org.codehaus.groovy.util.SingleKeyHashMap.<init>(SingleKeyHashMap.java:29)
at groovy.lang.MetaClassImpl$Index.<init>(MetaClassImpl.java:3381)
at groovy.lang.MetaClassImpl$MethodIndex.<init>(MetaClassImpl.java:3364)
at groovy.lang.MetaClassImpl.<init>(MetaClassImpl.java:140)
at groovy.lang.MetaClassImpl.<init>(MetaClassImpl.java:190)
at groovy.lang.MetaClassImpl.<init>(MetaClassImpl.java:196)
at groovy.lang.ExpandoMetaClass.<init>(ExpandoMetaClass.java:298)
at groovy.lang.ExpandoMetaClass.<init>(ExpandoMetaClass.java:333)
at groovy.lang.ExpandoMetaClassCreationHandle.createNormalMetaClass(ExpandoMetaClassCreationHandle.java:46)
at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.createWithCustomLookup(MetaClassRegistry.java:139)
at groovy.lang.MetaClassRegistry$MetaClassCreationHandle.create(MetaClassRegistry.java:122)
at org.codehaus.groovy.reflection.ClassInfo.getMetaClassUnderLock(ClassInfo.java:165)
at org.codehaus.groovy.reflection.ClassInfo.getMetaClass(ClassInfo.java:182)
at org.codehaus.groovy.runtime.callsite.ClassMetaClassGetPropertySite.<init>(ClassMetaClassGetPropertySite.java:35)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.createClassMetaClassGetPropertySite(AbstractCallSite.java:308)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.createGetPropertySite(AbstractCallSite.java:258)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.acceptGetProperty(AbstractCallSite.java:245)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGetProperty(AbstractCallSite.java:237)
at org.codehaus.groovy.grails.plugins.web.filters.FilterToHandlerAdapter.accept(FilterToHandlerAdapter.groovy:196)
at org.codehaus.groovy.grails.plugins.web.filters.FilterToHandlerAdapter$accept.callCurrent(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallCurrent(CallSiteArray.java:44)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:143)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callCurrent(AbstractCallSite.java:159)
at org.codehaus.groovy.grails.plugins.web.filters.FilterToHandlerAdapter.preHandle(FilterToHandlerAdapter.groovy:107)
at org.springframework.web.servlet.HandlerInterceptor$preHandle.call(Unknown Source)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCall(CallSiteArray.java:40)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:117)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.call(AbstractCallSite.java:133)
at org.codehaus.groovy.grails.plugins.web.filters.CompositeInterceptor.preHandle(CompositeInterceptor.groovy:42)
at org.codehaus.groovy.grails.web.servlet.GrailsDispatcherServlet.doDispatch(GrailsDispatcherServlet.java:282)
我在网上发现的一种方法是关于Hibernate和域验证中的一些漏洞,解释here和详细here。我正准备测试它,但我不知道这是否真的是我的问题的解决方案(如果是),最好是清理GORM。
或者我的代码中是否还有其他内存泄漏? 任何想法?
编辑:就我而言,异常发生在调用finder方法的位置。这意味着GORM无法处理数据库返回的数据量,对吧?很抱歉像新手一样问,但我从来没有遇到过这样的问题,即使结果非常大。
答案 0 :(得分:4)
Sun(此链接无效)had documented此OutOfMemoryError
如下:
并行/并发收集器 如果是的话也会抛出OutOfMemoryError 垃圾花了很多时间 收集:如果超过98%的 总时间花在垃圾上 收集和不到2%的 堆被恢复,OutOfMemoryError 将被抛出。这个功能是 旨在防止应用程序 跑了很长一段时间 虽然很少或没有进展 因为堆太小了。如果 必要的,这个功能可以 通过添加选项禁用 -XX:-UseGCOverheadLimit 到命令行。
换句话说,该错误是一个功能,一个增加可用内存的提示(如您所述,这不是您的情况下的首选选项)。一些开发人员consider此功能在每个用例中都没有用,所以请查看关闭它。
答案 1 :(得分:3)
已经提出的建议的另一个选择是在结果页面中工作。不使用动态查找器,而是使用Criteria并自行翻译结果。这是一个天真的伪代码示例:
def offset = 0
def max = 50
while(stillMoreResults) {
def batch = SomeDomain.findAllBySomePropIlike("%${queryString}%", [max: max, offset: offset])
appendBatchToJsonResult(batch)
offset += max
}
您可以根据内存要求调整批量大小。这样可以避免调整内存。
修改的
我刚刚重新阅读了Fletch的回答,并注意到他提到这是一个解决方案,你对此发表了评论。我会离开我的,因为它有一个例子,但如果Fletch给他添加了一个分页示例,我会删除这个答案,因为他在我之前提到过。
答案 2 :(得分:2)
如果您不想增加内存,可能只应搜索大于一定数量的字符串。我想这是某种提前/建议功能;也许你可以在有三个字符左右时开始搜索。否则,分页结果可能是一个选项吗?
顺便说一下,在架构上,控制器旨在处理与外部世界及其格式的交互,即您可能希望您的服务只是返回对象和控制器以进行JSON转换。但这不能解决你目前的问题。
答案 3 :(得分:1)
我还建议您只使用此查询类型提前查询返回所需的属性,并使用用户需要的实际数据获取完整的域对象。
JSON构建器将创建大量对象和comsume内存。例如,在用户的预先输入中,我只返回基本名称信息和id而不是完整对象
答案 4 :(得分:0)
在使用MySQL数据库的Grails应用程序(MySQL已通过Homebrew安装)中,奇怪的是,我只是通过运行应用程序而没有首先启动MySQL服务器而遇到了同样的问题。因此只需运行
mysql.server start
为我解决了这个问题。