1 /*
2  * Copyright 2008-2019 by Emeric Vernat
3  *
4  *     This file is part of Java Melody.
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */

18 package net.bull.javamelody.internal.model;
19
20 import java.io.Serializable;
21 import java.lang.management.GarbageCollectorMXBean;
22 import java.lang.management.ManagementFactory;
23 import java.lang.management.MemoryPoolMXBean;
24 import java.lang.management.MemoryUsage;
25 import java.text.DecimalFormat;
26
27 import net.bull.javamelody.internal.common.I18N;
28
29 /**
30  * Informations systèmes sur la mémoire du serveur, sans code html de présentation.
31  * L'état d'une instance est initialisé à son instanciation et non mutable;
32  * il est donc de fait thread-safe.
33  * Cet état est celui d'une instance de JVM java.
34  * Les instances sont sérialisables pour pouvoir être transmises au serveur de collecte.
35  * @author Emeric Vernat
36  */

37 public class MemoryInformations implements Serializable {
38     private static final long serialVersionUID = 3281861236369720876L;
39     private static final String NEXT = ",\n";
40     private static final String MO = " Mo";
41
42     // usedMemory est la mémoire utilisée du heap (voir aussi non heap dans gestion mémoire)
43     private final long usedMemory;
44     // maxMemory est la mémoire maximum pour le heap (paramètre -Xmx1024m par exemple)
45     private final long maxMemory;
46     // usedPermGen est la mémoire utilisée de "Perm Gen" (classes et les instances de String "interned")
47     private final long usedPermGen;
48     // maxPermGen est la mémoire maximum pour "Perm Gen" (paramètre -XX:MaxPermSize=128m par exemple)
49     private final long maxPermGen;
50     private final long usedNonHeapMemory;
51     private final long usedBufferedMemory;
52     private final int loadedClassesCount;
53     private final long garbageCollectionTimeMillis;
54     private final long usedPhysicalMemorySize;
55     private final long usedSwapSpaceSize;
56     private final String memoryDetails;
57
58     MemoryInformations() {
59         super();
60         usedMemory = Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
61         maxMemory = Runtime.getRuntime().maxMemory();
62         final MemoryPoolMXBean permGenMemoryPool = getPermGenMemoryPool();
63         if (permGenMemoryPool != null) {
64             final MemoryUsage usage = permGenMemoryPool.getUsage();
65             usedPermGen = usage.getUsed();
66             maxPermGen = usage.getMax();
67         } else {
68             usedPermGen = -1;
69             maxPermGen = -1;
70         }
71         usedNonHeapMemory = ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage().getUsed();
72         usedBufferedMemory = MBeansAccessor.getUsedBufferMemory();
73         loadedClassesCount = ManagementFactory.getClassLoadingMXBean().getLoadedClassCount();
74         garbageCollectionTimeMillis = buildGarbageCollectionTimeMillis();
75
76         usedPhysicalMemorySize = MBeansAccessor
77                 .getLongFromOperatingSystem("TotalPhysicalMemorySize")
78                 - MBeansAccessor.getLongFromOperatingSystem("FreePhysicalMemorySize");
79         usedSwapSpaceSize = buildUsedSwapSpaceSize();
80
81         memoryDetails = buildMemoryDetails();
82     }
83
84     private long buildUsedSwapSpaceSize() {
85         try {
86             return MBeansAccessor.getLongFromOperatingSystem("TotalSwapSpaceSize")
87                     - MBeansAccessor.getLongFromOperatingSystem("FreeSwapSpaceSize");
88         } catch (final Throwable e) { // NOPMD
89             // pour issues 794, dans google appengine
90             // javax.management.RuntimeErrorException: java.lang.InternalError: errno: 38 error: sysinfo failed to get swap size
91             return -1;
92         }
93     }
94
95     private static MemoryPoolMXBean getPermGenMemoryPool() {
96         for (final MemoryPoolMXBean memoryPool : ManagementFactory.getMemoryPoolMXBeans()) {
97             // name est "Perm Gen" ou "PS Perm Gen" (32 vs 64 bits ?)
98             if (memoryPool.getName().endsWith("Perm Gen")) {
99                 return memoryPool;
100             }
101         }
102         return null;
103     }
104
105     private static long buildGarbageCollectionTimeMillis() {
106         long garbageCollectionTime = 0;
107         for (final GarbageCollectorMXBean garbageCollector : ManagementFactory
108                 .getGarbageCollectorMXBeans()) {
109             garbageCollectionTime += garbageCollector.getCollectionTime();
110         }
111         return garbageCollectionTime;
112     }
113
114     private String buildMemoryDetails() {
115         final DecimalFormat integerFormat = I18N.createIntegerFormat();
116         final String nonHeapMemory = "Non heap memory = "
117                 + integerFormat.format(usedNonHeapMemory / 1024 / 1024) + MO
118                 + " (Perm Gen, Code Cache)";
119         // classes actuellement chargées
120         final String classLoading = "Loaded classes = " + integerFormat.format(loadedClassesCount);
121         final String gc = "Garbage collection time = "
122                 + integerFormat.format(garbageCollectionTimeMillis) + " ms";
123         String osInfo = "";
124         String osInfo2 = "";
125         final long totalPhysicalMemorySize = MBeansAccessor
126                 .getLongFromOperatingSystem("TotalPhysicalMemorySize");
127         if (totalPhysicalMemorySize >= 0) {
128             osInfo = "Process cpu time = "
129                     + integerFormat.format(
130                             MBeansAccessor.getLongFromOperatingSystem("ProcessCpuTime") / 1000000)
131                     + " ms,\nCommitted virtual memory = "
132                     + integerFormat.format(
133                             MBeansAccessor.getLongFromOperatingSystem("CommittedVirtualMemorySize")
134                                     / 1024 / 1024)
135                     + MO + ",\nFree physical memory = "
136                     + integerFormat.format(
137                             MBeansAccessor.getLongFromOperatingSystem("FreePhysicalMemorySize")
138                                     / 1024 / 1024)
139                     + MO + ",\nTotal physical memory = "
140                     + integerFormat.format(totalPhysicalMemorySize / 1024 / 1024) + MO;
141             try {
142                 osInfo2 = ",\nFree swap space = "
143                         + integerFormat.format(
144                                 MBeansAccessor.getLongFromOperatingSystem("FreeSwapSpaceSize")
145                                         / 1024 / 1024)
146                         + MO + ",\nTotal swap space = "
147                         + integerFormat.format(
148                                 MBeansAccessor.getLongFromOperatingSystem("TotalSwapSpaceSize")
149                                         / 1024 / 1024)
150                         + MO;
151             } catch (final Throwable e) { // NOPMD
152                 // pour issues 794, dans google appengine
153                 // javax.management.RuntimeErrorException: java.lang.InternalError: errno: 38 error: sysinfo failed to get swap size
154             }
155         }
156         if (usedBufferedMemory < 0) {
157             return nonHeapMemory + NEXT + classLoading + NEXT + gc + NEXT + osInfo + osInfo2;
158         }
159         final String bufferedMemory = "Buffered memory = "
160                 + integerFormat.format(usedBufferedMemory / 1024 / 1024) + MO;
161         return nonHeapMemory + NEXT + bufferedMemory + NEXT + classLoading + NEXT + gc + NEXT
162                 + osInfo;
163     }
164
165     public long getUsedMemory() {
166         return usedMemory;
167     }
168
169     public long getMaxMemory() {
170         return maxMemory;
171     }
172
173     public double getUsedMemoryPercentage() {
174         return 100d * usedMemory / maxMemory;
175     }
176
177     public long getUsedPermGen() {
178         return usedPermGen;
179     }
180
181     public long getMaxPermGen() {
182         return maxPermGen;
183     }
184
185     public double getUsedPermGenPercentage() {
186         if (usedPermGen > 0 && maxPermGen > 0) {
187             return 100d * usedPermGen / maxPermGen;
188         }
189         return -1d;
190     }
191
192     public long getUsedNonHeapMemory() {
193         return usedNonHeapMemory;
194     }
195
196     public long getUsedBufferedMemory() {
197         return usedBufferedMemory;
198     }
199
200     public int getLoadedClassesCount() {
201         return loadedClassesCount;
202     }
203
204     public long getGarbageCollectionTimeMillis() {
205         return garbageCollectionTimeMillis;
206     }
207
208     public long getUsedPhysicalMemorySize() {
209         return usedPhysicalMemorySize;
210     }
211
212     public long getUsedSwapSpaceSize() {
213         return usedSwapSpaceSize;
214     }
215
216     public String getMemoryDetails() {
217         return memoryDetails;
218     }
219
220     /** {@inheritDoc} */
221     @Override
222     public String toString() {
223         return getClass().getSimpleName() + "[usedMemory=" + getUsedMemory() + ", maxMemory="
224                 + getMaxMemory() + ']';
225     }
226 }
227