永久代空间是否会被减少?

12

我想知道JVM是否通常会卸载类以减少PermGen空间。以下是我的问题:

  1. JVM默认会卸载Java类吗?
  2. 关闭Jar类加载器是否会卸载该jar中的所有已加载类?
  3. 应使用哪些命令/方法来允许卸载类?

顺便提一下,我尝试过一些网络上的解决方案,但没有一个能回答我的问题。(例如:JVM标志CMSClassUnloadingEnabled实际上是做什么的?)

PS:我是指Java 6+hibernate(类加载由hibernate处理)


你的应用是什么类型的? - Thilo
它会随着JVM版本和设置而变化。要卸载一个类,您必须至少删除对类加载器的所有引用,并显然删除对类本身和任何实例以及由于堆栈调用帧而产生的任何引用。更微妙的是,您必须删除引用已删除类的任何类的引用。 (这意味着您不能使用例如new SomeClass在“主线”代码中引用的类来删除类 - 您必须从其名称作为字符串开始加载类。) - Hot Licks
@Thilo 我的应用程序由一个JAR包提供的动态加载类组成。用户可以在运行时更改此JAR包,但是在一些JAR包更改后,应用程序会出现“OutOfMemoryError: PermGen space”错误,因为旧类(来自被忽略的JAR包)从未被卸载。 - Adel Boutros
4个回答

3
唯一的摆脱已加载类的方法是允许JVM通过消除对该类加载器的所有引用来GC加载这些类的类加载器。 这就是在Web服务器中取消部署Web应用程序时发生的情况。

但是在Java 6中的问题是URLClassLoader从未真正取消引用Jar,因此类永远不会被卸载,对吗? - Adel Boutros
请检查我在问题中的个人陈述。 - Adel Boutros
你如何在运行时更改jar文件?在卸载旧类加载器及其所有类之前,必须先将它们清除。 - Thilo
@AdelBoutros 如果URLClassLoader仍然引用这些类,是可以的。如果对这些类的唯一引用来自它们的类加载器,并且没有对类加载器的引用,那么这就形成了一个“孤岛”,垃圾收集器可以很好地处理。 - Brandon
据我所知,Hibernate在内部处理类加载。 - Adel Boutros
这意味着可能是Hibernate没有对类进行解引用,对吗? - Adel Boutros

3

默认情况下,永久代正在被收集。

永久代由一个空间组成,但没有自己的收集器。相反,该空间作为老年代的一部分进行收集。

根据这个可视化GC页面

在旧的JVM中,您可以使用-XX:+CMSPermGenSweepingEnabled标志启用此功能。

据我所知,当类未从任何ClassLoader引用时,它们会被卸载。

java.lang.OutOfMemoryError: PermGen space错误是由于类加载器中的内存泄漏导致的。在取消部署/重新部署Web应用程序时经常出现此问题。


2
我假设您正在谈论HotSpot,即Oracle/Sun JVM。
  1. 如果没有对它们的引用。请注意,所有类都有对其类加载器的引用,反之亦然,所有对象都有对其类的引用。
  2. 也许。您所说的“close”是什么意思?
  3. 使用选项-XX:+UseConcMarkSweepGC-XX:+CMSClassUnloadingEnabled启动JVM。这将允许卸载未使用的类,即使它们的类加载器仍然存在。

另请参阅Java中的类卸载?


谢谢您的答复。在我的情况下,我必须动态地附加一个 Jar 文件来加载新类。随着时间的推移,我遇到了 OutOfMemoryError: PermGen space 错误,并想知道为什么旧 Jar 文件中的类一旦被丢弃就不会被卸载。 - Adel Boutros

2
Hibernate在SessionFactory启动时会加载所有的Entity类到内存中。然而,Hibernatelazy的方式加载实体实例,只有在需要时才会加载。
JVM需要内存时,类可以被GC卸载。实体实例也可以被垃圾回收,但是和其他对象一样,只有当没有指向它们的引用时才会被回收。换句话说,如果Hibernate会话仍然处于打开状态,就无法卸载Entity实例。要卸载它们,您需要清除持久性上下文或关闭会话。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接