Подтвердить что ты не робот

Как я могу получить память, которую использует моя Java-программа через Java Runtime api?

Есть похожие вопросы, но они, похоже, не отвечают на этот конкретный вопрос. Как я могу получить память, которую моя программа Java использует через Java Runtime api?

Ответ здесь показывает, что я могу сделать что-то вроде этого:

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

Но это всегда возвращает тот же номер, независимо от того, какую программу я запускаю. Например, ниже у меня есть программа, где независимо от того, сколько чисел я помещаю на карту, использование памяти остается прежним.

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

}

с помощью основного метода cambecc:

3085, Total: 128516096, Бесплатно: 127173744, Diff: 671120

173579, Всего: 128516096, Бесплатно: 110033976, Diff: 671128

335207, Всего: 128516096, Бесплатно: 92417792, Diff: 637544

672788, Total: 224198656, Бесплатно: 159302960, Diff: 1221520

1171480, Total: 224198656, Бесплатно: 106939136, Diff: 1221544

1489771, Total: 368377856, Бесплатно: 227374816, Diff: 1212984

1998743, Total: 368377856, Бесплатно: 182494408, Diff: 1212984

4b9b3361

Ответ 1

Вы делаете это правильно. Способ получения памяти используется точно так же, как вы описали:

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

Но причина, по которой ваша программа всегда возвращает одно и то же использование памяти, заключается в том, что вы не создаете достаточно объектов для преодоления ограничений точности метода freeMemory. Хотя он имеет разрешение в байте, нет никакой гарантии того, насколько точным будет freeMemory. Джавадок говорит так:

приблизительное значение общего объема памяти, доступного в настоящее время для будущих выделенных объектов, измеренного в байтах.

Попробуйте следующее, которое создает два миллиона экземпляров NewObject и печатает каждый раз, когда изменяется результат freeMemory:

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

На моей машине я вижу вывод следующим образом

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

Обратите внимание на то, как сообщаемая свободная память не изменилась, пока не был создан экземпляр 21 437-го объекта? Числа предлагают freeMemory для JVM, который я использую (Java7 Win 64-bit), имеет точность чуть более 2,5 МБ (хотя, если вы запустите эксперимент, вы увидите, что это число меняется).

- Изменить -

Этот код тот же, что и выше, но печатает более подробную информацию об использовании памяти. Надеюсь, это немного яснее, как ведет себя использование памяти JVM. Мы постоянно выделяем новые объекты в цикле. Во время каждой итерации, если totalMemory или freeMemory совпадает с последней итерацией, мы ничего не печатаем. Но если либо изменилось, мы сообщим о текущем использовании памяти. Значения представляют собой разницу между текущим использованием и предыдущим отчетением.

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

В моей записной книжке я вижу следующий вывод. Обратите внимание, что ваши результаты будут отличаться в зависимости от ОС, оборудования, реализации JVM и т.д.:

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

Есть несколько наблюдений по этим данным:

  • Используемая память имеет тенденцию увеличиваться, как и ожидалось. Используемая память включает живые объекты и мусор.
  • Но используемая память уменьшается во время GC, потому что мусор отбрасывается. Например, это произошло на # 419680.
  • Количество свободной памяти уменьшается в кусках, а не в байтах. Куски различаются по размеру. Иногда куски действительно крошечные, как 32 байта, но обычно они больше, например 400K или 800K. Таким образом, кажется, размер куска будет отличаться справедливым битом. Но по сравнению с общим размером кучи, вариация кажется крошечной. Например, в # 419681 размер куска составляет всего 0,6% от общего размера кучи.
  • Свободная память имеет тенденцию уменьшаться, как и ожидалось, до тех пор, пока GC не встряхнет и не очистит мусор. Когда это происходит, свободная память увеличивается довольно резко, в зависимости от количества отброшенного мусора.
  • Этот тест генерирует много мусора. По мере роста размера hashmap он перефразирует его содержимое, тем самым генерируя много мусора.

Ответ 2

У меня есть следующие методы

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

которые возвращают (использованную) память в байтах.

Если вы хотите пересчитать в MiB, у меня есть:

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

Ответ 3

Следующее может быть использовано в вашем java-методе для получения статистики, связанной с памятью.

// Get the Java runtime
Runtime runtime = Runtime.getRuntime();
// Run the garbage collector
runtime.gc();
// Calculate the used memory
long memory = runtime.totalMemory() - runtime.freeMemory();
System.out.println("Used memory is bytes: " + memory);
System.out.println("Used memory is megabytes: "
+ bytesToMegabytes(memory));