3

We have a Java EE application on WildFly 26 and OpenJDK 1.8, deployed as EAR and containing a JSF application in a WAR file.

I like to measure the size of a list of HttpSession objects, so I need something to calculate deep object size.

I tried different approaches and ended up with Java Object Layout (JOL, https://github.com/openjdk/jol) and its GraphLayout class:

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

When the application runs, I get

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 [..]

It seems the dependency to the vfs module is missing somewhere, but I don't know how and where to configure that.

Can anyone help me to make this work or propose a different approach, which works for an application as described above?

Edit 1: I put org.jboss.vfs in org/jboss/as/weld/ejb module but this only lead to another class not found in infinispan. Besides JOL, I tested two other approaches, but they ended up in the same error. And I tried to use the approach via an agent as decribed in this SO post, but couldn't make it work in combination with WildFly.

Edit 2: I saw, that the described problem has to do with using reflection, since using an old approach from Heinz Kabutz led to the very same exception:

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.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( [..]
Alexander Rühl
  • 6,769
  • 9
  • 53
  • 96
  • Maybe JOL is expecting a 'flat' classpath. In WildFly each module has its own loader and ensure a separation so maybe that is the issue you are encountering. – ehsavoie May 26 '23 at 15:11
  • Why are you trying to do this? Having more context could be useful to suggest alternatives. – aled May 26 '23 at 15:11
  • @ehsavoie: Could be - any idea how to use it nevertheless or achieve it with another tool? – Alexander Rühl May 30 '23 at 07:16
  • @aled: We saw by taking heap dumps that this one list may have a memory leak or at least is very large. So I wanted to log continuously its size to see when and how it raises in size. – Alexander Rühl May 30 '23 at 07:17
  • Use Eclipse MAT to analyze the heap dump. – aled May 30 '23 at 10:01
  • @aled That's what I did and how I came to the suspected data structure. By logging, I like to see how it behaves over time. – Alexander Rühl May 30 '23 at 10:28
  • Regarding the Classnotfound: The org.jboss.vfs.VirtualFile class is part of a JBoss module and not automatically part of your EAR classpath. You need to add it first. For that add a `jboss-deployment-structure.xml` in your META-INF folder and specify the module as a dependecy `` – Lonzak Jun 01 '23 at 10:41
  • @Lonzak If you could write that as an answer and provide a little bit more details where exactly to put that file in the ear deployment and how it looks like, I will try that out and if it works happily award the bounty. – Alexander Rühl Jun 01 '23 at 13:13

3 Answers3

1

As mentioned in the comment I try to provide a bit more detail. (Disclaimer: I didn't try it out...)

JBoss and Wildfly have the possibility to include a jboss deployment descriptor. It gives you control over the classloading in the deployment. So you can for example include or exclude dependencies or modules. We used this in the past to exclude libraries coming with the application server which (might) conflict with dependencies bundled in the deployment.

If your application does not have one, create a new file called jboss-deployment-structure.xml and add it to the project. This is an XML file with the root element <jboss-deployment-structure>. Yours would look something like this (others like this):

<jboss-deployment-structure>
    <deployment>
        <dependencies>
            <module name="org.jboss.vfs"/>
        </dependencies>
    </deployment>
</jboss-deployment-structure>

For a web application (WAR), add this file to the WEB-INF/ directory. For an EAR archive, add it to the META-INF/ directory. This should make the ClassNotFoundException go away. If it doesn't try to use <module name="org.jboss.as.weld"/>

If it doesn't then there might be other issues with the system classloader. Another approach might be to serialize the object and check the size of the object.This would also give you a rough estimate of the size. But this presumes that the object is serializable...

Lonzak
  • 9,334
  • 5
  • 57
  • 88
  • I tried this path, but unfortunately, it didn't change anything. I put it in the WAR, but still got the same exception. – Alexander Rühl Jun 05 '23 at 10:47
  • @AlexanderRühl But you have written "We have a Java EE application on WildFly 26 and OpenJDK 1.8, deployed as **EAR** ..." Deploying it as part of the WAR file might not be enough since you have a classloader [hierarchy](https://docs.jboss.org/jbossas/docs/Server_Configuration_Guide/4/html/Inside_the_JBoss_Class_Loading_Architecture-The_Complete_Class_Loading_Model.html) – Lonzak Jun 05 '23 at 12:09
  • It's like a curse, whatever I do, I end up with the same error. Sorry, the bounty is expiring now, I would have liked to honor the help, but could not yet make it run. Thanks anyway, I think I will take another path and avoid that size measring completely, allthough I like to have it seen running. – Alexander Rühl Jun 07 '23 at 09:16
  • Yes don't worry about the bounty...Did you try to put the file into the EAR (and not the WAR)? Also I another possibility to measure the size would be to serialize the object - have you considered that option? – Lonzak Jun 07 '23 at 10:39
  • Yes I put it in the EAR's META-INF. And serilization was not possible as in that list there were non serializable Objects. – Alexander Rühl Jun 07 '23 at 13:06
  • That's strange. One last idea would be to put your code that calculates the size in a separate JAR, and put that JAR directly into WildFly, e.g., wildfly.home/standalone/lib/ext. – Lonzak Jun 08 '23 at 12:43
1
  1. Your JOL problem

After reading how JOL works, I understand that it tries to attach itself to the JVM as an agent.

I had some similar issues with another java agent on wildfly, about loading some classes etc. It's probably a class loader issue. You webapp does not have access to some classes because your app has a "isolated" classloader. I think you need to say to Wildfly that JOL must have access to all modules in the JVM. I did not succeed to reproduce your error. so I'm really not sure about my answer but it does not cost a lot to try.

You just need to configure a JVM system property. Try to make your JVM start with the following :

-Djboss.modules.system.pkgs=org.jboss.byteman,org.openjdk.jol

Your Wildfly probably already starts with `-Djboss.modules.system.pkgs=org.jboss.byteman' already defined. Just add the JOL package to the property.

In a standalone wildfly instance, you can change this in the "/bin/standalone.conf" file, defined like this :

if [ "x$JBOSS_MODULES_SYSTEM_PKGS" = "x" ]; then
   JBOSS_MODULES_SYSTEM_PKGS="org.jboss.byteman"
fi

Just replace this piece of code with:

if [ "x$JBOSS_MODULES_SYSTEM_PKGS" = "x" ]; then
   JBOSS_MODULES_SYSTEM_PKGS="org.jboss.byteman,org.openjdk.jol"
fi

Edit: Put the code doing this : GraphLayout.parseInstance(sessions).totalSize()); in a dedicated class. Put this class in the package `org.openjdk.jol' in your app.

I think it will work.

  1. Does your suspected memory leak cause some OutOfMemoryError ? If it does, add this option to your JVM: -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath:"folder where you want your heap dump to be stored"

It's the best way to get an accurate heap dump. You will have the exact image of your heap when the OutOfMemoryError occurs. By inspecting the sessions, you will know what kind of objects occupy too much heap.

Seb Perp
  • 276
  • 1
  • 9
  • That sounded very promising, but didn't help. But this may have to do with re-setting the property at another place. I added it within the start script as well und this time, I did not get the original exception, but a `Caused by: java.lang.NoClassDefFoundError: org/openjdk/jol/info/GraphLayout`, allthough I added the jol jar to the war and then to the lib ear folder. Any idea what could cause this now? – Alexander Rühl Jun 05 '23 at 10:46
  • Yes, I think. Now, JOL is loaded in an "application server" level classloader. Its classes are not available anymore in your "war scope" classloader. Maybe you should deploy the JOL package as a global module (see there https://www.mastertheboss.com/jbossas/jboss-configuration/configuring-global-modules-and-directories-in-wildfly/) and define the dependency in your app as "provided". – Seb Perp Jun 05 '23 at 11:55
  • I have a far simpler solution for you. See the "Edit" part. – Seb Perp Jun 06 '23 at 18:19
  • It's like a curse, whatever I do, I end up with the same error. Sorry, the bounty is expiring now, I would have liked to honor the help, but could not yet make it run. Thanks anyway, I think I will take another path and avoid that size measring completely, allthough I like to have it seen running. – Alexander Rühl Jun 07 '23 at 09:15
  • Don't worry about the bounty, I want the riddle to be solved lol But did you try to put your code using JOL in org.openjdk.jol package ? Do you get the same error ? Let me know, I'm curious. – Seb Perp Jun 07 '23 at 09:25
  • Yes I did, but the very same exception occured. – Alexander Rühl Jun 07 '23 at 09:31
0

When using JOL to determine the size of HttpSession objects, the problem with the NoClassDefFoundError that you're encountering is caused by the absence of the class org.jboss.vfs.VirtualFile. The needed dependency is missing from your application's classpath, which leads to this error.

Include the relevant JAR file containing the org.jboss.vfs.VirtualFile class in your application's classpath as a potential workaround. You may find the JAR file containing this class and add it to your JSF application's WAR file's WEB-INF/lib directory. In this manner, when the application is deployed, it will be added to the classpath.

Alternatively, if you are using Maven or another build tool, you can add the dependency to your project's configuration file (pom.xml for Maven) to automatically include the required JAR file during the build process. You'll need to find the appropriate Maven coordinates for the JAR containing org.jboss.vfs.VirtualFile and add them to your project's dependencies.

Here's an example of how you can add a Maven dependency for the JBoss VFS module in your pom.xml file:

<dependencies>
<!-- Other dependencies -->
<dependency>
    <groupId>org.jboss</groupId>
    <artifactId>jboss-vfs</artifactId>
    <version><!-- Specify the appropriate version here --></version>
</dependency>
Nilupul Heshan
  • 580
  • 1
  • 5
  • 18
  • As you mentioned this is only a **workaround** since this dependency is part of the wildfly application server (Check e.g. `wildfly-26.0.1.Final\modules\system\layers\base\org\jboss\vfs\main\jboss-vfs-3.2.XX.Final.jar` If the manually added version doesn't match, both bring different versions along which might lead to conflicts later. – Lonzak Jun 02 '23 at 10:06