You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在WildFly的WAR包中使用OpenJDK JOL测量对象大小?

问题:WildFly 26下测量HttpSession深度内存大小遇到类加载异常

我们基于WildFly 26和OpenJDK 1.8开发Java EE应用,打包为EAR包(内含JSF的WAR模块),需要测量一组HttpSession对象的深度内存大小,但尝试多种方案均失败:

尝试的方案及错误信息

  1. 使用Java Object Layout (JOL)
    代码:

    GraphLayout.parseInstance(sessions).totalSize());
    

    抛出异常:

    java.lang.NoClassDefFoundError: Lorg/jboss/vfs/VirtualFile;
            at java.lang.Class.getDeclaredFields0(Native Method) [rt.jar:1.8.0_345]
            at java.lang.Class.privateGetDeclaredFields(Class.java:2583) [rt.jar:1.8.0_345]
            at java.lang.Class.getDeclaredFields(Class.java:1916) [rt.jar:1.8.0_345]
            at org.openjdk.jol.info.GraphWalker.getAllReferences(GraphWalker.java:128)
            at org.openjdk.jol.info.GraphWalker.walk(GraphWalker.java:96)
            at org.openjdk.jol.info.GraphLayout.parseInstance(GraphLayout.java:63)
            at <myClass> [..]
    Caused by: java.lang.ClassNotFoundException: org.jboss.vfs.VirtualFile from [Module "org.jboss.as.weld.ejb" version 26.1.1.Final from local module loader [..]
    
  2. 添加容器模块依赖
    尝试将org.jboss.vfs添加到org/jboss/as/weld/ejb模块后,又出现Infinispan相关类缺失的新异常。

  3. 使用Heinz Kabutz的内存测量方案
    同样抛出相同的类加载异常:

    Caused by: java.lang.NoClassDefFoundError: Lorg/jboss/vfs/VirtualFile;
            at java.lang.Class.getDeclaredFields0(Native Method) [rt.jar:1.8.0_345]
            at java.lang.Class.privateGetDeclaredFields(Class.java:2583) [rt.jar:1.8.0_345]
            at java.lang.Class.getDeclaredFields(Class.java:1916) [rt.jar:1.8.0_345]
            at MemoryCounter._estimate( [..]
    
解决方案

1. 只测量Session中的业务属性(推荐)

WildFly的HttpSession实现包含大量容器内部对象(如VFS引用、Infinispan缓存实例),这些对象属于容器模块,应用类加载器无法访问。因此应避开容器内部对象,仅测量Session中存储的业务数据:

// 提取Session中的所有业务属性
Map<String, Object> sessionBusinessData = new HashMap<>();
Enumeration<String> attributeNames = session.getAttributeNames();
while (attributeNames.hasMoreElements()) {
    String name = attributeNames.nextElement();
    sessionBusinessData.put(name, session.getAttribute(name));
}
// 使用JOL测量业务数据的总内存大小
long totalSize = GraphLayout.parseInstance(sessionBusinessData).totalSize();

2. 利用WildFly内置JMX监控获取Session内存数据

无需编写代码,直接通过WildFly的JMX控制台获取Session内存统计:

  • 启动WildFly后,使用jconsole或其他JMX工具连接服务器
  • 定位到jboss.web域下的host=default-host,path=/<你的应用上下文路径>,subsystem=undertow MBean
  • 查看sessionAverageSize、sessionMaxSize等属性,直接获取Session内存占用的统计值

3. 自定义反射逻辑,过滤容器内部类

如果必须测量完整Session对象,可修改反射遍历逻辑,过滤掉WildFly容器相关的类(如org.jboss.*、org.wildfly.*、org.infinispan.*)。以Heinz Kabutz的方案为例,修改核心测量方法:

private static long calculateSize(Object obj, Map<Object, Object> visited) {
    if (obj == null || visited.containsKey(obj)) {
        return 0;
    }
    Class<?> cls = obj.getClass();
    // 过滤容器内部类,避免加载不可访问的类
    if (cls.getName().matches("org\\.(jboss|wildfly|infinispan)\\..*")) {
        return 0;
    }
    visited.put(obj, null);
    long size = getClassSize(cls);
    
    // 遍历对象字段时跳过静态字段,并过滤容器类字段
    for (Field field : cls.getDeclaredFields()) {
        if (Modifier.isStatic(field.getModifiers())) {
            continue;
        }
        field.setAccessible(true);
        Object fieldValue;
        try {
            fieldValue = field.get(obj);
        } catch (IllegalAccessException e) {
            continue;
        }
        if (field.getType().isArray()) {
            int length = Array.getLength(fieldValue);
            for (int i = 0; i < length; i++) {
                size += calculateSize(Array.get(fieldValue, i), visited);
            }
        } else {
            size += calculateSize(fieldValue, visited);
        }
    }
    return size;
}

// 原方案中的sizeOf方法,用于计算单个类的内存大小
private static long getClassSize(Class<?> cls) {
    // 保留原实现逻辑
}

4. 声明容器模块依赖(完整遍历Session时使用)

若需要完整遍历Session对象,可在应用的jboss-deployment-structure.xml中声明对所需容器模块的依赖,无需修改WildFly核心模块:
在EAR包的META-INF目录下创建jboss-deployment-structure.xml,内容如下:

<jboss-deployment-structure>
    <deployment>
        <dependencies>
            <module name="org.jboss.vfs" export="true"/>
            <module name="org.infinispan.core" export="true"/>
            <module name="org.wildfly.clustering.web" export="true"/>
            <module name="org.jboss.as.web" export="true"/>
        </dependencies>
    </deployment>
</jboss-deployment-structure>

该配置会让应用类加载器能够访问这些容器模块,解决类加载异常问题。

内容的提问来源于stack exchange,提问作者Alexander Rühl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 01:03:15