it-swarm.com.de

Wie kann ich den Speicher, den mein Java-Programm verwendet, über die Runtime-API von Java beziehen?

Es gibt ähnliche Fragen, aber offenbar vermeiden sie es, diese spezifische Frage zu beantworten. Wie bekomme ich den Speicher, den mein Java-Programm über die Runtime-API von Java verwendet?

Die Antwort hier zeigt an, dass ich so etwas tun kann:

System.out.println("KB: " + (double) (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1024);

Dies gibt jedoch immer die gleiche Nummer zurück, egal welches Programm ich ausführt. Zum Beispiel habe ich unten ein Programm, bei dem, egal wie viele Zahlen ich in die Karte eingebe, der Speicherbedarf gleich bleibt.

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;
    }

}

Über die Hauptmethode von cambecc wird ausgegeben:

3085, gesamt: 128516096, frei: 127173744, diff: 671120

173579, gesamt: 128516096, frei: 110033976, diff: 671128

335207, gesamt: 128516096, frei: 92417792, diff: 637544

672788, gesamt: 224198656, frei: 159302960, diff: 1221520

1171480, gesamt: 224198656, frei: 106939136, diff: 1221544

1489771, gesamt: 368377856, frei: 227374816, diff: 1212984

1998743, gesamt: 368377856, frei: 182494408, diff: 1212984

22
Matt

Du machst es richtig. Die Speicherbelegung ist genau so, wie Sie es beschrieben haben:

Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()

Der Grund, warum Ihr Programm immer die gleiche Speichernutzung zurückgibt, ist, dass Sie nicht genügend Objekte erstellen, um die Genauigkeitsbeschränkungen der freeMemory-Methode zu überwinden. Obwohl es das Byte Auflösung hat, gibt es keine Garantie dafür, wie genaufreeMemory sein muss. Der Javadoc sagt so viel:

eine Annäherung an die Gesamtspeichermenge, die derzeit für zukünftige zugewiesene Objekte zur Verfügung steht, gemessen in Byte.

Versuchen Sie Folgendes, um zwei MillionenNewObject-Instanzen zu erstellen und jedes Mal auszudrucken, wenn sich das Ergebnis von freeMemory ändert:

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());
    }
}

Auf meinem Computer sehe ich folgende Ausgabe

#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
...

Beachten Sie, dass sich der gemeldete freie Speicher erst geändert hat, nachdem das 21 437. Objekt instanziiert wurde? Die Zahlen deuten darauf hin, dass freeMemory für die von mir verwendete JVM (Java7 Win 64-Bit) eine Genauigkeit von etwas mehr als 2,5 MB hat (wenn Sie jedoch das Experiment ausführen, werden diese Werte variieren).

- Bearbeiten -

Dieser Code ist derselbe wie oben, es werden jedoch weitere Details zur Speichernutzung ausgegeben. Hoffentlich ist es ein bisschen klarer, wie sich die Speichernutzung der JVM verhält. Wir ordnen ständig neue Objekte in einer Schleife zu. Wenn bei jeder Iteration die Variable totalMemory oder freeMemory der letzten Iteration entspricht, wird nichts gedruckt. Wenn sich eines der beiden Elemente geändert hat, wird der aktuelle Speicherbedarf gemeldet. Die -Werte repräsentieren die Differenz zwischen der aktuellen Verwendung und dem vorherigen Speicherbericht.

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());
    }
}

Auf meinem Notebook sehe ich die folgende Ausgabe. Beachten Sie, dass Ihre Ergebnisse je nach Betriebssystem, Hardware, JVM-Implementierung usw. unterschiedlich sind:

#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
...

Es gibt einige Beobachtungen aus diesen Daten:

  1. Der verbrauchte Speicher nimmt erwartungsgemäß zu. Verwendeter Speicher umfasst Live-Objekte und Müll.
  2. Verwendeter Speicher verkleinert während einer GC, weil Müll weggeworfen wurde. Beispielsweise trat dies bei # 419680 auf.
  3. Die Menge des freien Speichers verringert sich in Blöcken, nicht Byte für Byte. Die Brocken variieren in der Größe. Manchmal sind die Brocken wirklich winzig wie 32 Bytes, aber normalerweise sind sie größer, wie 400K oder 800K. Es scheint also, dass die Chunk-Größe ziemlich unterschiedlich sein wird. Verglichen mit der Gesamthaufengröße erscheint die Abweichung jedoch klein. Bei # 419681 beträgt die Blockgröße beispielsweise nur 0,6% der gesamten Heapgröße.
  4. Der freie Speicher neigt wie erwartet dazu, sich zu verringern, bis ein GC in den Müll gelangt und den Müll aufräumt. In diesem Fall nimmt der freie Speicher abhängig von der Anzahl der verworfenen Abfälle erheblich zu.
  5. Dieser Test erzeugt viel Müll. Mit zunehmender Größe der Hashmap wird der Inhalt erneut aufgewärmt, wodurch viel Müll erzeugt wird.
44
cambecc

Ich habe die folgenden Methoden

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();
}

die den (verwendeten) Speicher in Bytes zurückgeben.

Wenn Sie zu MiB neu berechnen möchten, habe ich:

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) + "%";
    }
1
Code.IT