使用Quartz调试Groovy/Grails应用程序中的堆空间问题

使用Quartz调试Groovy/Grails应用程序中的堆空间问题,grails,groovy,heap,quartz-scheduler,Grails,Groovy,Heap,Quartz Scheduler,我在Groovy/Grails中创建了一个小应用程序,它使用Quartz每10秒执行一个小作业。现在我遇到的问题是,在运行数小时后,应用程序崩溃,出现org.quartz.JobExecutionException:java.lang.OutOfMemoryError:java堆空间[请参阅嵌套异常:java.lang.OutOfMemoryError:java堆空间] 现在,我正在尝试使用查找该问题的原因。通过查找“问题嫌疑犯”,分析仪显示以下结果: Problem Suspect 1 3,

我在Groovy/Grails中创建了一个小应用程序,它使用Quartz每10秒执行一个小作业。现在我遇到的问题是,在运行数小时后,应用程序崩溃,出现
org.quartz.JobExecutionException:java.lang.OutOfMemoryError:java堆空间[请参阅嵌套异常:java.lang.OutOfMemoryError:java堆空间]

现在,我正在尝试使用查找该问题的原因。通过查找“问题嫌疑犯”,分析仪显示以下结果:

Problem Suspect 1

3,926 instances of "groovy.lang.ExpandoMetaClass",
loaded by "org.codehaus.groovy.grails.cli.support.GrailsRootLoader @ 0x122e88b98" 
occupy 95,746,168 (33.69%) bytes. 

Keywords
org.codehaus.groovy.grails.cli.support.GrailsRootLoader @ 0x122e88b98
groovy.lang.ExpandoMetaClass

--    

Problem Suspect 2

1,010 instances of "com.mongodb.DBApiLayer",
loaded by "org.codehaus.groovy.grails.cli.support.GrailsRootLoader @ 0x122e88b98" 
occupy 56,522,416 (19.89%) bytes.
These instances are referenced from one instance of
"org.codehaus.groovy.util.AbstractConcurrentMapBase$Segment[]", loaded by 
"org.codehaus.groovy.grails.cli.support.GrailsRootLoader @ 0x122e88b98"

Keywords
org.codehaus.groovy.grails.cli.support.GrailsRootLoader @ 0x122e88b98
org.codehaus.groovy.util.AbstractConcurrentMapBase$Segment[]
com.mongodb.DBApiLayer
Groovy(和Grails)应用程序中有这么多的
ExpandoMetaClass
实例是正常的还是我介绍的问题

关于MongoDB:应用程序使用GORM并直接使用Gmongo从DB读取和写入许多小项目。但是,我已经检查了所有连接,并且它们在一段时间后正确关闭。活动线程的大约数量为40个。所以我认为DB层不应该是问题所在。尽管如此,它还是占据了堆的很大一部分。有什么想法吗


有什么建议吗?

这实际上可能是GMongo司机的问题。在这种情况下,具有相似环境的用户会遇到非常相似的问题

Gmongo是grailsmongo插件()使用的驱动程序

如果可以,尝试使用MongoDB驱动程序本身,而不是它所依赖的Gmongo或Grails插件

作为一种解决方法,并且作为隔离问题的一种手段,您可以尝试增加堆大小;如果您当前正在人为地缩小堆大小,这尤其是一个好主意

export GRAILS_OPTS="-Xmx1G -Xms256m -XX:MaxPermSize=256m"
grails run-app

如果内存消耗在某一点稳定下来,您可能会停止出现内存不足错误。否则,增加堆大小只会延迟不可避免的事件。跟踪相对于堆大小的崩溃时间将非常有助于向gmongo开发团队报告。

User@jonnybot认为问题可能是由gmongo引起的,事实上他是对的。我开始与GMongo的创建者进行讨论,在讨论中我创建了一个小应用程序,该应用程序每秒向MongoDB集合中插入一些内容,以便能够复制问题。该应用程序显示了较大的内存泄漏:

class MemoryJob {
    def concurrent = false
    static triggers = {
        simple startDelay: 5000, repeatInterval: 1000
    }
    def execute() {
        def mongoUrl = "mongodb://localhost:27017"
        def mongo = new GMongo(new MongoURI(mongoUrl))
        def db = mongo.getDB("memory")
        println new Date()
        db.getCollection("test").insert(['date':new Date()])
        mongo.close()
    }
}
然后,GMongo的作者建议重用DB连接,而不是在每个请求上创建新的连接。例如:

class MemoryJob {
    def concurrent = false
    static triggers = {
        simple startDelay: 5000, repeatInterval: 200
    }
    static mongoUrl = "mongodb://localhost:27017"
    static mongo = new GMongo(new MongoURI(mongoUrl))
    static db = mongo.getDB("memory");
    def execute() {
        println new Date()
        db.getCollection("test").insert(['date':new Date()])
    }
}
事实上,这解决了我所遭受的内存泄漏问题

结论是:不要在每个请求上创建MongoDB连接,而是重用单个连接。尽管GMongo中似乎存在内存泄漏,但可以通过重用连接来避免


希望这能帮助某人节省一些时间。

实际上我自己也在使用Gmongo,中间没有使用GORM(这也被添加到问题中)。谢谢你的链接,我将对此进行调查,然而,这个问题似乎从未真正得到解决。我现在正在考虑直接使用官方的MongoDB驱动程序来替换我的Gmongo代码。啊,堆空间设置为2GB。重新启动后,应用程序只使用了大约400MB的内存,所以这根本不应该是个问题。看起来这是有相同问题的人必须做的。我能够解决问题并将其转化为答案:)