Runtime.totalMemory: примеры (JAVA)

Значение totalMemory при анализе кучи
Раздел: Работа с процессами и памятью (Runtime, Process)
Runtime.totalMemory: long

Описание

Метод Runtime.totalMemory() возвращает текущий размер кучи, выделенной виртуальной машиной Java, в байтах. Формально это значение отражает суммарный объём памяти, доступный JVM для размещения объектов в данный момент; оно обычно равно сумме занятой и свободной памяти внутри кучи: totalMemory = used + free.

Сигнатура метода: public long totalMemory(). Аргументы отсутствуют. Возвращаемое значение имеет тип long и выражается в байтах. Значение не бросает проверяемых исключений и всегда неотрицательно.

Когда применяется: для быстрой оценки текущего объёма кучи, логирования состояния памяти, простого мониторинга и вычисления используемой памяти (в паре с Runtime.freeMemory()). Не подходит для точного профилирования: для детальной информации рекомендуется использовать API из java.lang.management и специализированные профайлеры.

Особенности поведения:

  • Возвращаемая величина может динамически изменяться в процессе работы программы: JVM может расширять или сокращать кучу (в зависимости от настроек -Xms, -Xmx и поведения GC).
  • totalMemory не обязана быть равна maxMemory. maxMemory() задаёт верхнюю границу кучи, тогда как totalMemory показывает текущий выделенный размер.
  • Значение зависит от используемого сборщика мусора и параметров JVM; поведение может отличаться между версиями Java и разными реализациями JVM.
// Простой вызов в Java
Runtime rt = Runtime.getRuntime();
long total = rt.totalMemory();
long free = rt.freeMemory();
long used = total - free;
System.out.println("total=" + total + " bytes, used=" + used + " bytes");
total=268435456 bytes, used=12345678 bytes

Короткие примеры

Ниже представлены простые варианты использования totalMemory в типичных ситуациях. Приведённый вывод - примерный и зависит от конфигурации JVM.

1) Базовый вывод значений памяти и перевод в мегабайты.

public class Example1 {
    public static void main(String[] args) {
        Runtime rt = Runtime.getRuntime();
        long total = rt.totalMemory();
        long free = rt.freeMemory();
        long used = total - free;
        System.out.println("total bytes: " + total);
        System.out.println("used bytes: " + used);
        System.out.println("total MB: " + (total / 1024 / 1024) + " MB");
    }
}
total bytes: 268435456
used bytes: 12582912
total MB: 256 MB

2) Наблюдение за влиянием -Xmx. Запуск с флагом -Xmx64m демонстрирует ограничение максимаума; totalMemory может быть меньше или равен maxMemory.

// Запуск: java -Xmx64m Example2
public class Example2 {
    public static void main(String[] args) {
        Runtime rt = Runtime.getRuntime();
        System.out.println("total=" + rt.totalMemory() + " bytes");
        System.out.println("max=" + rt.maxMemory() + " bytes");
    }
}
total=33554432 bytes
max=67108864 bytes

3) Влияние вызова сборки мусора на freeMemory и totalMemory.

public class Example3 {
    public static void main(String[] args) {
        Runtime rt = Runtime.getRuntime();
        byte[] a = new byte[10_000_000]; // занимает ~10MB
        System.out.println("before GC: total=" + rt.totalMemory() + ", free=" + rt.freeMemory());
        a = null;
        System.gc();
        System.out.println("after GC: total=" + rt.totalMemory() + ", free=" + rt.freeMemory());
    }
}
before GC: total=67108864, free=4800000
after GC: total=67108864, free=61000000

Похожие API в Java

В Java есть несколько инструментов, дающих похожую или более детальную информацию о памяти:

  • Runtime.freeMemory() - возвращает объём свободной памяти в текущей выделенной куче. Часто используется вместе с totalMemory для вычисления used.
  • Runtime.maxMemory() - верхняя граница кучи (обычно связана с -Xmx). Полезна для проверки лимита, в отличие от текущего выделенного размера.
  • MemoryMXBean (java.lang.management.ManagementFactory.getMemoryMXBean()) - предоставляет структуру MemoryUsage с полями init, used, committed, max для хипа и non-heap, более точная для мониторинга.
  • MemoryPoolMXBean - позволяет смотреть по пулам памяти (Eden, Survivor, Old Gen) и выбирать более точные метрики.

