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