Grails堆空间间歇性地溢出

时间:2012-08-21 23:48:44

标签: grails

我有一个间歇性且难以复制的快乐问题之一。

经过几分钟或几天的操作后,我们的测试服务器上的grails应用程序失败。我无法在Dev或我的本地复制。

此处的堆栈跟踪模式示例将中间截断为1000的行(这将继续像这样继续转储,直到我重新启动Tomcat):

at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63)
    at grails.plugin.springcache.web.GrailsFragmentCachingFilter.doFilter(GrailsFragmentCachingFilter.groovy:66)
    at net.sf.ehcache.constructs.web.filter.Filter.doFilter(Filter.java:86)
    at net.bull.javamelody.JspWrapper.invoke(JspWrapper.java:117)
    at net.bull.javamelody.JdbcWrapper$DelegatingInvocationHandler.invoke(JdbcWrapper.java:231)
    at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:195)
Caused by: javax.servlet.ServletException: javax.servlet.ServletException: org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.OutOfMemoryError: Java heap space
    at net.sf.ehcache.constructs.web.filter.Filter.logThrowable(Filter.java:143)
    at net.sf.ehcache.constructs.web.filter.Filter.doFilter(Filter.java:91)
    at net.bull.javamelody.JspWrapper.invoke(JspWrapper.java:117)
    at net.bull.javamelody.JdbcWrapper$DelegatingInvocationHandler.invoke(JdbcWrapper.java:231)
    at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:195)
......
    at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:195)
    at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63)
    at grails.plugin.springcache.web.GrailsFragmentCachingFilter.doFilter(GrailsFragmentCachingFilter.groovy:66)
    at net.sf.ehcache.constructs.web.filter.Filter.doFilter(Filter.java:86)
Caused by: javax.servlet.ServletException: org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.OutOfMemoryError: Java heap space
    at grails.plugin.cache.web.filter.AbstractFilter.logThrowable(AbstractFilter.java:116)
    at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:70)
    at grails.plugin.springcache.web.GrailsFragmentCachingFilter.doFilter(GrailsFragmentCachingFilter.groovy:66)
    at net.sf.ehcache.constructs.web.filter.Filter.doFilter(Filter.java:86)
    at net.bull.javamelody.JspWrapper.invoke(JspWrapper.java:117)
    at net.bull.javamelody.JdbcWrapper$DelegatingInvocationHandler.invoke(JdbcWrapper.java:231)
    at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:195)
    at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63)
    at grails.plugin.springcache.web.GrailsFragmentCachingFilter.doFilter(GrailsFragmentCachingFilter.groovy:66)
    at net.sf.ehcache.constructs.web.filter.Filter.doFilter(Filter.java:86)

