/*
* Copyright 2008-2014 by Emeric Vernat
*
* This file is part of Java Melody.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.bull.javamelody;
import java.io.Serializable;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;
import java.lang.management.OperatingSystemMXBean;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.DecimalFormat;
/**
* Informations systèmes sur la mémoire du serveur, sans code html de présentation.
* L'état d'une instance est initialisé à son instanciation et non mutable;
* il est donc de fait thread-safe.
* Cet état est celui d'une instance de JVM java.
* Les instances sont sérialisables pour pouvoir être transmises au serveur de collecte.
* @author Emeric Vernat
*/
class MemoryInformations implements Serializable {
private static final long serialVersionUID = 3281861236369720876L;
private static final String NEXT = ",\n";
private static final String MO = " Mo";
// usedMemory est la mémoire utilisée du heap (voir aussi non heap dans gestion mémoire)
private final long usedMemory;
// maxMemory est la mémoire maximum pour le heap (paramètre -Xmx1024m par exemple)
private final long maxMemory;
// usedPermGen est la mémoire utilisée de "Perm Gen" (classes et les instances de String "interned")
private final long usedPermGen;
// maxPermGen est la mémoire maximum pour "Perm Gen" (paramètre -XX:MaxPermSize=128m par exemple)
private final long maxPermGen;
private final long usedNonHeapMemory;
private final int loadedClassesCount;
private final long garbageCollectionTimeMillis;
private final long usedPhysicalMemorySize;
private final long usedSwapSpaceSize;
private final String memoryDetails;
MemoryInformations() {
super();
usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
maxMemory = Runtime.getRuntime().maxMemory();
final MemoryPoolMXBean permGenMemoryPool = getPermGenMemoryPool();
if (permGenMemoryPool != null) {
final MemoryUsage usage = permGenMemoryPool.getUsage();
usedPermGen = usage.getUsed();
maxPermGen = usage.getMax();
} else {
usedPermGen = -1;
maxPermGen = -1;
}
usedNonHeapMemory = ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage().getUsed();
loadedClassesCount = ManagementFactory.getClassLoadingMXBean().getLoadedClassCount();
garbageCollectionTimeMillis = buildGarbageCollectionTimeMillis();
final OperatingSystemMXBean operatingSystem = ManagementFactory.getOperatingSystemMXBean();
if (isSunOsMBean(operatingSystem)) {
usedPhysicalMemorySize = getLongFromOperatingSystem(operatingSystem,
"getTotalPhysicalMemorySize")
- getLongFromOperatingSystem(operatingSystem, "getFreePhysicalMemorySize");
usedSwapSpaceSize = getLongFromOperatingSystem(operatingSystem, "getTotalSwapSpaceSize")
- getLongFromOperatingSystem(operatingSystem, "getFreeSwapSpaceSize");
} else {
usedPhysicalMemorySize = -1;
usedSwapSpaceSize = -1;
}
memoryDetails = buildMemoryDetails();
}
private static MemoryPoolMXBean getPermGenMemoryPool() {
for (final MemoryPoolMXBean memoryPool : ManagementFactory.getMemoryPoolMXBeans()) {
// name est "Perm Gen" ou "PS Perm Gen" (32 vs 64 bits ?)
if (memoryPool.getName().endsWith("Perm Gen")) {
return memoryPool;
}
}
return null;
}
private static long buildGarbageCollectionTimeMillis() {
long garbageCollectionTime = 0;
for (final GarbageCollectorMXBean garbageCollector : ManagementFactory
.getGarbageCollectorMXBeans()) {
garbageCollectionTime += garbageCollector.getCollectionTime();
}
return garbageCollectionTime;
}
private String buildMemoryDetails() {
final DecimalFormat integerFormat = I18N.createIntegerFormat();
final String nonHeapMemory = "Non heap memory = "
+ integerFormat.format(usedNonHeapMemory / 1024 / 1024) + MO
+ " (Perm Gen, Code Cache)";
// classes actuellement chargées
final String classLoading = "Loaded classes = " + integerFormat.format(loadedClassesCount);
final String gc = "Garbage collection time = "
+ integerFormat.format(garbageCollectionTimeMillis) + " ms";
final OperatingSystemMXBean operatingSystem = ManagementFactory.getOperatingSystemMXBean();
String osInfo = "";
if (isSunOsMBean(operatingSystem)) {
osInfo = "Process cpu time = "
+ integerFormat.format(getLongFromOperatingSystem(operatingSystem,
"getProcessCpuTime") / 1000000)
+ " ms,\nCommitted virtual memory = "
+ integerFormat.format(getLongFromOperatingSystem(operatingSystem,
"getCommittedVirtualMemorySize") / 1024 / 1024)
+ MO
+ ",\nFree physical memory = "
+ integerFormat.format(getLongFromOperatingSystem(operatingSystem,
"getFreePhysicalMemorySize") / 1024 / 1024)
+ MO
+ ",\nTotal physical memory = "
+ integerFormat.format(getLongFromOperatingSystem(operatingSystem,
"getTotalPhysicalMemorySize") / 1024 / 1024)
+ MO
+ ",\nFree swap space = "
+ integerFormat.format(getLongFromOperatingSystem(operatingSystem,
"getFreeSwapSpaceSize") / 1024 / 1024)
+ MO
+ ",\nTotal swap space = "
+ integerFormat.format(getLongFromOperatingSystem(operatingSystem,
"getTotalSwapSpaceSize") / 1024 / 1024) + MO;
}
return nonHeapMemory + NEXT + classLoading + NEXT + gc + NEXT + osInfo;
}
private static boolean isSunOsMBean(OperatingSystemMXBean operatingSystem) {
// on ne teste pas operatingSystem instanceof com.sun.management.OperatingSystemMXBean
// car le package com.sun n'existe à priori pas sur une jvm tierce
final String className = operatingSystem.getClass().getName();
return "com.sun.management.OperatingSystem".equals(className)
|| "com.sun.management.UnixOperatingSystem".equals(className)
// sun.management.OperatingSystemImpl pour java 8
|| "sun.management.OperatingSystemImpl".equals(className);
}
static long getLongFromOperatingSystem(OperatingSystemMXBean operatingSystem, String methodName) {
try {
final Method method = operatingSystem.getClass().getMethod(methodName,
(Class<?>[]) null);
method.setAccessible(true);
return (Long) method.invoke(operatingSystem, (Object[]) null);
} catch (final InvocationTargetException e) {
if (e.getCause() instanceof Error) {
throw (Error) e.getCause();
} else if (e.getCause() instanceof RuntimeException) {
throw (RuntimeException) e.getCause();
}
throw new IllegalStateException(e.getCause());
} catch (final NoSuchMethodException e) {
throw new IllegalArgumentException(e);
} catch (final IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
long getUsedMemory() {
return usedMemory;
}
long getMaxMemory() {
return maxMemory;
}
double getUsedMemoryPercentage() {
return 100d * usedMemory / maxMemory;
}
long getUsedPermGen() {
return usedPermGen;
}
long getMaxPermGen() {
return maxPermGen;
}
double getUsedPermGenPercentage() {
if (usedPermGen > 0 && maxPermGen > 0) {
return 100d * usedPermGen / maxPermGen;
}
return -1d;
}
long getUsedNonHeapMemory() {
return usedNonHeapMemory;
}
int getLoadedClassesCount() {
return loadedClassesCount;
}
long getGarbageCollectionTimeMillis() {
return garbageCollectionTimeMillis;
}
long getUsedPhysicalMemorySize() {
return usedPhysicalMemorySize;
}
long getUsedSwapSpaceSize() {
return usedSwapSpaceSize;
}
String getMemoryDetails() {
return memoryDetails;
}
/** {@inheritDoc} */
@Override
public String toString() {
return getClass().getSimpleName() + "[usedMemory=" + getUsedMemory() + ", maxMemory="
+ getMaxMemory() + ']';
}
}