/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2008-2010 Sun Microsystems, Inc. * Portions Copyright 2014-2015 ForgeRock AS */ package org.opends.server.monitors; import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.lang.management.MemoryPoolMXBean; import java.lang.management.MemoryUsage; import java.util.ArrayList; import java.util.HashMap; import java.util.concurrent.TimeUnit; import org.opends.server.admin.std.server.MemoryUsageMonitorProviderCfg; import org.opends.server.api.MonitorProvider; import org.forgerock.opendj.config.server.ConfigException; import org.opends.server.core.DirectoryServer; import org.opends.server.types.*; /** * This class defines a monitor provider that reports information about * Directory Server memory usage. */ public class MemoryUsageMonitorProvider extends MonitorProvider implements Runnable { /** A map of the last GC counts seen by this monitor for calculating recent stats. */ private HashMap lastGCCounts = new HashMap<>(); /** A map of the last GC times seen by this monitor for calculating recent stats. */ private HashMap lastGCTimes = new HashMap<>(); /** A map of the most recent GC durations seen by this monitor. */ private HashMap recentGCDurations = new HashMap<>(); /** A map of the memory manager names to names that are safe for use in attribute names. */ private HashMap gcSafeNames = new HashMap<>(); /** {@inheritDoc} */ public void initializeMonitorProvider( MemoryUsageMonitorProviderCfg configuration) throws ConfigException, InitializationException { scheduleUpdate(this, 0, 1, TimeUnit.SECONDS); } /** {@inheritDoc} */ @Override public String getMonitorInstanceName() { return "JVM Memory Usage"; } /** {@inheritDoc} */ public void run() { for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) { String gcName = gc.getName(); long gcCount = gc.getCollectionCount(); long gcTime = gc.getCollectionTime(); long lastGCCount = 0L; long lastGCTime = 0L; long recentGCDuration = 0L; if (lastGCCounts.containsKey(gcName)) { lastGCCount = lastGCCounts.get(gcName); lastGCTime = lastGCTimes.get(gcName); recentGCDuration = recentGCDurations.get(gcName); } if (gcCount > lastGCCount) { long recentGCCount = gcCount - lastGCCount; long recentGCTime = gcTime - lastGCTime; recentGCDuration = recentGCTime / recentGCCount; } lastGCCounts.put(gcName, gcCount); lastGCTimes.put(gcName, gcTime); recentGCDurations.put(gcName, recentGCDuration); } } /** {@inheritDoc} */ @Override public ArrayList getMonitorData() { ArrayList attrs = new ArrayList<>(); for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) { String gcName = gc.getName(); long gcCount = gc.getCollectionCount(); long gcTime = gc.getCollectionTime(); long avgGCDuration = 0L; if (gcCount > 0) { avgGCDuration = gcTime / gcCount; } long recentGCDuration = 0L; if (recentGCDurations.containsKey(gcName)) { recentGCDuration = recentGCDurations.get(gcName); } String safeName = gcSafeNames.get(gcName); if (safeName == null) { safeName = generateSafeName(gcName); gcSafeNames.put(gcName, safeName); } attrs.add(createAttribute(safeName + "-total-collection-count", String.valueOf(gcCount))); attrs.add(createAttribute(safeName + "-total-collection-duration", String.valueOf(gcTime))); attrs.add(createAttribute(safeName + "-average-collection-duration", String.valueOf(avgGCDuration))); attrs.add(createAttribute(safeName + "-recent-collection-duration", String.valueOf(recentGCDuration))); } for (MemoryPoolMXBean mp : ManagementFactory.getMemoryPoolMXBeans()) { String poolName = mp.getName(); MemoryUsage currentUsage = mp.getUsage(); MemoryUsage collectionUsage = mp.getCollectionUsage(); String safeName = gcSafeNames.get(poolName); if (safeName == null) { safeName = generateSafeName(poolName); gcSafeNames.put(poolName, safeName); } if (currentUsage == null) { attrs.add(createAttribute(safeName + "-current-bytes-used", "0")); } else { attrs.add(createAttribute(safeName + "-current-bytes-used", String.valueOf(currentUsage.getUsed()))); } if (collectionUsage == null) { attrs.add(createAttribute(safeName + "-bytes-used-after-last-collection", "0")); } else { attrs.add(createAttribute(safeName + "-bytes-used-after-last-collection", String.valueOf(collectionUsage.getUsed()))); } } return attrs; } /** * Constructs an attribute using the provided information. It will have the * default syntax. * * @param name The name to use for the attribute. * @param value The value to use for the attribute. * * @return The attribute created from the provided information. */ private Attribute createAttribute(String name, String value) { AttributeType attrType = DirectoryServer.getDefaultAttributeType(name); return Attributes.create(attrType, value); } /** * Creates a "safe" version of the provided name, which is acceptable for * use as part of an attribute name. * * @param name The name for which to obtain the safe name. * * @return The calculated safe name. */ private String generateSafeName(String name) { StringBuilder buffer = new StringBuilder(); boolean lastWasUppercase = false; boolean lastWasDash = false; for (int i=0; i < name.length(); i++) { char c = name.charAt(i); if (Character.isLetter(c)) { if (Character.isUpperCase(c)) { char lowerCaseCharacter = Character.toLowerCase(c); if (buffer.length() > 0 && !lastWasUppercase && !lastWasDash) { buffer.append('-'); } buffer.append(lowerCaseCharacter); lastWasUppercase = true; lastWasDash = false; } else { buffer.append(c); lastWasUppercase = false; lastWasDash = false; } } else if (Character.isDigit(c)) { buffer.append(c); lastWasUppercase = false; lastWasDash = false; } else if (c == ' ' || c == '_' || c == '-') { if (! lastWasDash) { buffer.append('-'); } lastWasUppercase = false; lastWasDash = true; } } return buffer.toString(); } }