..................
    at net.sf.ehcache.constructs.web.filter.Filter.doFilter(Filter.java:86)
    at net.bull.javamelody.JspWrapper.invoke(JspWrapper.java:117)
    at net.bull.javamelody.JdbcWrapper$DelegatingInvocationHandler.invoke(JdbcWrapper.java:231)
    at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:195)
    at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63)
    at grails.plugin.springcache.web.GrailsFragmentCachingFilter.doFilter(GrailsFragmentCachingFilter.groovy:66)
    at net.sf.ehcache.constructs.web.filter.Filter.doFilter(Filter.java:86)
    at net.bull.javamelody.JspWrapper.invoke(JspWrapper.java:117)
    at net.bull.javamelody.JdbcWrapper$DelegatingInvocationHandler.invoke(JdbcWrapper.java:231)
    at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:195)
    at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63)
    at grails.plugin.springcache.web.GrailsFragmentCachingFilter.doFilter(GrailsFragmentCachingFilter.groovy:66)
    at net.sf.ehcache.constructs.web.filter.Filter.doFilter(Filter.java:86)
    at net.bull.javamelody.JspWrapper.invoke(JspWrapper.java:117)
    at net.bull.javamelody.JdbcWrapper$DelegatingInvocationHandler.invoke(JdbcWrapper.java:231)
    at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:195)
    at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63)
    at grails.plugin.springcache.web.GrailsFragmentCachingFilter.doFilter(GrailsFragmentCachingFilter.groovy:66)
    at net.sf.ehcache.constructs.web.filter.Filter.doFilter(Filter.java:86)
    at net.bull.javamelody.JspWrapper.invoke(JspWrapper.java:117)
    at net.bull.javamelody.JdbcWrapper$DelegatingInvocationHandler.invoke(JdbcWrapper.java:231)
    at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:195)
    at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63)
    at grails.plugin.springcache.web.GrailsFragmentCachingFilter.doFilter(GrailsFragmentCachingFilter.groovy:66)
    at net.sf.ehcache.constructs.web.filter.Filter.doFilter(Filter.java:86)
    at net.bull.javamelody.JspWrapper.invoke(JspWrapper.java:117)
    at net.bull.javamelody.JdbcWrapper$DelegatingInvocationHandler.invoke(JdbcWrapper.java:231)
    at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:195)
    at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63)
    at grails.plugin.springcache.web.GrailsFragmentCachingFilter.doFilter(GrailsFragmentCachingFilter.groovy:66)
    at net.sf.ehcache.constructs.web.filter.Filter.doFilter(Filter.java:86)
    at net.bull.javamelody.JspWrapper.invoke(JspWrapper.java:117)
    at net.bull.javamelody.JdbcWrapper$DelegatingInvocationHandler.invoke(JdbcWrapper.java:231)
    at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:195)
    at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63)
    at grails.plugin.springcache.web.GrailsFragmentCachingFilter.doFilter(GrailsFragmentCachingFilter.groovy:66)
    at net.sf.ehcache.constructs.web.filter.Filter.doFilter(Filter.java:86)
    at net.bull.javamelody.JspWrapper.invoke(JspWrapper.java:117)
    at net.bull.javamelody.JdbcWrapper$DelegatingInvocationHandler.invoke(JdbcWrapper.java:231)
    at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:195)
    at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63)
Caused by: org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.OutOfMemoryError: Java heap space
    at grails.plugin.cache.web.filter.PageFragmentCachingFilter.doFilter(PageFragmentCachingFilter.java:195)
    at grails.plugin.cache.web.filter.AbstractFilter.doFilter(AbstractFilter.java:63)
    ... 132 more
Caused by: java.lang.OutOfMemoryError: Java heap space

这看起来可能发生了应用程序错误,调用了errorHandler,它一直在调用自己... kaboom!

环境

  • Grails:2.0.4
  • Tomcat:apache-tomcat-7.0.28
  • 操作系统:Red Hat Enterprise Linux Server 5.8版(Tikanga)

爪哇

java version "1.6.0_22"
OpenJDK Runtime Environment (IcedTea6 1.10.8) (rhel-1.27.1.10.8.el5_8-x86_64)
OpenJDK 64-Bit Server VM (build 20.0-b11, mixed mode)

在Dev Java中(这可能是我试图让SA在测试系统中进行更改的主要问题)

java version "1.6.0_30"
Java(TM) SE Runtime Environment (build 1.6.0_30-b12)
Java HotSpot(TM) 64-Bit Server VM (build 20.5-b03, mixed mode)

服务器目前只有0-5个用户登录,但这种情况在24小时内消退。

分配的堆空间为5g,通常使用<20%。

在测试中还有2个奇怪的事情发生在开发中:

  • 日志,最初几乎没有记录但是由于问题已经打开了它,在50日开始下载DEBUG和RollingFile 100Meg文件。日志很好,记录应用程序特定的东西,直到Stack爆炸,然后它们中唯一的东西是堆栈,同样适用于tomcat日志记录。即使我在第50个日志滚动之前关闭服务器也是如此。所以我不知道是否有一些应用程序特定的事情会触发这个。

  • JavaMelody插件:图表上的标签是不可读的,就像我3岁的时候写的那样。我相信所有的OS字体都已加载但可以指向Java版本。

