共享公共库能否解决java.lang.OutOfMemoryError: Metaspace问题?
好问题!结合WildFly的类加载机制,咱们一步步拆解你的疑问:
1. 创建公共依赖模块会不会因类加载隔离无效?
完全不会!WildFly采用的是模块化类加载体系,和传统Java EE容器的类加载逻辑不一样。你创建的公共模块是一个独立的、可被其他部署单元(比如WAR)显式依赖的模块,只要每个WAR正确声明对这个公共模块的依赖,类加载器就会优先从公共模块加载共享类,而不是从各自WAR的WEB-INF/lib里加载。这种方式不仅不会被隔离,反而能精准实现类的共享,从根源上避免重复加载相同的类。
2. 独立WAR的类加载器能否从公共模块获取依赖?
当然可以!WildFly中每个WAR会有自己的ModuleClassLoader,但这个类加载器遵循模块依赖委托规则:当需要加载一个类时,它会先委托给它依赖的模块的类加载器去加载。也就是说,只要你在WAR的jboss-deployment-structure.xml里声明了对公共模块的依赖,这个WAR的类加载器就会去公共模块的类加载器里找共享类,而不是自己加载一份。这样所有WAR共享同一套公共类的实例,Metaspace的占用会大幅降低,直接解决多WAR加载时的类重复问题。
举个简单的配置例子:
- 公共模块的
module.xml(放在$WILDFLY_HOME/modules/system/layers/base/com/your/common/main目录下):
<?xml version="1.0" encoding="UTF-8"?> <module xmlns="urn:jboss:module:1.3" name="com.your.common"> <resources> <resource-root path="common-dependency-1.jar"/> <resource-root path="common-dependency-2.jar"/> <!-- 所有公共依赖的jar都放在这里 --> </resources> <dependencies> <!-- 模块自身需要的依赖,比如javax.api等 --> <module name="javax.api"/> </dependencies> </module>
- 每个WAR的
WEB-INF/jboss-deployment-structure.xml:
<?xml version="1.0" encoding="UTF-8"?> <jboss-deployment-structure> <deployment> <dependencies> <module name="com.your.common" export="true"/> </dependencies> </deployment> </jboss-deployment-structure>
3. 调高MaxMetaspace是不是只能延缓问题?Metaspace不会GC吗?
这里要纠正一个常见误区:Metaspace是会被垃圾回收的!它和Java 8之前的永久代(PermGen)不同,Metaspace使用的是本地内存,当类加载器被回收时,它加载的类对应的Metaspace空间也会被释放。但问题在于,如果你每个WAR都加载自己的一套公共类,这些类会被各自的WAR类加载器持有,只要WAR处于部署状态,这些类加载器就不会被回收,对应的Metaspace也无法释放。
所以调高MaxMetaspaceSize确实只能延缓问题——当WAR数量持续增长时,重复加载的类会越来越多,Metaspace最终还是会被耗尽。只有通过共享类加载(比如公共模块),让多个WAR复用同一套类的元数据,才能从根本上解决这个问题。
内容的提问来源于stack exchange,提问作者Phate

