Ответ 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 он перефразирует его содержимое, тем самым генерируя много мусора.