调度程序: 是的,有2个正在运行,已经运行它们没有问题

数据库配置: 传统数据库有8个数据源,例如,每个数据源都可能有点饥饿,例如:

def connectionPropertiesMedium = [
        maxActive: 100,
        maxIdle: 30,
        minIdle: 5,
        initialSize: 30,
        testOnBorrow: true,
        testWhileIdle: false,
        testOnReturn: false,
        validationQuery: "SELECT 1",
        minEvictableIdleTimeMillis: 600000,
        timeBetweenEvictionRunsMillis: 600000,
        numTestsPerEvictionRun: 3,
        maxWait: 10000,
        defaultTransactionIsolation: java.sql.Connection.TRANSACTION_READ_UNCOMMITTED
]

HEAPDUMP: 使用-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heapdump.hprof运行 使用VisualVM进行阅读时,这显示很少(对我而言)70%+是char [],上面是堆栈跟踪。 Eclipse MAT不会加载它。

JVM args:

CATALINA_OPTS="-server -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heapdump.hprof -XX:MaxPermSize=1024m -XX:MaxNewSize=256m -XX:NewSize=256m -Xms768m -Xmx1024m -XX:SurvivorRatio=128 -XX:MaxTenuringThreshold=0 -XX:+UseTLAB -XX:+UseConcMarkSweepGC -XX:+CMSClassUnloadingEnabled -XX:+CMSIncrementalMode -XX:-UseGCOverheadLimit -XX:+ExplicitGCInvokesConcurrent

压力测试: 应用程序可以运行一些激烈的查询,这些被多达200个模拟JMeter用户点击几次而没有问题...实际上数据库确实有点变慢;但是内存问题没有被复制很多次。

所以,如果有人读到这里,我有2条线索(开放更多):

  • Java版本 - 旨在脱离OpenJDK并转向标准版本
  • 我的Grails设置中的某些内容在发生错误时导致无限/递归处理。

直到我发现质子包能够在机器上点击这些鬼魂这个应用程序才会上线。

有什么想法吗?

干杯...

更新#1

安装了Java 1.7并删除了OpenJDK,这肯定修复了JavaMelody显示问题。现在它是一个等待游戏,看看这是否解决了主要问题。有趣的是,该网站似乎明显更快。

1 个答案:

答案 0 :(得分:1)

如果用户多次重新提交登录表单(至j_spring_security_check),则导致在自定义UserDetailsS​​ervice中调用User.save()(例如更新上次登录时间),Hibernate会话在每个用户的后续调用中关闭

将User.save()移动到AuthenticationEventListener.onApplicationEvent(AuthenticationSuccessEvent事件),它运行正常。

基本设置来自Spring security core plugin - events的文档,我的是:

在您的配置中添加:grails.plugin.springsecurity.useSecurityEventListener = true

在resources.groovy:

import security.AuthenticationEventListener
....
authenticationEventListener(AuthenticationEventListener)

AuthenticationEventListener.groovy:

class AuthenticationEventListener implements ApplicationListener<AuthenticationSuccessEvent> {

    void onApplicationEvent(AuthenticationSuccessEvent event) {
        if (event instanceof AuthenticationSuccessEvent) {
            UserDetails userDetails = (UserDetails) event.getAuthentication().getPrincipal()
            def httpSession = SecurityRequestHolder.request.session

            if (!httpSession.loggedIn) {
                httpSession.loggedIn = true
                synchronized (httpSession.loggedIn) {
                    httpSession.timeZone = userDetails.timeZone
                    User.withSession { session ->
                        if (session.isOpen()) {
                            User user = User.findByUsername(userDetails.username, [fetch: [roles: 'eager']])
                            user.lastLoggedIn = new Date()
                            user.save(flush: true)
                        }
                    }
                }
            }
        }
    }
}