Почему существует разница в статистике Unix и Java RunTime?

У меня проблемы с памятью в приложении, и мне нужна помощь в понимании этой статистики.

Unix 'top' показывает эту статистику для моего процесса

VSZ: 37.4g
RSS: 20.0g 

Таким образом, это означает, что 20 г в настоящее время заменены для процесса и в использовании.

Однако, когда я печатаю статистику из моего приложения с помощью класса Runtime, я получаю следующее:

Runtime.totalMemory() : 9.8G
Runtime.freeMemory()  : 3.6G
Runtime.maxMemory()  : 14.3G

Почему [Runtime.totalMemory() - Runtime.freeMemory()] не соответствует RSS? Это память, используемая в данный момент процессом. Существует огромная разница между двумя числами.

Кроме того, среда выполнения возвращает неиспользованную память (Runtime.freeMemory()) обратно в ОС для использования другими процессами?

Обратите внимание, что мои приложения работают в одноранговой системе кэширования GemFire, настроенной с использованием общих и реплицированных кэшей. Мне нужно оптимизировать приложение, чтобы уменьшить объем памяти.

1 ответ

Runtime.totalMemory показывает доступную в данный момент память. Java лениво выделяет память.

Кроме того, среда выполнения возвращает неиспользованную память (Runtime.freeMemory()) обратно в ОС для использования другими процессами?

Нет. Если Java выделил память (totalMemory), то теперь она находится в процессе Java.

RSS: 20,0 г 14,3 г

Как уже упоминалось, Java использует память помимо кучи. Также Gemfire использует память вне кучи ( проверьте это). Попробуйте посмотреть на них в VisualVM-Buffer Monitor.

Какова ваша инфраструктура (ОС, виртуальные машины)?

Если вы не можете использовать стандартные инструменты, вам, вероятно, следует написать собственный агент по обслуживанию с использованием JMX ( например)

ОБНОВИТЬ

Согласно документу, по умолчанию Gemfire использует JVM Heap.

Хорошо

Почему RSS постоянно показывает 20G для процесса, который использует только ~10G динамической памяти.

Если вы все еще спросите, я бы предоставил более подробную информацию. Что такое использование памяти Java?

  • Память кучи (Xmx);
  • Стековая память (каждый поток имеет собственный стек ThreadStackSize, VMThreadStackSize);
  • MaxPermSize или MaxMetaspaceSize (MaxPermGenSize, MaxMetaspaceSize);
  • Прямые байтовые буферы (MaxDirectMemorySize);
  • Пулы памяти (пространство Par Eden, пространство Par Survivor, CMS Old Gen, Metaspace/PermGen, кэш кода, сжатое пространство классов (CompressedClassSpaceSize));
  • Строковая таблица (где будут все String.intern(), StringTableSize);
  • PerfDataMemorySize;
  • MarkStackSize;
  • CompilerThreadStackSize;
  • Постоянный пул;
  • Я, вероятно, что-то пропустил;

Чтобы увидеть значения по умолчанию, вы можете запустить:

 java -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version

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

 jps -lvm

Есть ли в Unix инструмент командной строки для определения остального использования?

Для некоторых из них да. Для некоторых из них нет.

Для прямой памяти вы можете попробовать sun.misc.SharedSecrets.getJavaNioAccess(). GetDirectBufferPool(). GetMemoryUsed().

С помощью cmd, например:

 jcmd <pid> VM.native_memory baseline

Но это зависит от текущих настроек (NativeMemoryTracking). Вы можете прочитать, как включить отслеживание Native Memory здесь.

В Linux вы также можете использовать:

 pmap -x <pid>

В заключение это, вероятно, не имеет значения, потому что ваша задача:

Обратите внимание, что мои приложения работают в одноранговой системе кэширования GemFire, настроенной с использованием общих и реплицированных кэшей. Мне нужно оптимизировать приложение, чтобы уменьшить объем памяти.

И вы не можете повлиять на использование родной памяти. Я полагаю, посмотрите на утилиту jmap, которая может показать вам гистограмму классов. Вы должны проверить, что имеет большой размер в GemFire, и просмотреть эти объекты, возможно, вы храните в кеше данные, которых там быть не должно. Я имею в виду, что в своей практике по оптимизации кэша я рассматриваю объект и поля и вижу, какие поля действительно часты, а какие нет. Другой подход - проверить механизм сериализации и расположение объектов.

Как я уже говорил, вы можете использовать агентов по исправности:

import com.sun.tools.attach.VirtualMachine;
import sun.tools.attach.HotSpotVirtualMachine;

import java.io.InputStream;

public class JMemoryMain {

    public static void main(String[] args) throws Exception {
        final int pid = JMemoryMainUtils.getPid(args);
        final HotSpotVirtualMachine vm = (HotSpotVirtualMachine) VirtualMachine.attach(String.valueOf(pid));
        final byte[] buffer = new byte[1024];
        try (InputStream is = vm.heapHisto()) {
            for (int read; (read = is.read(buffer)) > 0;) {
                System.out.write(buffer, 0, read);
            }
        }
        vm.detach();
    }

}

Вам нужен tools.jar из JDK в зависимостях, чтобы запустить это. Он может также напечатать вашу гистограмму класса, но иногда работает, когда jmap нет. Кроме того, когда вы должны ждать много времени, пока вычисляется гистограмма, вы можете использовать VM.getSystemDictionary() и найти только ваши классы. И было бы полезно, если вы не можете включить NMT, из-за накладных расходов.

Другие вопросы по тегам