【问题标题】:Grails Heap space overflowing intermittentlyGrails 堆空间间歇性溢出
【发布时间】:2012-08-21 23:48:43
【问题描述】:

我遇到了一个间歇性且难以复制的快乐问题。

运行几分钟或几天后,我的测试服务器上的 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

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,通常使用

在测试中发生了另外两件奇怪的事情,而不是在开发中:

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

  • JavaMelody 插件:图表上的标签不可读,就像我 3 岁的孩子写的那样。我相信所有操作系统字体都已加载,但可能指向 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
]

堆转储:-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heapdump.hprof一起运行 用 VisualVM 阅读这几乎没有(无论如何对我来说) 70%+ 是 char[] 充满了上面的堆栈跟踪。 Eclipse MAT 不会加载它。

JVM 参数:

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 显示问题。现在它正在等待游戏,看看这是否解决了主要问题。有趣的是,该网站似乎明显更快。

【问题讨论】:

  • 您是否尝试过删除 grails.plugin.cache.web.filter.PageFragmentCachingFilter 并且可以使用 2.1 重新创建?我知道他们已经调整了缓存。否则你能附上探查器吗?
  • @chrislovecnm 谢谢,我没有尝试过。我注意到 2.1 中的缓存更改,但认为它不足以升级,我会再次检查。您想到了什么分析器以及如何处理它?我正在运行 VisualVM,但它经常在我睡觉时轰炸!

标签: grails


【解决方案1】:

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

将 User.save() 移至 AuthenticationEventListener.onApplicationEvent(AuthenticationSuccessEvent event) 并且工作正常。

基本设置来自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)
                        }
                    }
                }
            }
        }
    }
}

【讨论】:

  • 这个类AuthenticationEventListener是一个自定义类,扩展了一些抽象grails事件监听器;你必须在 resources.groovy 中声明它吗?现在有这个问题,所以我真的可以使用一些帮助。
  • 另外,我是否必须添加 spring events 插件才能使这种方法起作用?
  • 更新答案以帮助您。这仅适用于 Grails 2.3.1 和 SS 插件 spring-security-core:2.0-RC2。 HTH
  • 谢谢。由于我有一个多租户解决方案,因此在进行身份验证事件时,我总是必须考虑租户。这导致了我的部分问题。我还没有升级到 2.0 SS 插件,但我什么时候升级它肯定会派上用场。
猜你喜欢
  • 2012-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-21
  • 1970-01-01
  • 2012-02-06
  • 2012-08-09
  • 1970-01-01
相关资源
最近更新 更多