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

Тестирование модуля Java: как измерить объем памяти для вызова метода

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

class MyClass()
{
   public void myMethod()
   {
      for(int i=0; i<10000000; i++)
      {
         // Allocate some memory, may be several collections
      }
   }
}

class MyClassTest
{
   @Test
   public void myMethod_makeSureMemoryFootprintIsNotBiggerThanMax()
   {
      new MyClass().myMethod(); 
      // How do I measure amount of memory it may try to allocate?
   }
}

Каков правильный подход? Или это невозможно/невозможно?

4b9b3361

Ответ 1

Я могу представить несколько вариантов:

  • Узнайте, сколько памяти требуется вашему методу с помощью микрообъектива (т.е. jmh).
  • Стратегии распределения зданий на основе эвристической оценки. Существует несколько решений с открытым исходным кодом, реализующих оценку размера класса, т.е. ClassSize. Гораздо проще использовать кеш, который освобождает редко используемые объекты (например, Guava Cache). Как уже упоминалось @EnnoShioji, кэш Guava имеет политики выселения на основе памяти.

Вы также можете написать собственный тестовый тест, который учитывает память. Идея состоит в том, чтобы

  • Пропустите один поток.
  • Создайте новый массив для хранения ваших объектов для выделения. Таким образом, эти объекты не будут собраны во время выполнения GC.
  • System.gc(), memoryBefore = runtime.totalMemory() - runtime.freeMemory()
  • Выделите свои объекты. Поместите их в массив.
  • System.gc(), memoryAfter = runtime.totalMemory() - runtime.freeMemory()

Это метод, который я использовал в моем лёгком микро-контрольном инструменте, который способен измерять распределение памяти с точностью до байта.

Ответ 2

Вы можете использовать профайлер (например, JProfiler) для использования памяти вида по классам. Или, как упоминается Areo, просто используйте память для печати:

    Runtime runtime = Runtime.getRuntime();
    long usedMemoryBefore = runtime.totalMemory() - runtime.freeMemory();
    System.out.println("Used Memory before" + usedMemoryBefore);
        // working code here
    long usedMemoryAfter = runtime.totalMemory() - runtime.freeMemory();
    System.out.println("Memory increased:" + (usedMemoryAfter-usedMemoryBefore));

Ответ 3

Для измерения использования текущей памяти используйте:

Runtime.getRuntime().freeMemory(), Runtime.getRuntime().totalMemory()

Вот хороший пример: получить системную информацию на уровне ОС

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

Ответ 4

Вот пример из Netty, который делает что-то подобное: MemoryAwareThreadPoolExecutor. Guava класс кеша также вытесняет размер. Вы можете посмотреть на эти источники и скопировать то, что они делают. В частности, вот как Netty оценивает размеры объектов. По существу, вы оцениваете размер объектов, которые вы генерируете в методе, и сохраняете счет.

Получение общей информации о памяти (например, сколько кучи доступно/используется) поможет вам определить, сколько памяти используется для выделения методу, но не для отслеживания того, сколько памяти было использовано отдельными вызовами метода.

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