我们正在运行一个在Tomcat上运行的Rails上写的JRuby的小型Web应用程序.我们正在使用与另一个生产Web应用程序共享的Spring后端.不幸的是,我们一直遇到PermGen问题.
操作系统:Ubuntu Linux 2.6.24-24-server#1 SMP x86_64 GNU/Linux Java:1.6.0_21 Tomcat:6.0.28 JRuby:1.5.0 Rails:2.3.7
我们目前正在被谷歌,雅虎和百度抓获,因此网站使用率上升.我一直在使用JConsole监视Tomcat,我们肯定会看到有太多类的问题.当tomcat启动时,我们加载了大约12,000个类.8小时后,我们加载了近75,000个班级.PermGen同时从100MB增加到460MB.
类卸载工作正常,但它只在同一个8小时内卸载了~500个类.PermGen似乎永远不会被收集.
我们正在运行Tomcat的以下VM选项:
-Xms2048m -Xmx2048m -XX:MaxPermSize=512m -XX:PermSize=128m \
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:ParallelGCThreads=4 \
-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled
Run Code Online (Sandbox Code Playgroud)
显然存在某种泄漏.问题是在哪里?关于如何追踪谁以及对此负责的任何建议?我希望这是我们的一些非常愚蠢的错误,但我不知道从哪里开始.
任何建议将不胜感激.
编辑
看起来我们正在看到为每个传入请求创建一个新类.
编辑2
这肯定与JRuby有关.使用JConsole,我为类加载器启用了详细模式.以下是来自catalina.out的示例:
[Loaded anon_class1275113147_895127379 from file:/opt/apache-tomcat-6.0.28/webapps/notes/WEB-INF/lib/jruby-core-1.5.0.jar]
[Loaded anon_class1354333392_895127376 from file:/opt/apache-tomcat-6.0.28/webapps/notes/WEB-INF/lib/jruby-core-1.5.0.jar]
[Loaded anon_class1402528430_895127373 from file:/opt/apache-tomcat-6.0.28/webapps/notes/WEB-INF/lib/jruby-core-1.5.0.jar]
Run Code Online (Sandbox Code Playgroud)
那么问题就变成了如何追踪负责创建这些额外课程的一方?
编辑3
不确定这是否是问题所在,但不知何故,我们最终会遇到疯狂的类加载器.跑jmap -permstat PID了但得到了:
class_loader classes bytes parent_loader alive? type
total = 1320 135748 947431296 N/A alive=1, dead=1319 N/A
Run Code Online (Sandbox Code Playgroud)
这看起来有点过分了.大多数有三种类型的类加载器的一个:sun.reflect.DelegatingClassLoader,org.jruby.util.JRubyClassLoader或org.jruby.util.ClassCache$OneShotClassLoader.再次,样本输出来自jmap -permstat:
class_loader classes bytes parent_loader alive? …Run Code Online (Sandbox Code Playgroud) 这是Sun JDK 1.6u21,x64.
我有一个类用于试验perm gen用法,它只包含一个大字符串(512k字符):
public class Big0 {
public String bigString =
"A string with 2^19 characters, should be 1 MB in size";
}
Run Code Online (Sandbox Code Playgroud)
我检查使用烫发根的使用getUsage().toString()上MemoryPoolMXBean为永久生成(在21岁以下称为"PS烫发根"对象,尽管它有不同的版本,或者与不同的垃圾收集器略有不同的名称.
当我第一次引用类时,通过读取来说Big0.class,perm gen跳过~500 KB - 这就是我所期望的,因为字符串的常量池编码是UTF-8,而我只使用ASCII字符.
然而,当我实际创建这个类的实例时,perm gen会跳跃大约2 MB.由于这是1 MB内存中的字符串(每个UTF16字符2个字节,当然没有代理),我很困惑为什么内存使用量是双倍的.
如果我将字符串设为静态,则会产生相同的效果.如果我用最后的,它不能作为编译我超过65535个字节常量池项的上限(不知道为什么最后留下关闭避免了要么 - 认为这是一个加分题).
有任何见解赞赏!
编辑:我还应该指出,这发生在非静态,最终的非静态和静态字符串中,但不适用于最终的静态字符串.由于这已经是字符串常量的最佳实践,也许这主要是学术兴趣.
我们一直在推动我们应用程序中的permgen内存空间越来越高,我试图找出我们是否有某种类型的泄漏进入permgen区域.我们不做热取消部署/重新部署操作,但我们有很多代理,包括动态和CGLIB生成.我们还做了一些复杂的类加载器位来支持各种用例,我怀疑这些也可能是导致permgen浪费的一个原因.
所以我在运行的应用程序上运行jmap -permstat,希望能够深入了解可能填补我们permgen空间的内容.(我还运行带有实时和死对象的正常堆转储,这样我就可以追溯可能来自permstat输出的线索).
但是,在jmap permstat列出的2400个类加载器中,除引导类加载器之外的所有类都被列为"死".这没有任何意义,因为该应用程序绝对是直播和工作.
我的理解是,如果有资格进行垃圾收集,jmap会将类加载器报告为"死"但我在这里肯定是错的......
我错过了什么?"死"在这里意味着什么?除了我在这里可能存在的误解之外,谷歌搜索并没有提供很多答案.
第一次应用程序正确启动.然后我删除webapp/*.war文件并粘贴*.war的新版本.Jetty开始部署新战争但java.lang.OutOfMemoryError: PermGen space发生错误.如何配置Jetty以修复错误/进行正确的重新部署?
这个解决方案对我没有帮助.
Jetty版本:jetty-7.4.3.v20110701
Scala类库中使用的标准模式是类和特征中的类的定义.父类对象的大多数操作都会导致创建这些内部类的对象.每个对象的每个内部类都不同.
例如,请参阅scala.io.Source和LineIterator的source.我认为这是标准库中最简单的一个.
正如文件所示,下面是两个不同的类.
val s1:Source = ...
val s2:Source = ...
s1.getLines.getClass != s2.getLines.getClass //true if s1 != s2
Run Code Online (Sandbox Code Playgroud)
意味着创建了两个类.
由于整个集合库使用相同的模式,对于长时间运行的进程,permgen空间有什么影响?
通过"发布",我的意思是没有对类加载器的引用.
我们遇到了一个问题,即频繁重新部署的Java EE应用程序会占用permgen空间.分析表明,Java EE应用程序中的单例已经传递了对应用程序之外的应用程序类加载器对象的引用(违反Java EE规则),并且在取消部署应用程序时不会清除它们.
假设单例或类对象没有其他引用,那么在释放类的类加载器时是否会调用单例的finalize()? 我想在那里清除流氓入境参考.或者我是在catch-22中,在类加载器本身可以被垃圾收集之前不会调用finalize - 因此由于流氓外部引用而永远不会被调用?
这里的主要问题可能是:
在这种情况下,类对象是否会被垃圾收集? 这可能取决于类加载器行为的规范,或者可能依赖于实现.
参考文献(另一种!;-))将不胜感激,但不是必需的.
我的理解是PermGen(在某种意义上)将类代码保存在内存中.通常我们有很多jar文件引用了我们的类路径.当一个jar文件包含在类路径中时(例如在tomcat的lib目录中),所有这些jar的所有类都自动加载到PermGen中吗?
在类似的问题中,一旦使用了一个jar文件类,PermGen是否会加载该jar文件中的所有类,或者仅加载所使用的类(然后在必要时加载其余的类文件)?
我在Ubuntu 11.10下使用Struts2 + Spring和Hibernate框架在NetBeans中创建了一个项目.第一次运行是好的,但是当我第二次或第三次运行它时,我一直得到这个例外.没有Maven一切顺利.我安装了Maven的apt-get install,是的,我加入这一行export MAVEN_OPTS=-Xmx512m的usr/bin/mvn,但没有运气.如何从中获得更好的表现?
我正在尝试生成类并在运行时加载它们.
我正在使用一个ClassLoader对象来加载类.因为我不想耗尽PermGen内存,所以我不时引用类加载器并创建一个新的加载要使用的新类.这似乎工作正常,我不会PermGen失去记忆.问题是,当我这样做时,一段时间后我得到以下错误:
java.lang.OutOfMemoryError: GC overhead limit exceeded
Run Code Online (Sandbox Code Playgroud)
所以我的问题是,什么时候我应该取消引用类加载器以避免错误?:
我应该在我的代码中监视PermGen用法,以便在取消使用接近限制System.gc()时取消引用类加载器并调用PermGen吗?
或者我应该采用不同的方法?
谢谢
今天我收到PermGen OutOfMemory错误.
显示,分析最近的GC根为WebappClassLoader是的logback螺纹:
this - value: org.apache.catalina.loader.WebappClassLoader #4
<- contextClassLoader (thread object) - class: java.lang.Thread, value: org.apache.catalina.loader.WebappClassLoader #4
Run Code Online (Sandbox Code Playgroud)
这是:
java.lang.Thread#11 - logback-1
Run Code Online (Sandbox Code Playgroud)
来自此线程的堆转储的线程转储:
"logback-1" daemon prio=5 tid=34 WAITING
at sun.misc.Unsafe.park(Native Method)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:458)
at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:359)
Local Variable: java.util.concurrent.SynchronousQueue$TransferStack$SNode#1
Local Variable: java.util.concurrent.SynchronousQueue$TransferStack#6
at java.util.concurrent.SynchronousQueue.take(SynchronousQueue.java:925)
Local Variable: java.util.concurrent.SynchronousQueue#6
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
Local Variable: java.util.concurrent.ThreadPoolExecutor#34
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
Local Variable: java.util.concurrent.ThreadPoolExecutor$Worker#11
at java.lang.Thread.run(Thread.java:745)
Run Code Online (Sandbox Code Playgroud)
我使用具有热重新部署功能的Tomcat 8 reloadable="true"并CLASSPATH通过PreResources以下方式外部化:
<Context docBase="/home/user/devel/app/src/main/webapp"
reloadable="true">
<Resources> …Run Code Online (Sandbox Code Playgroud)