Рекомендации по выбору: для простых проверок и быстрой диагностики подходит Runtime.totalMemory вместе с freeMemory и maxMemory. Для промышленных мониторингов и алертинга предпочтительнее ManagementFactory и специализированные бенчмарки и профайлеры.

Аналоги в других языках

Краткие аналоги и отличия от Java totalMemory в разных языках с примерами.

  • PHP: memory_get_usage() и memory_get_peak_usage(). Возвращают используемую PHP-процессом память в байтах. Это процессная метрика, не отражает кучу JVM.
    <?
    echo memory_get_usage() . " bytes\n";
    ?>
    1234567 bytes
  • JavaScript (Node.js): process.memoryUsage() возвращает объект с heapTotal, heapUsed и rss. heapTotal похож на totalMemory.
    // Node.js
    console.log(process.memoryUsage());
    { rss: 23191040, heapTotal: 5767168, heapUsed: 3324552, external: 12345 }
  • Браузер JS: performance.memory (не стандартно поддерживается во всех браузерах) предоставляет похожие поля.
  • Python: нет встроенного прямого аналога к JVM-куче; используются модуль psutil для процесса или tracemalloc для слежения за аллокациями.
    import psutil, os
    p = psutil.Process(os.getpid())
    print(p.memory_info().rss)
    2345678
  • SQL: в стандартном SQL нет понятия JVM-кучи; можно использовать средства СУБД для просмотра потребления памяти сервера (например, PostgreSQL: pg_stat_activity или внешние инструменты).
  • C#: GC.GetTotalMemory(false) возвращает количество байт, занятой управляемой памятью. Поведение похоже, но учитывает модель CLR.
    using System;
    class P { static void Main(){ Console.WriteLine(GC.GetTotalMemory(false)); }}
    8765432
  • Lua: collectgarbage("count") возвращает килобайты занятой памяти Lua-станции.
    print(collectgarbage("count"))
    52.3
  • Go (Golang): пакет runtime и структура runtime.MemStats дают подробные метрики, в том числе HeapAlloc и HeapSys.
    package main
    import (
      "fmt"
      "runtime"
    )
    func main(){ var m runtime.MemStats; runtime.ReadMemStats(&m); fmt.Println(m.HeapSys) }
    524288
  • Kotlin: использует ту же JVM, что и Java, поэтому применяется Runtime.getRuntime().totalMemory() и прочие Java-методы непосредственно.

Отличия: многие языки дают метрики уровня процесса или аллокатора конкретной среды выполнения. Java totalMemory - показатель внутренней кучи JVM, не напрямую сопоставимый с системным RSS без дополнительных преобразований.

Типичные ошибки

Частые заблуждения и ошибки при использовании totalMemory:

  • Путаница totalMemory и maxMemory. Первый - текущий выделенный объём, второй - верхняя граница. Пример ошибки: ожидание, что total всегда равен max.
  • Неправильная интерпретация единиц. Метод возвращает байты; частая ошибка - деление с потерей точности при использовании целочисленного деления в неправильной последовательности.
  • Ожидание немедленного уменьшения totalMemory после System.gc(). GC может освободить объём внутри текущей кучи (увеличив freeMemory), но JVM не обязана уменьшать totalMemory.
  • Использование int для хранения результата. Возвращаемое значение long - приведение к int может вызвать переполнение на больших кластерах.

Пример ошибки с целочисленным делением:

Runtime rt = Runtime.getRuntime();
int totalMB = (int)(rt.totalMemory() / 1024 / 1024); // OK
int bad = (int)(rt.totalMemory() / (1024 * 1024)); // тоже OK но при 32-битном int возможен overflow позже
System.out.println(totalMB);
256

