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.List; 038import java.util.concurrent.TimeUnit; 039 040import org.forgerock.opendj.config.server.ConfigException; 041import org.opends.server.admin.std.server.MemoryUsageMonitorProviderCfg; 042import org.opends.server.api.MonitorProvider; 043import org.opends.server.types.Attribute; 044import org.opends.server.types.Attributes; 045import org.opends.server.types.InitializationException; 046 047/** 048 * This class defines a monitor provider that reports information about 049 * Directory Server memory usage. 050 */ 051public class MemoryUsageMonitorProvider 052 extends MonitorProvider<MemoryUsageMonitorProviderCfg> 053 implements Runnable 054{ 055 /** A map of the last GC counts seen by this monitor for calculating recent stats. */ 056 private HashMap<String,Long> lastGCCounts = new HashMap<>(); 057 /** A map of the last GC times seen by this monitor for calculating recent stats. */ 058 private HashMap<String,Long> lastGCTimes = new HashMap<>(); 059 /** A map of the most recent GC durations seen by this monitor. */ 060 private HashMap<String,Long> recentGCDurations = new HashMap<>(); 061 /** A map of the memory manager names to names that are safe for use in attribute names. */ 062 private HashMap<String,String> gcSafeNames = new HashMap<>(); 063 064 065 /** {@inheritDoc} */ 066 public void initializeMonitorProvider( 067 MemoryUsageMonitorProviderCfg configuration) 068 throws ConfigException, InitializationException 069 { 070 scheduleUpdate(this, 0, 1, TimeUnit.SECONDS); 071 } 072 073 /** {@inheritDoc} */ 074 @Override 075 public String getMonitorInstanceName() 076 { 077 return "JVM Memory Usage"; 078 } 079 080 081 /** {@inheritDoc} */ 082 public void run() 083 { 084 for (GarbageCollectorMXBean gc : 085 ManagementFactory.getGarbageCollectorMXBeans()) 086 { 087 String gcName = gc.getName(); 088 long gcCount = gc.getCollectionCount(); 089 long gcTime = gc.getCollectionTime(); 090 091 long lastGCCount = 0L; 092 long lastGCTime = 0L; 093 long recentGCDuration = 0L; 094 if (lastGCCounts.containsKey(gcName)) 095 { 096 lastGCCount = lastGCCounts.get(gcName); 097 lastGCTime = lastGCTimes.get(gcName); 098 recentGCDuration = recentGCDurations.get(gcName); 099 } 100 101 if (gcCount > lastGCCount) 102 { 103 long recentGCCount = gcCount - lastGCCount; 104 long recentGCTime = gcTime - lastGCTime; 105 recentGCDuration = recentGCTime / recentGCCount; 106 } 107 108 lastGCCounts.put(gcName, gcCount); 109 lastGCTimes.put(gcName, gcTime); 110 recentGCDurations.put(gcName, recentGCDuration); 111 } 112 } 113 114 115 116 @Override 117 public List<Attribute> getMonitorData() 118 { 119 ArrayList<Attribute> attrs = new ArrayList<>(); 120 121 for (GarbageCollectorMXBean gc : 122 ManagementFactory.getGarbageCollectorMXBeans()) 123 { 124 String gcName = gc.getName(); 125 long gcCount = gc.getCollectionCount(); 126 long gcTime = gc.getCollectionTime(); 127 128 long avgGCDuration = 0L; 129 if (gcCount > 0) 130 { 131 avgGCDuration = gcTime / gcCount; 132 } 133 134 long recentGCDuration = 0L; 135 if (recentGCDurations.containsKey(gcName)) 136 { 137 recentGCDuration = recentGCDurations.get(gcName); 138 } 139 140 String safeName = gcSafeNames.get(gcName); 141 if (safeName == null) 142 { 143 safeName = generateSafeName(gcName); 144 gcSafeNames.put(gcName, safeName); 145 } 146 147 attrs.add(createAttribute(safeName + "-total-collection-count", gcCount)); 148 attrs.add(createAttribute(safeName + "-total-collection-duration", gcTime)); 149 attrs.add(createAttribute(safeName + "-average-collection-duration", avgGCDuration)); 150 attrs.add(createAttribute(safeName + "-recent-collection-duration", recentGCDuration)); 151 } 152 153 for (MemoryPoolMXBean mp : ManagementFactory.getMemoryPoolMXBeans()) 154 { 155 String poolName = mp.getName(); 156 MemoryUsage currentUsage = mp.getUsage(); 157 MemoryUsage collectionUsage = mp.getCollectionUsage(); 158 159 String safeName = gcSafeNames.get(poolName); 160 if (safeName == null) 161 { 162 safeName = generateSafeName(poolName); 163 gcSafeNames.put(poolName, safeName); 164 } 165 166 long currentBytesUsed = currentUsage != null ? currentUsage.getUsed() : 0; 167 attrs.add(createAttribute(safeName + "-current-bytes-used", currentBytesUsed)); 168 169 long collectionBytesUsed = collectionUsage != null ? collectionUsage.getUsed() : 0; 170 attrs.add(createAttribute(safeName + "-bytes-used-after-last-collection", collectionBytesUsed)); 171 } 172 173 return attrs; 174 } 175 176 private Attribute createAttribute(String name, Object value) 177 { 178 return Attributes.create(name, String.valueOf(value)); 179 } 180 181 182 183 /** 184 * Creates a "safe" version of the provided name, which is acceptable for 185 * use as part of an attribute name. 186 * 187 * @param name The name for which to obtain the safe name. 188 * 189 * @return The calculated safe name. 190 */ 191 private String generateSafeName(String name) 192 { 193 StringBuilder buffer = new StringBuilder(); 194 boolean lastWasUppercase = false; 195 boolean lastWasDash = false; 196 for (int i=0; i < name.length(); i++) 197 { 198 char c = name.charAt(i); 199 if (Character.isLetter(c)) 200 { 201 if (Character.isUpperCase(c)) 202 { 203 char lowerCaseCharacter = Character.toLowerCase(c); 204 if (buffer.length() > 0 && !lastWasUppercase && !lastWasDash) 205 { 206 buffer.append('-'); 207 } 208 209 buffer.append(lowerCaseCharacter); 210 lastWasUppercase = true; 211 lastWasDash = false; 212 } 213 else 214 { 215 buffer.append(c); 216 lastWasUppercase = false; 217 lastWasDash = false; 218 } 219 } 220 else if (Character.isDigit(c)) 221 { 222 buffer.append(c); 223 lastWasUppercase = false; 224 lastWasDash = false; 225 } 226 else if (c == ' ' || c == '_' || c == '-') 227 { 228 if (! lastWasDash) 229 { 230 buffer.append('-'); 231 } 232 233 lastWasUppercase = false; 234 lastWasDash = true; 235 } 236 } 237 238 return buffer.toString(); 239 } 240} 241