Java 7 w akcjiJava 7 in action Wreszcie znalazłem wolną chwilę by wypróbować w praktyce nowe ficzery Java 7. Zabawy z wersją beta najnowszego jdk ułatwia NetBeans IDE 7.0 Beta 2. Do testów na pierwszy ogień poszły usprawnienia...

Readmore

Monitoring podstawowych parametrów JVM z poziomu web... Problem Monitoring podstawowych parametrów JVM z poziomu web aplikacji - przydatne zwłaszcza wtedy, gdy nasz serwer aplikacji/kontener serwletów nie pokazuje takich informacji w swojej webowej konsoli...

Readmore

Vaadin vs Richfaces i o tym co z tego wyszło [Java... Głośno ostatnio na DWorld i DZone zrobiło się o nowej odsłonie Vaadina - frameworku opartego na GWT. Nigdy wcześniej nie miałem styczności z GWT (prócz kilku tutoriali i paru hellowordów). Pracuję...

Readmore

Jak wyciągnąć kilka pierwszych wyników zapytania... [sql] -- Oracle select a.* from (select rownum row_num, t.* from t_table t ) a where a.row_num <= N -- DB2 select * from t_table fetch first 10 rows only -- Informix select...

Readmore

Vademecum IBM i oraz darmowe konto na iSeriesIBM i... Znalazłem jakiś czas temu 'hosting' oparty o iSeries, na którym można założyć sobie darmowe konto. Gdyby ktoś zatem poczuł nieodpartą pokusę pobawienia się AS/400 Green Screen, to ma taką...

Readmore

twitter

Monitoring podstawowych parametrów JVM z poziomu web aplikacji

Kategoria : java

Problem

Monitoring podstawowych parametrów JVM z poziomu web aplikacji – przydatne zwłaszcza wtedy, gdy nasz serwer aplikacji/kontener serwletów nie pokazuje takich informacji w swojej webowej konsoli i nie mamy możliwości szybkiego podpięcia się VisualVM albo JConsole (brak aktywowanego jmxremote, firewalle, czy cokolwiek innego uniemożliwiającego połączenie), a podejrzewamy że coś niedobrego może dziać się z naszą aplikacją.

Rozwiązanie

Wrzucenie prostej stronki JSP, która nam takie parametry wyświetli.

Poniżej listing (po moich drobnych modyfikacjach) zapożyczony z http://www.freshblurbs.com/explaining-java-lang-outofmemoryerror-permgen-space

Skryptlet (tak wiem, śmierdzi) użyty na tej stronie pozwala wrzucać ją do aplikacji bez potrzeby restartu serwera/redeploy aplikacji.

<%@page contentType="text/html" pageEncoding="UTF-8"%>
<%@ page import="java.util.*, java.lang.management.*" %>
<%@ taglib uri="http://java.sun.com/jstl/core_rt" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
    	               "http://www.w3.org/TR/html4/loose.dtd">

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>JVM Memory Monitor</title>
    <style type="text/css">
      td {
        text-align: right;
      }
    </style>
  </head>
<body>
  <jsp:scriptlet>
    MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
    pageContext.setAttribute("memoryBean", memoryBean, PageContext.PAGE_SCOPE);

    List poolBeans = ManagementFactory.getMemoryPoolMXBeans();
    pageContext.setAttribute("poolBeans", poolBeans, PageContext.PAGE_SCOPE);
  </jsp:scriptlet>

  <h3>Total Memory</h3>
  <table border="1" width="100%">
    <tr>
      <th>usage</th>
      <th>init</th>
      <th>used</th>
      <th>committed</th>
      <th>max</th>
    </tr>
    <tr>
      <td style="text-align: left">Heap Memory Usage</td>
      <td><fmt:formatNumber value="${pageScope.memoryBean.heapMemoryUsage.init / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
      <td><fmt:formatNumber value="${pageScope.memoryBean.heapMemoryUsage.used / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
      <td><fmt:formatNumber value="${pageScope.memoryBean.heapMemoryUsage.committed / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
      <td><fmt:formatNumber value="${pageScope.memoryBean.heapMemoryUsage.max / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
    </tr>
    <tr>
      <td style="text-align: left">Non-heap Memory Usage</td>
      <td><fmt:formatNumber value="${pageScope.memoryBean.nonHeapMemoryUsage.init / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
      <td><fmt:formatNumber value="${pageScope.memoryBean.nonHeapMemoryUsage.used / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
      <td><fmt:formatNumber value="${pageScope.memoryBean.nonHeapMemoryUsage.committed / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
      <td><fmt:formatNumber value="${pageScope.memoryBean.nonHeapMemoryUsage.max / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
    </tr>
  </table>

  <h3>Memory Pools</h3>
  <table border="1" width="100%">
    <tr>
      <th>Name</th>
      <th>Usage</th>
      <th>Init</th>
      <th>Used</th>
      <th>Committed</th>
      <th>Max</th>
    </tr>
    <c:forEach var="bean" items="${pageScope.poolBeans}">
      <tr>
        <td style="text-align: left"><c:out value="${bean.name}" /></td>
        <td style="text-align: left">Memory Usage</td>
        <td><fmt:formatNumber value="${bean.usage.init / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
        <td><fmt:formatNumber value="${bean.usage.used / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
        <td><fmt:formatNumber value="${bean.usage.committed / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
        <td><fmt:formatNumber value="${bean.usage.max / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
      </tr>
      <tr>
        <td></td>
        <td style="text-align: left">Peak Usage</td>
        <td><fmt:formatNumber value="${bean.peakUsage.init / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
        <td><fmt:formatNumber value="${bean.peakUsage.used / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
        <td><fmt:formatNumber value="${bean.peakUsage.committed / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
        <td><fmt:formatNumber value="${bean.peakUsage.max / (1024 * 1024)}" maxFractionDigits="1"/> MB</td>
      </tr>
    </c:forEach>
  </table>
</body>
</html>

Efekt poniżej:

To rozwiązanie tak mi się spodobało, że z rozpędu napisałem sobie to samo w Richfaces. Strona odświeża się automatycznie co zadany przedział czasu (w ms). Wykorzystanie Ajax Push/Poll nie powala na pewno z punktu widzenia wydajności, jest natomiast rozwiązaniem banalnym do zaimplementowania:

<html xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:a4j="http://richfaces.org/a4j"
    xmlns:rich="http://richfaces.org/rich">

<head>
    <title>JVM Memory Monitor</title>
</head>

<body>
    <a4j:region>
        <h:form>
            <a4j:poll id="poll" interval="#{jvmPerformanceManager.interval}" enabled="#{jvmPerformanceManager.pollEnabled}" reRender="polledPanel, poll"/>
        </h:form>
    </a4j:region>
    <h:form prependId="false">
        <h:panelGrid id="polledPanel" columns="1">
            <h3>Total Memory</h3>
            <rich:dataTable value="2" style="text-align: right">
                <f:facet name="header">
                    <rich:columnGroup>
                        <rich:column>
                            <h:outputText value="Name" />
                        </rich:column>
                        <rich:column>
                            <h:outputText value="Init" />
                        </rich:column>
                        <rich:column>
                            <h:outputText value="Used" />
                        </rich:column>
                        <rich:column>
                            <h:outputText value="Committed" />
                        </rich:column>
                        <rich:column>
                            <h:outputText value="Max" />
                        </rich:column>
                    </rich:columnGroup>
                </f:facet>
                <rich:column>
                    <h:outputText value="Heap Memory Usage" />
                </rich:column>
                <rich:column>
                    <h:outputText
                        value="#{jvmPerformanceManager.memoryBean.heapMemoryUsage.init / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText
                        value="#{jvmPerformanceManager.memoryBean.heapMemoryUsage.used / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText
                        value="#{jvmPerformanceManager.memoryBean.heapMemoryUsage.committed / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText
                        value="#{jvmPerformanceManager.memoryBean.heapMemoryUsage.max / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column breakBefore="true">
                    <h:outputText value="Non-heap Memory Usage" />
                </rich:column>
                <rich:column>
                    <h:outputText
                        value="#{jvmPerformanceManager.memoryBean.nonHeapMemoryUsage.init / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText
                        value="#{jvmPerformanceManager.memoryBean.nonHeapMemoryUsage.used / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText
                        value="#{jvmPerformanceManager.memoryBean.nonHeapMemoryUsage.committed / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText
                        value="#{jvmPerformanceManager.memoryBean.nonHeapMemoryUsage.max / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>

                <f:facet name="footer">
                    <rich:columnGroup>
                        <rich:column>Last actualization date</rich:column>
                        <rich:column colspan="4">
                            <h:outputText value="#{jvmPerformanceManager.lastActualization}">
                                <f:convertDateTime pattern="yyyy-MM-dd HH:mm:ss" timeZone="#{jvmPerformanceManager.timeZone}"/>
                            </h:outputText>
                        </rich:column>
                    </rich:columnGroup>
                </f:facet>
            </rich:dataTable>

            <h3>Memory Pools</h3>
            <rich:dataTable var="poolBean" value="#{jvmPerformanceManager.poolList}"
                style="text-align: right">
                <f:facet name="header">
                    <rich:columnGroup>
                        <rich:column>
                            <h:outputText value="Name" />
                        </rich:column>
                        <rich:column>
                            <h:outputText value="Usage" />
                        </rich:column>
                        <rich:column>
                            <h:outputText value="Init" />
                        </rich:column>
                        <rich:column>
                            <h:outputText value="Used" />
                        </rich:column>
                        <rich:column>
                            <h:outputText value="Committed" />
                        </rich:column>
                        <rich:column>
                            <h:outputText value="Max" />
                        </rich:column>
                    </rich:columnGroup>
                </f:facet>
                <rich:column rowspan="2">
                    <h:outputText value="#{poolBean.name}" />
                </rich:column>
                <rich:column>
                    <h:outputText value="Memory" />
                </rich:column>
                <rich:column>
                    <h:outputText value="#{poolBean.usage.init / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText value="#{poolBean.usage.used / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText value="#{poolBean.usage.committed / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText value="#{poolBean.usage.max / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column breakBefore="true">
                    <h:outputText value="Peak" />
                </rich:column>
                <rich:column>
                    <h:outputText value="#{poolBean.peakUsage.init / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText value="#{poolBean.peakUsage.used / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText
                        value="#{poolBean.peakUsage.committed / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
                <rich:column>
                    <h:outputText value="#{poolBean.peakUsage.max / (1024 * 1024)}">
                        <f:convertNumber pattern="#,##0.0 MB" locale="US" />
                    </h:outputText>
                </rich:column>
            </rich:dataTable>

            <h:panelGrid columns="2">
                <h:column>
                    <a4j:commandButton id="controlBtn"
                        value="#{jvmPerformanceManager.pollEnabled?'Stop':'Start'}" reRender="polledPanel, poll">
                    <a4j:actionparam name="polling" value="#{!jvmPerformanceManager.pollEnabled}"
                        assignTo="#{jvmPerformanceManager.pollEnabled}" />
                    </a4j:commandButton>
                </h:column>
                <h:column>
                    <a4j:outputPanel style="display: inline" rendered="#{!jvmPerformanceManager.pollEnabled}">
                        <h:outputText value=" with " />
                        <h:inputText value="#{jvmPerformanceManager.interval}">
                            <f:validateLongRange minimum="500" />
                        </h:inputText>
                        <h:outputText value=" ms interval." />
                    </a4j:outputPanel>
                </h:column>
            </h:panelGrid>
        </h:panelGrid>
    </h:form>
</body>
</html>
package info.ludera.helper;

import java.io.Serializable;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryPoolMXBean;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.event.ActionEvent;

@ManagedBean(name="jvmPerformanceManager")
@SessionScoped
public class JvmPerformanceManager implements Serializable {

    private MemoryMXBean memoryBean;

    private List<MemoryPoolMXBean> poolList;

    private Boolean pollEnabled = true;

    /**
     * Interval in milliseconds. Default 10s.
     */
    private Long interval = 10*1000l;

    private String timeZone;

    public JvmPerformanceManager() {
        getActualMemoryInfo(null);
        timeZone = readDefaultHostTimeZone();
    }

    private String readDefaultHostTimeZone() {
        return TimeZone.getDefault().getID();
    }

    public void getActualMemoryInfo(ActionEvent actionEvent) {
        memoryBean = ManagementFactory.getMemoryMXBean();
        poolList = ManagementFactory.getMemoryPoolMXBeans();
    }

    public MemoryMXBean getMemoryBean() {
        return memoryBean;
    }

    public List<MemoryPoolMXBean> getPoolList() {
        return poolList;
    }

    public Boolean getPollEnabled() {
        return pollEnabled;
    }

    public void setPollEnabled(Boolean pollEnabled) {
        this.pollEnabled = pollEnabled;
    }

    public Long getInterval() {
        return interval;
    }

    public void setInterval(Long interval) {
        this.interval = interval;
    }

    public Date getLastActualization() {
        return Calendar.getInstance().getTime();
    }

    public String getTimeZone() {
        return timeZone;
    }
}

Efekt:

Jestem obecnie, jak pewnie wielu, na etapie rozpoznawania nowości w Java EE 6. Treningowo, postanowiłem zatem napisać ten sam JVM Monitor z wykorzystaniem Asynchronious Processing Support z Servlets 3.0 – także wkrótce rozwinięcie tematu.