001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2008-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2016 ForgeRock AS. 016 */ 017package org.opends.server.monitors; 018 019 020 021import java.lang.management.GarbageCollectorMXBean; 022import java.lang.management.ManagementFactory; 023import java.lang.management.MemoryPoolMXBean; 024import java.lang.management.MemoryUsage; 025import java.util.HashMap; 026import java.util.concurrent.TimeUnit; 027 028import org.forgerock.opendj.config.server.ConfigException; 029import org.opends.server.api.MonitorData; 030import org.forgerock.opendj.server.config.server.MemoryUsageMonitorProviderCfg; 031import org.opends.server.api.MonitorProvider; 032import org.opends.server.types.InitializationException; 033 034/** 035 * This class defines a monitor provider that reports information about 036 * Directory Server memory usage. 037 */ 038public class MemoryUsageMonitorProvider 039 extends MonitorProvider<MemoryUsageMonitorProviderCfg> 040 implements Runnable 041{ 042 /** A map of the last GC counts seen by this monitor for calculating recent stats. */ 043 private HashMap<String,Long> lastGCCounts = new HashMap<>(); 044 /** A map of the last GC times seen by this monitor for calculating recent stats. */ 045 private HashMap<String,Long> lastGCTimes = new HashMap<>(); 046 /** A map of the most recent GC durations seen by this monitor. */ 047 private HashMap<String,Long> recentGCDurations = new HashMap<>(); 048 /** A map of the memory manager names to names that are safe for use in attribute names. */ 049 private HashMap<String,String> gcSafeNames = new HashMap<>(); 050 051 052 /** {@inheritDoc} */ 053 @Override 054 public void initializeMonitorProvider( 055 MemoryUsageMonitorProviderCfg configuration) 056 throws ConfigException, InitializationException 057 { 058 scheduleUpdate(this, 0, 1, TimeUnit.SECONDS); 059 } 060 061 /** {@inheritDoc} */ 062 @Override 063 public String getMonitorInstanceName() 064 { 065 return "JVM Memory Usage"; 066 } 067 068 069 /** {@inheritDoc} */ 070 @Override 071 public void run() 072 { 073 for (GarbageCollectorMXBean gc : 074 ManagementFactory.getGarbageCollectorMXBeans()) 075 { 076 String gcName = gc.getName(); 077 long gcCount = gc.getCollectionCount(); 078 long gcTime = gc.getCollectionTime(); 079 080 long lastGCCount = 0L; 081 long lastGCTime = 0L; 082 long recentGCDuration = 0L; 083 if (lastGCCounts.containsKey(gcName)) 084 { 085 lastGCCount = lastGCCounts.get(gcName); 086 lastGCTime = lastGCTimes.get(gcName); 087 recentGCDuration = recentGCDurations.get(gcName); 088 } 089 090 if (gcCount > lastGCCount) 091 { 092 long recentGCCount = gcCount - lastGCCount; 093 long recentGCTime = gcTime - lastGCTime; 094 recentGCDuration = recentGCTime / recentGCCount; 095 } 096 097 lastGCCounts.put(gcName, gcCount); 098 lastGCTimes.put(gcName, gcTime); 099 recentGCDurations.put(gcName, recentGCDuration); 100 } 101 } 102 103 104 105 @Override 106 public MonitorData getMonitorData() 107 { 108 MonitorData attrs = new MonitorData(); 109 110 for (GarbageCollectorMXBean gc : 111 ManagementFactory.getGarbageCollectorMXBeans()) 112 { 113 String gcName = gc.getName(); 114 long gcCount = gc.getCollectionCount(); 115 long gcTime = gc.getCollectionTime(); 116 117 long avgGCDuration = 0L; 118 if (gcCount > 0) 119 { 120 avgGCDuration = gcTime / gcCount; 121 } 122 123 long recentGCDuration = 0L; 124 if (recentGCDurations.containsKey(gcName)) 125 { 126 recentGCDuration = recentGCDurations.get(gcName); 127 } 128 129 String safeName = gcSafeNames.get(gcName); 130 if (safeName == null) 131 { 132 safeName = generateSafeName(gcName); 133 gcSafeNames.put(gcName, safeName); 134 } 135 136 attrs.add(safeName + "-total-collection-count", gcCount); 137 attrs.add(safeName + "-total-collection-duration", gcTime); 138 attrs.add(safeName + "-average-collection-duration", avgGCDuration); 139 attrs.add(safeName + "-recent-collection-duration", recentGCDuration); 140 } 141 142 for (MemoryPoolMXBean mp : ManagementFactory.getMemoryPoolMXBeans()) 143 { 144 String poolName = mp.getName(); 145 MemoryUsage currentUsage = mp.getUsage(); 146 MemoryUsage collectionUsage = mp.getCollectionUsage(); 147 148 String safeName = gcSafeNames.get(poolName); 149 if (safeName == null) 150 { 151 safeName = generateSafeName(poolName); 152 gcSafeNames.put(poolName, safeName); 153 } 154 155 long currentBytesUsed = currentUsage != null ? currentUsage.getUsed() : 0; 156 attrs.add(safeName + "-current-bytes-used", currentBytesUsed); 157 158 long collectionBytesUsed = collectionUsage != null ? collectionUsage.getUsed() : 0; 159 attrs.add(safeName + "-bytes-used-after-last-collection", collectionBytesUsed); 160 } 161 162 return attrs; 163 } 164 165 /** 166 * Creates a "safe" version of the provided name, which is acceptable for 167 * use as part of an attribute name. 168 * 169 * @param name The name for which to obtain the safe name. 170 * 171 * @return The calculated safe name. 172 */ 173 private String generateSafeName(String name) 174 { 175 StringBuilder buffer = new StringBuilder(); 176 boolean lastWasUppercase = false; 177 boolean lastWasDash = false; 178 for (int i=0; i < name.length(); i++) 179 { 180 char c = name.charAt(i); 181 if (Character.isLetter(c)) 182 { 183 if (Character.isUpperCase(c)) 184 { 185 char lowerCaseCharacter = Character.toLowerCase(c); 186 if (buffer.length() > 0 && !lastWasUppercase && !lastWasDash) 187 { 188 buffer.append('-'); 189 } 190 191 buffer.append(lowerCaseCharacter); 192 lastWasUppercase = true; 193 lastWasDash = false; 194 } 195 else 196 { 197 buffer.append(c); 198 lastWasUppercase = false; 199 lastWasDash = false; 200 } 201 } 202 else if (Character.isDigit(c)) 203 { 204 buffer.append(c); 205 lastWasUppercase = false; 206 lastWasDash = false; 207 } 208 else if (c == ' ' || c == '_' || c == '-') 209 { 210 if (! lastWasDash) 211 { 212 buffer.append('-'); 213 } 214 215 lastWasUppercase = false; 216 lastWasDash = true; 217 } 218 } 219 220 return buffer.toString(); 221 } 222} 223