前几日,碰见一个奇怪的现象,连续的文件上传操作,会导致JVM内存溢出,而且是java.lang.OutOfMemoryError: PermGen space,内存的永久保存区域溢出。最终,导致整个Apusic应用服务器宕掉。
对于Permanent Generation space,JVM在运行期是不会进行垃圾清理的,这块内存溢出,一般主要是因为加载的类太多了,超出了JVM的默认值,或者设定的值。一般的解决方案是加大permanent generation space的大小。
但是,在这里将MaxPermSize由原来的128M增加到256M,照样在上传一些文件之后出现同样的问题,估计这块空间再怎么增大,溢出都是迟早的问题。应用在启动的时候,一切正常,说明加载应用的class文件是不成问题的。增加maxPermSize对问题的解决没有意义。
但是,文件上传的操作,怎么会引起permGen space的溢出呢?想不明白?
还是来监控一下应用的日志吧。
整个应用运行期间一切都很正常,debug出来的持久层框架生成的SQL语句都非常正常,只是在某些时间点,并没有规律的时间点总是出现以下的信息:
不明白为什么应用总要重新启动?
看到这里突然有些眉目了,会不会是频繁的重新启动应用,而每次重新启动的时候,Permanent Generation space总是会重新加载一遍所有的class文件,而JVM并没有对permanent generation space的垃圾收集,最终导致这块内存溢出了!
这只是猜测,找证据!终于,在经过几次频繁的文件上传的操作之后,久违的异常在万众瞩目中出现了!伴随着异常的是,每上传一个文件,整个session就会丢失。
看起来是在又一次重新启动应用的时候发生异常了,永久代内存溢出了!足以证明永久代的溢出,跟应用的不断重启有关,而Apuisc的宕机跟应用的不断自动重启有直接关系。问题找到了!但是,为什么应用会不断的自动重启呢?在确认代码没问题以后(我相信任何一个负责任的编程人员都会不动不动就重启应用玩的,而且JavaEE的规范从安全性上来考虑,也不会允许从应用中来重启应用的),更多开始转向对Apusic的分析。什么情况下,Apusic会自动重启应用?
应用中的文件发生变化?用户会不会直接把文件上传到当前应用目录下,而当前应用又放在Apusic默认的应用目录下,这样一旦默认应用下的某些文件发生变化,无论是文件的增加、修改还是删除,Apusic都会产生相应的措施,来保证变化之后的文件能够及时加载。
在与总部沟通之后(在这里要感谢总部李伟斌、韦永森等同事的大力支持,才使问题最终解决),最终确定问题就在于此,将上传目录从应用中迁移出来之后,一切正常!
总之,如果上传文件存放的位置如果正好在当前应用的某个目录下,而且这个应用又放在了Apusic默认的应用目录下,那很不巧,每上传一次文件,Apusic都会自动重新启动一下应用,以保证文件的及时加载。
因此,建议在进行涉及到文件上传的操作时,文件如果不是转换成二进制流存放在数据库中,那么最好将文件存放的问题放在应用之外,至少与应用平行的目录是可以做到的,甚至可以指定一个绝对路径,这样对于文件的管理也未尝不是一件好事。