Java >> Java tutorial >  >> Java

Hvordan kan jeg få den hukommelse, som mit Java-program bruger, via Javas Runtime-api?

Du gør det rigtigt. Måden at få hukommelsesbrug på er nøjagtig som du beskrev:

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

Men grunden til, at dit program altid returnerer det samme hukommelsesforbrug, er fordi du ikke opretter nok objekter til at overvinde præcisionsbegrænsningerne i freeMemory metode. Selvom den har byte opløsning , der er ingen garanti for, hvor præcis freeMemory skal være. Javadoc'en siger så meget:

en tilnærmelse til den samlede mængde hukommelse, der i øjeblikket er tilgængelig for fremtidige allokerede objekter, målt i bytes.

Prøv følgende, som skaber to millioner NewObject instanser og udskriver hver gang resultatet af freeMemory ændringer:

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

På min maskine ser jeg output som følgende

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

Læg mærke til, hvordan den rapporterede ledige hukommelse ikke ændrede sig, før det 21.437. objekt blev instantieret? Tallene antyder freeMemory for den JVM, jeg bruger (Java7 Win 64-bit), har en præcision på lidt over 2,5 MB (selvom hvis du kører eksperimentet, vil du se, at dette tal varierer).

-- Rediger --

Denne kode er den samme som ovenfor, men udskriver flere detaljer om hukommelsesbrug. Forhåbentlig er det lidt tydeligere, hvordan JVM's hukommelsesbrug opfører sig. Vi allokerer løbende nye objekter i en loop. Under hver iteration, hvis totalMemory eller freeMemory er det samme som sidste iteration, vi udskriver ikke noget. Men hvis en af ​​dem har ændret sig, rapporterer vi det aktuelle hukommelsesforbrug. værdier repræsenterer forskellen mellem aktuel brug og den tidligere hukommelsesrapport.

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

På min notesbog ser jeg følgende output. Bemærk, at dine resultater vil variere afhængigt af OS, hardware, JVM-implementering osv.:

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

Der er et par observationer fra disse data:

  1. Brugt hukommelse har en tendens til at stige, som forventet. Brugt hukommelse inkluderer levende genstande og affald.
  2. Men brugt hukommelse falder under en GC, fordi affald er blevet kasseret. For eksempel skete dette ved #419680.
  3. Mængden af ​​ledig hukommelse reduceres i bidder, ikke byte-for-byte. Klumperne varierer i størrelse. Nogle gange er bidderne virkelig små, f.eks. 32 bytes, men normalt er de større, f.eks. 400K eller 800K. Så det ser ud til, at klumpstørrelsen vil variere en del. Men sammenlignet med den samlede bunkestørrelse virker variationen lille. For eksempel, ved #419681 er chunk-størrelsen kun 0,6 % af den samlede bunkestørrelse.
  4. Fri hukommelse har en tendens til at falde, som forventet, indtil en GC sætter ind og rydder op i skrald. Når dette sker, øges ledig hukommelse temmelig dramatisk, afhængigt af mængden af ​​kasseret affald.
  5. Denne test genererer en masse affald. Efterhånden som hashmap'et vokser i størrelse, genhasher det indholdet og genererer dermed en masse affald.

Jeg har følgende metoder

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

som returnerer den (brugte) hukommelse i bytes.

Hvis du vil omregne til MiB, har jeg:

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) + "%";
    }

Java tag