Il existe des questions similaires, mais elles semblent éviter de répondre à cette question spécifique. Comment obtenir la mémoire utilisée par mon programme Java via l'API Runtime de Java?
La réponse ici indique que je peux faire quelque chose comme ceci:
System.out.println("KB: " + (double) (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024);
Mais cela retourne toujours le même nombre, quel que soit le programme que je lance. Par exemple, ci-dessous, j'ai un programme dans lequel, peu importe le nombre de chiffres que j'ai mis dans la carte, l'utilisation de la mémoire reste la même.
package memoryTest;
import Java.util.HashMap;
import Java.util.Map;
public class MemoryTest {
static Map<Integer, NewObject> map = new HashMap<Integer, NewObject>();
public static void main(String[] args){
System.out.println("KB: " + (double) (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024);
fillMemory(25);
System.out.println("KB: " + (double) (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024);
}
static int j=0;
public static void fillMemory(int i){
for(int k=0; k< 2000; k++)
map.put(j++, new NewObject());
}
public static class NewObject{
long i = 0L;
long j = 0L;
long k = 0L;
}
}
via la méthode principale de cambecc, la sortie est:
Vous le faites correctement. Le moyen d'obtenir l'utilisation de la mémoire est exactement comme vous l'avez décrit:
Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()
Mais la raison pour laquelle votre programme retourne toujours la même utilisation de la mémoire est que vous ne créez pas assez d'objets pour dépasser les limitations de précision de la méthode freeMemory
. Bien qu'il ait un octet resolution, rien ne garantit que precisefreeMemory
doit être. Le javadoc en dit autant:
une approximation de la quantité totale de mémoire actuellement disponible pour les futurs objets alloués, mesurée en octets.
Essayez ce qui suit, qui crée deux instances millionNewObject
et affiche chaque fois que le résultat de freeMemory
change:
public static void main(String[] args) {
Runtime rt = Runtime.getRuntime();
long prevTotal = 0;
long prevFree = rt.freeMemory();
for (int i = 0; i < 2_000_000; i++) {
long total = rt.totalMemory();
long free = rt.freeMemory();
if (total != prevTotal || free != prevFree) {
System.out.println(
String.format("#%s, Total: %s, Free: %s, Diff: %s",
i,
total,
free,
prevFree - free));
prevTotal = total;
prevFree = free;
}
map.put(i, new NewObject());
}
}
Sur ma machine, je vois la sortie comme suit
#0, Total: 513998848, Free: 508635256, Diff: 0
#21437, Total: 513998848, Free: 505953496, Diff: 2681760
#48905, Total: 513998848, Free: 503271728, Diff: 2681768
#73394, Total: 513998848, Free: 500589960, Diff: 2681768
#103841, Total: 513998848, Free: 497908192, Diff: 2681768
...
Remarquez comment la mémoire libre signalée n'a pas changé jusqu'à ce que le 21 437e objet ait été instancié? Les chiffres suggèrent que freeMemory
pour la machine virtuelle que j'utilise (Java7 Win 64 bits) a une précision d'un peu plus de 2,5 Mo (bien que si vous exécutez l'expérience, vous verrez que ce nombre varie).
Ce code est le même que ci-dessus, mais affiche plus de détails sur l'utilisation de la mémoire. J'espère que la façon dont la JVM utilise la mémoire se comporte un peu plus clairement. Nous allouons continuellement de nouveaux objets dans une boucle. Lors de chaque itération, si totalMemory
ou freeMemory
est identique à la dernière itération, nous n'imprimons rien. Mais si l'un ou l'autre a changé, nous rapportons l'utilisation actuelle de la mémoire. Les valeurs ∆
représentent la différence entre l'utilisation actuelle et le rapport de mémoire précédent.
public static void main(String[] args) {
Runtime rt = Runtime.getRuntime();
long prevTotal = 0;
long prevFree = rt.freeMemory();
for (int i = 0; i < 2_000_000; i++) {
long total = rt.totalMemory();
long free = rt.freeMemory();
if (total != prevTotal || free != prevFree) {
long used = total - free;
long prevUsed = (prevTotal - prevFree);
System.out.println(
"#" + i +
", Total: " + total +
", Used: " + used +
", ∆Used: " + (used - prevUsed) +
", Free: " + free +
", ∆Free: " + (free - prevFree));
prevTotal = total;
prevFree = free;
}
map.put(i, new NewObject());
}
}
Sur mon cahier, je vois la sortie suivante. Notez que vos résultats seront différents selon le système d'exploitation, le matériel, la mise en œuvre JVM, etc.:
#0, Total: 83427328, Used: 1741048, ∆Used: 83427328, Free: 81686280, ∆Free: 0
#3228, Total: 83427328, Used: 1741080, ∆Used: 32, Free: 81686248, ∆Free: -32
#3229, Total: 83427328, Used: 2176280, ∆Used: 435200, Free: 81251048, ∆Free: -435200
#7777, Total: 83427328, Used: 2176312, ∆Used: 32, Free: 81251016, ∆Free: -32
#7778, Total: 83427328, Used: 2611536, ∆Used: 435224, Free: 80815792, ∆Free: -435224
...
#415056, Total: 83427328, Used: 41517072, ∆Used: 407920, Free: 41910256, ∆Free: -407920
#419680, Total: 145358848, Used: 39477560, ∆Used: -2039512, Free: 105881288, ∆Free: 63971032
#419681, Total: 145358848, Used: 40283832, ∆Used: 806272, Free: 105075016, ∆Free: -806272
...
Il y a quelques observations à partir de ces données:
J'ai les méthodes suivantes
public static long getMaxMemory() {
return Runtime.getRuntime().maxMemory();
}
public static long getUsedMemory() {
return getMaxMemory() - getFreeMemory();
}
public static long getTotalMemory() {
return Runtime.getRuntime().totalMemory();
}
public static long getFreeMemory() {
return Runtime.getRuntime().freeMemory();
}
qui retournent la mémoire (utilisée) en octets.
Si vous voulez recalculer en MiB j'ai:
private static final long MEGABYTE_FACTOR = 1024L * 1024L;
private static final DecimalFormat ROUNDED_DOUBLE_DECIMALFORMAT;
private static final String MIB = "MiB";
static {
DecimalFormatSymbols otherSymbols = new DecimalFormatSymbols(Locale.ENGLISH);
otherSymbols.setDecimalSeparator('.');
otherSymbols.setGroupingSeparator(',');
ROUNDED_DOUBLE_DECIMALFORMAT = new DecimalFormat("####0.00", otherSymbols);
ROUNDED_DOUBLE_DECIMALFORMAT.setGroupingUsed(false);
}
public static String getTotalMemoryInMiB() {
double totalMiB = bytesToMiB(getTotalMemory());
return String.format("%s %s", ROUNDED_DOUBLE_DECIMALFORMAT.format(totalMiB), MIB);
}
public static String getFreeMemoryInMiB() {
double freeMiB = bytesToMiB(getFreeMemory());
return String.format("%s %s", ROUNDED_DOUBLE_DECIMALFORMAT.format(freeMiB), MIB);
}
public static String getUsedMemoryInMiB() {
double usedMiB = bytesToMiB(getUsedMemory());
return String.format("%s %s", ROUNDED_DOUBLE_DECIMALFORMAT.format(usedMiB), MIB);
}
public static String getMaxMemoryInMiB() {
double maxMiB = bytesToMiB(getMaxMemory());
return String.format("%s %s", ROUNDED_DOUBLE_DECIMALFORMAT.format(maxMiB), MIB);
}
public static double getPercentageUsed() {
return ((double) getUsedMemory() / getMaxMemory()) * 100;
}
public static String getPercentageUsedFormatted() {
double usedPercentage = getPercentageUsed();
return ROUNDED_DOUBLE_DECIMALFORMAT.format(usedPercentage) + "%";
}