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.admin.std.server.MemoryUsageMonitorProviderCfg; 030import org.opends.server.api.MonitorData; 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 public void initializeMonitorProvider( 054 MemoryUsageMonitorProviderCfg configuration) 055 throws ConfigException, InitializationException 056 { 057 scheduleUpdate(this, 0, 1, TimeUnit.SECONDS); 058 } 059 060 /** {@inheritDoc} */ 061 @Override 062 public String getMonitorInstanceName() 063 { 064 return "JVM Memory Usage"; 065 } 066 067 068 /** {@inheritDoc} */ 069 public void run() 070 { 071 for (GarbageCollectorMXBean gc : 072 ManagementFactory.getGarbageCollectorMXBeans()) 073 { 074 String gcName = gc.getName(); 075 long gcCount = gc.getCollectionCount(); 076 long gcTime = gc.getCollectionTime(); 077 078 long lastGCCount = 0L; 079 long lastGCTime = 0L; 080 long recentGCDuration = 0L; 081 if (lastGCCounts.containsKey(gcName)) 082 { 083 lastGCCount = lastGCCounts.get(gcName); 084 lastGCTime = lastGCTimes.get(gcName); 085 recentGCDuration = recentGCDurations.get(gcName); 086 } 087 088 if (gcCount > lastGCCount) 089 { 090 long recentGCCount = gcCount - lastGCCount; 091 long recentGCTime = gcTime - lastGCTime; 092 recentGCDuration = recentGCTime / recentGCCount; 093 } 094 095 lastGCCounts.put(gcName, gcCount); 096 lastGCTimes.put(gcName, gcTime); 097 recentGCDurations.put(gcName, recentGCDuration); 098 } 099 } 100 101 102 103 @Override 104 public MonitorData getMonitorData() 105 { 106 MonitorData attrs = new MonitorData(); 107 108 for (GarbageCollectorMXBean gc : 109 ManagementFactory.getGarbageCollectorMXBeans()) 110 { 111 String gcName = gc.getName(); 112 long gcCount = gc.getCollectionCount(); 113 long gcTime = gc.getCollectionTime(); 114 115 long avgGCDuration = 0L; 116 if (gcCount > 0) 117 { 118 avgGCDuration = gcTime / gcCount; 119 } 120 121 long recentGCDuration = 0L; 122 if (recentGCDurations.containsKey(gcName)) 123 { 124 recentGCDuration = recentGCDurations.get(gcName); 125 } 126 127 String safeName = gcSafeNames.get(gcName); 128 if (safeName == null) 129 { 130 safeName = generateSafeName(gcName); 131 gcSafeNames.put(gcName, safeName); 132 } 133 134 attrs.add(safeName + "-total-collection-count", gcCount); 135 attrs.add(safeName + "-total-collection-duration", gcTime); 136 attrs.add(safeName + "-average-collection-duration", avgGCDuration); 137 attrs.add(safeName + "-recent-collection-duration", recentGCDuration); 138 } 139 140 for (MemoryPoolMXBean mp : ManagementFactory.getMemoryPoolMXBeans()) 141 { 142 String poolName = mp.getName(); 143 MemoryUsage currentUsage = mp.getUsage(); 144 MemoryUsage collectionUsage = mp.getCollectionUsage(); 145 146 String safeName = gcSafeNames.get(poolName); 147 if (safeName == null) 148 { 149 safeName = generateSafeName(poolName); 150 gcSafeNames.put(poolName, safeName); 151 } 152 153 long currentBytesUsed = currentUsage != null ? currentUsage.getUsed() : 0; 154 attrs.add(safeName + "-current-bytes-used", currentBytesUsed); 155 156 long collectionBytesUsed = collectionUsage != null ? collectionUsage.getUsed() : 0; 157 attrs.add(safeName + "-bytes-used-after-last-collection", collectionBytesUsed); 158 } 159 160 return attrs; 161 } 162 163 /** 164 * Creates a "safe" version of the provided name, which is acceptable for 165 * use as part of an attribute name. 166 * 167 * @param name The name for which to obtain the safe name. 168 * 169 * @return The calculated safe name. 170 */ 171 private String generateSafeName(String name) 172 { 173 StringBuilder buffer = new StringBuilder(); 174 boolean lastWasUppercase = false; 175 boolean lastWasDash = false; 176 for (int i=0; i < name.length(); i++) 177 { 178 char c = name.charAt(i); 179 if (Character.isLetter(c)) 180 { 181 if (Character.isUpperCase(c)) 182 { 183 char lowerCaseCharacter = Character.toLowerCase(c); 184 if (buffer.length() > 0 && !lastWasUppercase && !lastWasDash) 185 { 186 buffer.append('-'); 187 } 188 189 buffer.append(lowerCaseCharacter); 190 lastWasUppercase = true; 191 lastWasDash = false; 192 } 193 else 194 { 195 buffer.append(c); 196 lastWasUppercase = false; 197 lastWasDash = false; 198 } 199 } 200 else if (Character.isDigit(c)) 201 { 202 buffer.append(c); 203 lastWasUppercase = false; 204 lastWasDash = false; 205 } 206 else if (c == ' ' || c == '_' || c == '-') 207 { 208 if (! lastWasDash) 209 { 210 buffer.append('-'); 211 } 212 213 lastWasUppercase = false; 214 lastWasDash = true; 215 } 216 } 217 218 return buffer.toString(); 219 } 220} 221