2010-11-03 2 views
3

Вот моя ситуация:Как избежать огромного результата запроса вызывает OutOfMemoryException

Из моего контроллера Grails, я называю сервис, который запрашивает базу данных только для чтения, преобразует результат в формате JSON, и возвращает результат.

функции являются: JDK 1.6, Tomcat 5.5, Grails 1.3.4, БД с помощью JNDI

Tomcats MaxPermSize установлен на 256M и Xmx к 128M.
EDIT: Увеличение памяти должно быть последним средством

Метод обслуживания:

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 == «а» набор результатов огромна, и я в конечном итоге с этим:

[ERROR] 03/Nov/[email protected]: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) 

Один подход, который я нашел в Интернете рассматривает некоторые утечки в спящем и области проверки, пояснил here и подробно here. Я собираюсь протестировать его, но я не знаю, действительно ли это решение для моей проблемы и (если это так), в этот момент лучше всего очистить GORM.

Или есть еще одна утечка памяти в моем коде? Идеи кто-нибудь?

EDIT: Насколько я знаю, исключение происходит в точке, где вызывается метод finder. Это означает, что GORM не может обрабатывать объем данных, возвращаемых базой данных, не так ли? Извините за то, что вы спрашиваете, как greenhorn, но я никогда не сталкивался с такой проблемой даже с очень большими наборами результатов.

+0

Сколько результатов вы ожидали бы вернуть этот запрос? –

+0

«a» вернет несколько тысяч результатов. – codeporn

+0

Xmx 128m звучит довольно мало для приложения grails, я определенно хотел бы поднять его, если это возможно. Кроме того, MaxPermSize довольно большой по сравнению с размером Xmx; это может стоить попробовать более низкий пермен с большим Xmx. – ataylor

ответ

4

Sun (эта ссылка недействительна больше) had documented это OutOfMemoryError следующим образом:

Параллельный/параллельный коллектор выбросит OutOfMemoryError если слишком много времени тратится на сбор мусора : если более 98% от общее время тратится на мусор и менее 2% от куча восстанавливается, OutOfMemoryError будет выброшен. Эта функция предназначена для предотвращения использования приложений от , работающих в течение длительного периода времени при незначительном прогрессе , потому что куча слишком мала. Если требуется , эта функция может быть отключена, добавив опцию -XX: -UseGCOverheadLimit в командную строку.

Иными словами, эта ошибка является функцией, подсказкой для увеличения доступной памяти (что не является предпочтительным вариантом в вашем случае, как вы уже упоминали). Некоторые разработчики consider эта функция не будет полезна в каждом прецеденте, поэтому проверьте ее.

+0

Вы правы, не больше OutOfMemoryException с этой опцией ... но очень медленный запрос :) Теперь я извлекаю результаты (например, предложенные Fletch) в куски, чтобы избежать исключений и поддерживать производительность до приемлемого уровня; – codeporn

2

Если вы не хотите увеличивать объем памяти, возможно, вам следует искать строки, превышающие определенную сумму. Я предполагаю, что это какая-то функция типа вперед/предложение; возможно, вы можете начать поиск, когда есть три символа или около того. В противном случае, возможно, постраничные результаты - это вариант?

К слову, в архитектуре контроллер предназначен для взаимодействия с внешним миром и его форматами, то есть вы, вероятно, захотите, чтобы ваша служба просто возвращала объекты и ваш контроллер для преобразования JSON. Но это не решит вашу текущую проблему.

+0

Хорошие идеи и хорошая точка зрения на архитектуру; Сейчас я делаю вид подкачки. Тип-вперед был бы идеальным, но строка запроса не поступает непосредственно из взаимодействия пользователя. – codeporn

3

Другим вариантом для уже предложенных было бы работать на страницах результатов. Вместо использования динамического искателя, используйте критерии и страницу через результаты самостоятельно. Вот наивный, псевдокод пример:

def offset = 0 
def max = 50 
while(stillMoreResults) { 
    def batch = SomeDomain.findAllBySomePropIlike("%${queryString}%", [max: max, offset: offset]) 
    appendBatchToJsonResult(batch) 
    offset += max 
} 

Вы можете настроить размер пакета в соответствии с вашими требованиями к памяти. Это позволит избежать необходимости настройки памяти.

Редактировать

Я просто перечитать ответ Флетч и заметил, что он говорил об этом как решение, и вы прокомментировали это. Я останусь здесь, потому что у него есть пример, но если Флетч добавит к нему пример подкачки, я удалю этот ответ, так как он упомянул об этом, прежде чем я это сделал.

+0

Не обязательно использовать критерий для получения пейджинга: вы можете передать max и offset в динамический искатель, например 'SomeDomain.findAllBySomePropIlike ("% $ {queryString}% ", [max: 50, offset: offset])' – ataylor

+0

@ataylor - Хорошая мысль, спасибо за головы. Я уточню свой ответ. –

1

Я бы также предложил вам вернуть свойства, которые вам нужны, с этим запросом типа запроса вперед и получить полный доменный объект с фактическими данными, которые требуются пользователю.

Создатель JSON собирается создавать много объектов и память для comsume. Например, для типа «вперед» для пользователей я бы возвращал только базовую информацию о имени и идентификатор вместо полного объекта

0

В приложении Grails с использованием базы данных MySQL (MySQL был установлен через Homebrew), я получил тот же самый проблема, как ни странно, только при запуске приложения, не запустив сначала сервер MySQL. Таким образом, просто запустив

mysql.server start

исправлена ​​проблема для меня.

Смежные вопросы