JVM标志CMSClassUnloadingEnabled实际做什么?


183

CMSClassUnloadingEnabled除了一些非常模糊的高级定义(例如“摆脱PermGen问题”(不是,btw))之外,我一生无法找到Java VM标志实际作用的定义。

我查看了Sun / Oracle的站点,甚至选项列表都没有真正说明它的作用。

基于该标志的名称,我猜测CMS垃圾收集器默认情况下不会卸载类,并且该标志将其打开-但我不确定。

Answers:


219

更新此答案与Java 5-7有关,Java 8已修复:https: //blogs.oracle.com/poonam/about-g1-garbage-collector,-permanent-generation-and-metaspace Kudos转到mt。乌鲁

对于Java 5-7:

世界上标准的Oracle / Sun VM外观是:类永远存在。因此,一旦加载,即使没有人在乎,它们仍保留在内存中。这通常是没有问题的,因为您没有那么多纯粹的“设置”类(=一次用于安装,然后再也不用)。因此,即使他们占用了1MB的内存,谁也会在乎。

但是最近,我们有了诸如Groovy之类的语言,它们在运行时定义了类。每次运行脚本时,都会创建一个(或多个)新类,并将它们永久保留在PermGen中。如果您正在运行服务器,则意味着内存泄漏。

如果启用CMSClassUnloadingEnabled,GC也会扫描PermGen,并删除不再使用的类。

[编辑]您还必须启用UseConcMarkSweepGC(感谢Sam Hasler)。看到这个答案:https : //stackoverflow.com/a/372​​0052/2541


17
stackoverflow.com/a/372​​0052/2541CMSClassUnloadingEnabled有任何影响,UseConcMarkSweepGC也必须设置
山姆·哈斯勒

1
不确定这对使用UseConcatSweepGC的想法有何影响,但看来CMSClassUnloadingEnabled中最近修复了一个错误。此处已标记
Bill Rosmus

3
@Kevin:是的,肯定。请参阅groovy.codehaus.org/Running的底部:“ Groovy动态创建类,但是默认的Java VM不对PermGen进行GC。如果使用的是Java 6或更高版本,则需要添加-XX:+CMSClassUnloadingEnabled -XX:+UseConcMarkSweepGCUseConcMarkSweepGC来启用CMSClassUnloadingEnabled。”
亚伦·迪古拉

1
关于将UseConcMarkSweepGC和CMSClassUnloadingEnabled一起使用的好文章。blog.redfin.com/devblog/2012/06/…–
维克多

1
不再适用于1.8:blogs.oracle.com/poonam/…–
mt.uulu

35

根据博客文章Java JVM的-XX选项的最完整列表,它确定是否在CMS垃圾收集器下启用了类卸载。默认值为false。有一个叫另一种选择ClassUnloadingtrue在默认情况下它(大概)影响其他垃圾收集器。

这个想法是,如果GC检测到先前加载的类不再在JVM中的任何地方使用,它可以回收用于保存类字节码和/或本机代码的内存。

如果当前正在使用CMS收集器,则设置CMSClassUnloadingEnabled 可能有助于解决permgen问题。但是可能是您没有使用CMS,或者您有真正的类加载器相关的内存泄漏。在后一种情况下,您的类将永远不会在GC中显示出来,因此也不会被卸载。


亚伦·迪古拉(Aaron Digulla)说:“课堂永远存在”。即使在纯Java世界中,也不是完全正确的。实际上,类的生命周期与其类加载器相关联。因此,如果您可以安排对类加载器进行垃圾收集(这并不总是一件容易的事),那么它加载的类也将被垃圾收集。

实际上,这是在对Web应用程序进行热重新部署时发生的情况。(或者至少,如果您可以避免导致电导存储泄漏的问题,那就应该这样做。)


24

一个有用的示例:

-XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled在我们的Weblogic 10.3 JVM上进行设置有助于解决以下问题:JAX-WS实现为每个Web服务调用创建了一个新的代理类,最终导致内存不足错误。

追踪并非易事。以下代码始终返回相同的代理类port

final MyPortType port = 
Service.create(
        getClass().getResource("/path/to.wsdl"), 
        new QName("http://www.example.com", "MyService"))
    .getPort(
        new QName("http://www.example.com", "MyPortType"), 
        MyPortType.class);

在内部,此代理委托给的实例weblogic.wsee.jaxws.spi.ClientInstance,该实例又委托给一个新$Proxy[nnnn]类,该类n在每次调用时都会增加。当添加标志时,n仍会增加,但是至少那些临时类已从内存中删除。

总的来说,当通过以下方式大量使用Java反射和代理时,这可能非常有用 java.lang.reflect.Proxy


+1分享真实经验。我们在扭矩盒上也遇到了这个问题,由于JRuby编译过程,服务器生成了大量的类。
nurettin

7
还请注意,-XX:+CMSPermGenSweepingEnabled赞成弃用-XX:+CMSClassUnloadingEnabled
nurettin

3
但是,解决此问题的真正方法是一次创建端口并重新使用它。这就是应该使用JAX-WS的方式。该端口也是100%线程安全的。
rustyx

@rustyx:可以通过一些权威链接支持该主张吗?我一直非常警惕重复使用Web服务代理和存根...例如,请参阅Apache CXF免责声明。或这个问题
Lukas Eder 2014年

1
@rukavitsya:正如我在回答中所说。每次我调用上述逻辑时,都会创建一个新的代理
Lukas Eder
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.