Sunday, May 19, 2013

Monitoring Memory Usage inside a Java Web Application with JSF, PrimeFaces and MemoryMXBean

This article explains how to monitor memory usage in your web application by requesting the MemoryMXBean and exposing collected metrics within a Primefaces LineChart component in a JSF page.

Principle

The monitoring of the memory usage involves:

The overall design is illustrated below:



  1. The LineChart component calls every minute the MonitorController instance.
  2. The MonitorController instance requests the MemoryMXBean instance to get the current memory usage.
  3. The MonitorController instance updates a CartesianChartModel instance by adding a memory usage snapshot.
  4. The CartesianChartModel instance is returned to the LineChart component that renders it inside a JSF page.

PrimeFaces LineChart

To enable PrimeFaces components into your web application, update your maven pom.xml with the following repository and dependency:
    
        
            primefaces-maven-repo
            PrimeFaces Maven Repository
            http://repository.primefaces.org
            default
        
    
        
            org.primefaces
            primefaces
            3.4.2
        
Next, inside a JSF page, add the following code to display the LineChart and make it polling the MonitorController every minute:
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:p="http://primefaces.org/ui"
      xmlns:c="http://java.sun.com/jsp/jstl/core"
      xmlns:ui="http://java.sun.com/jsf/facelets">

    <h:head>
       ...
    </h:head>

    <h:body>
       ...
                <h:form id="form">  

                    <!-- polls every minute -->
                    <p:poll interval="60" update="memoryChart" />  

                    <!-- memory line chart -->
                    <p:lineChart id="memoryChart" value="#{monitorController.memoryModel}"
                                 legendPosition="ne" title="Memory Usage" style="height:300px;margin-top:20px"
                                 xaxisLabel="Minutes" yaxisLabel="Bytes" zoom="true"/>  

                </h:form>  
       ...

    </h:body>
</html>


MonitorController

The code corresponding to the monitor controller class is:
package org.javabenchmark;

import java.io.Serializable;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryUsage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import org.primefaces.model.chart.CartesianChartModel;
import org.primefaces.model.chart.ChartSeries;
import org.slf4j.LoggerFactory;

/**
 * JSF Managed Bean to get JVM memory usage.
 *
 */
@ManagedBean
@SessionScoped
public class MonitorController implements Serializable {

    /**
     * memory model.
     */
    private CartesianChartModel memoryModel;
    /**
     * memory usage series.
     */
    private ChartSeries memoryUsageSerie, maxMemorySerie;
    /**
     * elapsed time in minutes since the monitoring starts
     */
    private long elapsedTime = -1;

    /**
     * instantiates a new monitoring controller.
     */
    public MonitorController() {
        
        createMemoryModel();
    }
    
    /**
     * initializes memory usage model.
     */
    private void createMemoryModel() {  
        // model
        memoryModel = new CartesianChartModel(); 
        // heap serie
        memoryUsageSerie = new ChartSeries();  
        memoryUsageSerie.setLabel("Heap");
        memoryModel.addSeries(memoryUsageSerie);
        // max serie
        maxMemorySerie = new ChartSeries();  
        maxMemorySerie.setLabel("Max");
        memoryModel.addSeries(maxMemorySerie); 
    }
    
    /**
     * gets the memory model that will be rendered within a LineChart
     * component. The LineChart component should call this method every minute.
     * @return the updated memory model
     */
    public CartesianChartModel getMemoryModel() {  
        
        // gets data
        MemoryMXBean memoryMxBean = ManagementFactory.getMemoryMXBean();
        MemoryUsage memoryUsage = memoryMxBean.getHeapMemoryUsage();
        
        // one more minute
        elapsedTime++;
        
        // updates series
        memoryUsageSerie.set(elapsedTime, memoryUsage.getUsed());
        maxMemorySerie.set(elapsedTime, memoryUsage.getMax()); 
        
        return memoryModel;  
    }
}


Screenshot

Finally, a screenshot of the PrimeFaces line chart representing the memory usage :


Notice that it is possible to zoom with drag and drop, if you have a lot of points displayed.

Conclusion

As you can see, it is quite easy to monitor the JVM and to display collected metrics within a JSF page. You can go further by monitoring CPU usage for instance, or by adding some features like starting/stopping/resetting the monitoring.

1 comment: