Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Slimming Down Your CDI Memory Footprint

DZone's Guide to

Slimming Down Your CDI Memory Footprint

Weld recently released an update that should help lower the memory use of your CDI apps. See how it works and how to implement it.

Free Resource

Download Microservices for Java Developers: A hands-on introduction to frameworks and containers. Brought to you in partnership with Red Hat.

Recently released Weld 3.0.4 introduced a new optimization that helps redue the memory consumption of CDI applications. It's an ability to detect and remove so-called unused beans. In this article, we're going to describe the basic principles of this optimization and then, using a simple application, we will enable the optimization (it's disabled by default) and measure the actual impact.

How Does it Work?

Most CDI applications contain a lot of beans coming from third-party libraries and integrations (e.g. DeltaSpike and various Java EE technologies — JSF, Bean Validation, Batch). The problem is that Weld has to retain the metadata for all the beans in memory, even though only a small part of those beans is used at runtime. So what is this optimization about? In short, Weld can perform additional cleanup after bootstrap, i.e. detect unused beans and remove the corresponding metadata. An unused bean is, in brief, neither injected anywhere, nor does it declare a CDI observer, nor does it have a name (a complete list of rules is listed in the Weld docs).

Note: As usual, there is a trade-off between memory consumption and bootstrap time.  Your mileage may vary depending on the application, but you should always expect a most probably negligible increase of the bootstrap time. In our example, the optimization saves about 446 KB (14% of theWeld footprint) but increases the bootstrap time by about 60ms (5% of the Weld bootstrap time).

Example Application

The following steps show how to enable the aforementioned optimization for a Java EE application deployed to an WildFly application server.

What You’ll Need

  • About 15 minutes

  • Java Development Kit 1.8

  • Maven, Git

  • Ticket Monster example (see also step 1)

  • WildFly 12 and Weld 3.0.4 patch (see also step 2)

Build the Example App

Simply clone the repo and build the Maven project:

git clone git@github.com:jboss-developer/ticket-monster.git
cd ticket-monster
mvn clean package


Note: If the build fails to download some JBoss artifacts, try to replace the Red Hat repository URL with https://maven.repository.redhat.com/earlyaccess/all.

Start WildFly and Deploy the App

Download WildFly 12 distribution and the Weld 3.0.4 patch. Applying the patch is quite straightforward — follow this Weld FAQ entry. Now we're ready to deploy the app:

$ cp /path/to/ticket-monster/target/ticket-monster.war /path/to/wildfly/standalone/deployments


And start the server:

$ cd /path/to/wildfly
$ cd bin
$ ./standalone.sh


Launch Probe (Optional)

This step is optional, but using the Probe development tool, you can easily get an idea about how many unused beans can be found in your app. Stop the server and enable development mode, e.g. edit the configuration file  /path/to/wildfly/standalone/configuration/standalone.xml and add the development-mode="true" attribute to the Weld subsystem tag (see the WildFly docs for more info):

<subsystem xmlns="urn:jboss:domain:weld:4.0" development-mode="true"/>


Then start the server again and open  http://localhost:8080/ticket-monster/weld-probe in the browser. You should see the Probe UI. Now navigate to the "Beans" view, check Unused beans only and select  ANY bean archive.

Weld Probe - Beans View


You should find 127 unused bean candidates. Stop the server and disable development mode again (it will affect the measurement).

First Measurement

Start the server and run the VisualVM tool (${JDK_HOME}/bin/jvisualvm). Then open the WildFly JVM instance (its name will be something like  org.jboss.modules.Main (pid 1234) ), switch to the Monitor tab, and perform GC several times. Then, acquire a heap dump ("Heap dump" button).

Now we're ready to calculate Weld's footprint. Let's open the OQL console and type the following query: 

select sum(heap.objects('java.lang.Object', true, "/org.jboss.weld./.test(classof(it).name)"), 'rsizeof(it)')


This query should find all classes whose FQCN starts with org.jboss.weld and add all the retained sizes. You should get something like 3 006 447B (3MB). In other words, the footprint of all Weld classes is about 3 MB.

Enable Optimization

Add the weld.properties file to /path/to/ticket-monster/src/main/resources. The content of the file should be:  org.jboss.weld.bootstrap.unusedBeans.excludeType=NONE (see the Weld docs for the description). Build and re-deploy the app (steps 1 and 2).

Next Measurement and Comparing the Results

Perform the measurement again (step 4) and compare the results. You should get something like 2 560 789B (2.6MB). And that's it. 

Conclusion

We've shown how to enable a new Weld optimization and also measure its impact. Try it out and let us know about your results or possible glitches! For further details, see the Weld docs.

Download Building Reactive Microservices in Java: Asynchronous and Event-Based Application Design. Brought to you in partnership with Red Hat

Topics:
cdi ,weld ,wildfly ,java ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}