Пример неверного ожидания уменьшения totalMemory после GC:

Runtime rt = Runtime.getRuntime();
// при больших аллокациях
System.gc();
System.out.println("total after GC: " + rt.totalMemory());
total after GC: 67108864

Комментарий: значение total может остаться 67108864, хотя freeMemory выросла.

Изменения в последних версиях Java

Метод totalMemory существует в Java с ранних версий и сохранил свою сигнатуру и семантику. Прямых изменений в контракте метода не происходило. Однако поведение управления кучей могло измениться в новых версиях JVM и при внедрении новых сборщиков мусора (G1, ZGC, Shenandoah), что влияет на динамику изменения totalMemory и на стратегию расширения/сжатия кучи.

Кратко: API стабилен, но наблюдаемое значение может отличаться в зависимости от версии JVM и выбранного GC.

Расширенные примеры

Несколько подробных сценариев применения totalMemory в сочетании с другими инструментами.

1) Отслеживание утечек через периодические снимки памяти:

Пример java
public class LeakDetector {
    public static void main(String[] args) throws Exception {
        Runtime rt = Runtime.getRuntime();
        for (int i = 0; i < 10; i++) {
            System.gc();
            Thread.sleep(500);
            long used = rt.totalMemory() - rt.freeMemory();
            System.out.println(i + ": used=" + (used / 1024 / 1024) + " MB, total=" + (rt.totalMemory() / 1024 / 1024) + " MB");
            // эмулировать нарастание занятой памяти
            byte[] b = new byte[5_000_000];
        }
    }
}
0: used=20 MB, total=256 MB
1: used=25 MB, total=256 MB
... (прогресс) ...
9: used=70 MB, total=256 MB

Комментарий: наблюдение динамики used и total позволит заметить устойчивый рост used при постоянном увеличении retained объектов.

2) Принятие решения об отложленной загрузке компонентов, если свободная память мала:

Пример java
public class LazyLoadPolicy {
    public static boolean canLoadLargeModule() {
        Runtime rt = Runtime.getRuntime();
        long free = rt.freeMemory();
        long total = rt.totalMemory();
        long max = rt.maxMemory();
        long availableToGrow = max - total + free; // сколько ещё можно безопасно использовать
        return availableToGrow > (50L * 1024 * 1024); // требование 50MB
    }
}
true

Пояснение: расчёт availableToGrow учитывает и текущий свободный буфер, и возможное расширение кучи до maxMemory.

3) Сравнение данных Runtime с MemoryMXBean для валидации:

Пример java
import java.lang.management.*;
public class CompareMemory {
    public static void main(String[] args) {
        Runtime rt = Runtime.getRuntime();
        MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
        long rtTotal = rt.totalMemory();
        long heapCommitted = mbean.getHeapMemoryUsage().getCommitted();
        System.out.println("rt.total=" + rtTotal + ", mbean.committed=" + heapCommitted);
    }
}
rt.total=268435456, mbean.committed=268435456

Пояснение: committed в MemoryUsage соответствует объёму памяти, выделенному под хип и аналогично totalMemory в большинстве реализаций JVM.

4) Замер кратковременного потребления при нагрузочном тесте:

Пример java
public class LoadSpike {
    public static void main(String[] args) throws Exception {
        Runtime rt = Runtime.getRuntime();
        long before = rt.totalMemory() - rt.freeMemory();
        byte[][] arrays = new byte[200][];
        for (int i = 0; i < arrays.length; i++) arrays[i] = new byte[200_000]; // ~200KB * 200 ~40MB
        long after = rt.totalMemory() - rt.freeMemory();
        System.out.println("delta MB: " + ((after - before) / 1024 / 1024));
    }
}
delta MB: 38

Комментарий: в продакшн-сценариях подобные замеры помогают определить влияние определённых операций на использование кучи.

джава Runtime.totalMemory function comments

En
Runtime.totalMemory Returns the total amount of memory in the Java virtual machine