Java получить доступную память


есть ли хороший способ получить оставшуюся память, доступную для JVM во время выполнения? В случае использования этого было бы иметь веб-службы, которые терпят неудачу изящно, когда они приближаются к своим ограничениям памяти, отказываясь от новых соединений с хорошим сообщением об ошибке "слишком много людей, использующих это, попробуйте еще раз позже", а не внезапно умирают с ошибкой OutOfMemory.

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

8 56

8 ответов:

этот образец Уильяма Бренделя может быть полезен.

EDIT: я изначально предоставил этот образец (ссылка на ответ Уильяма Бренделя по другой теме). Создатель этой темы (Стив М) хотел создать многоплатформенное Java-приложение. В частности, пользователь пытался найти средство, с помощью которого можно оценить ресурсы запущенной машины (дисковое пространство, процессор и использование памяти).

это встроенная расшифровка ответа, данного в этом тема. Однако на эту тему было указано, что это не идеальное решение, несмотря на то, что мой ответ был отмечен как принятый.

public class Main {
  public static void main(String[] args) {
  /* Total number of processors or cores available to the JVM */
  System.out.println("Available processors (cores): " + 
  Runtime.getRuntime().availableProcessors());

  /* Total amount of free memory available to the JVM */
  System.out.println("Free memory (bytes): " + 
  Runtime.getRuntime().freeMemory());

  /* This will return Long.MAX_VALUE if there is no preset limit */
  long maxMemory = Runtime.getRuntime().maxMemory();
  /* Maximum amount of memory the JVM will attempt to use */
  System.out.println("Maximum memory (bytes): " + 
  (maxMemory == Long.MAX_VALUE ? "no limit" : maxMemory));

  /* Total memory currently in use by the JVM */
  System.out.println("Total memory (bytes): " + 
  Runtime.getRuntime().totalMemory());

  /* Get a list of all filesystem roots on this system */
  File[] roots = File.listRoots();

  /* For each filesystem root, print some info */
  for (File root : roots) {
    System.out.println("File system root: " + root.getAbsolutePath());
    System.out.println("Total space (bytes): " + root.getTotalSpace());
    System.out.println("Free space (bytes): " + root.getFreeSpace());
    System.out.println("Usable space (bytes): " + root.getUsableSpace());
  }
 }
}

пользователь Christian Fries указывает, что неправильно предполагать, что Runtime.getRuntime().freeMemory() дает вам объем памяти,который может быть выделен до возникновения ошибки из памяти.

С документация возвращение подпись Runtime.getRuntime().freeMemory() таковы:

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

однако пользователь Christian Fries утверждает, что эта функция может быть неправильно истолкована. Он утверждает, что приблизительный объем памяти, который может быть выделен до тех пор, пока не возникнет ошибка нехватки памяти (свободная память), скорее всего, будет задан:

long presumableFreeMemory = Runtime.getRuntime().maxMemory() - allocatedMemory;

С allocatedMemory время:

long allocatedMemory = 
  (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory());

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

учитывая, что память, предоставленная Java-приложениям, управляется в блоках виртуальной машиной Java, количество свободный доступная для виртуальной машины Java может не точно соответствовать памяти, доступной для Java приложение.

в частности, Christian Fries обозначает использование -mx или -Xmx флаги для установки максимального объема памяти, доступной для виртуальной машины Java. Он отмечает следующие функциональные различия:

/* Returns the maximum amount of memory available to 
   the Java Virtual Machine set by the '-mx' or '-Xmx' flags. */
Runtime.getRuntime().maxMemory();

/* Returns the total memory allocated from the system 
   (which can at most reach the maximum memory value 
   returned by the previous function). */
Runtime.getRuntime().totalMemory();

/* Returns the free memory *within* the total memory 
   returned by the previous function. */
Runtime.getRuntime().freeMemory();

Кристиан завершает свой ответ утверждением, что Runtime.getRuntime().freeMemory() фактически возвращает то, что можно назвать предполагаемой свободной памятью; даже если будущее выделение памяти не превышает значение, возвращаемое этой функцией, если виртуальная машина Java имеет еще не получен фактический кусок памяти, назначенный хост-системой, a java.lang.OutOfMemoryError все еще может быть произведено.

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

Я предоставляю еще одну ссылку, которая может быть полезна. Это вопрос, сделанный пользователем Richard Dormand и отвеченный stones333 об определении используемого размера кучи Java по умолчанию.

Примечание: все ответы до сих пор, даже принятые, похоже, отвечают на вопрос, сказав, что Runtime.getRuntime().freeMemory() дает вам объем памяти,который может быть выделен до возникновения ошибки из памяти. Однако: это неправильно.

The примерное объем памяти, который может быть выделен до тех пор, пока не произойдет ошибка нехватки памяти, т. е. свободная память, вероятно,

long presumableFreeMemory = Runtime.getRuntime().maxMemory() - allocatedMemory;

здесь

long allocatedMemory      = (Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory());

объяснение: Если вы запуск JVM с помощью параметра-mx (или-Xmx) вы указываете максимальную сумму, доступную для JVM. Runtime.getRuntime().maxMemory() даст вам эту сумму. Из этого объема системной памяти JVM будет выделять память в кусках, скажем, например блоки 64 Мб. При запуске JVM будет выделять только такой кусок из системы, а не полную сумму. Runtime.getRuntime().totalMemory() дает общую память, выделенную из системы, в то время как Runtime.getRuntime().freeMemory() дает вам свободную память внутри общая память распределяемый.

отсюда:

long definitelyFreeMemory = Runtime.getRuntime().freeMemory();

это свободная память, уже зарезервированная JVM, но это, вероятно, просто небольшое количество. И вы, скорее всего, получите presumableFreeMemory. Конечно, вы можете получить исключение из памяти, даже если вы попытались выделить сумму меньше, чем presumableFreeMemory. Это может произойти, если JVM не получит следующий блок памяти из системы. Однако в большинстве систем этого никогда не произойдет, и система скорее начнет меняться местами - ситуация, которая вам нравится избегать. W. r. t. к исходному вопросу: если-mx имеет разумное значение, то presumableFreeMemory является хорошим индикатором для свободной памяти.

в дополнение к использованию методов выполнения, вы можете получить дополнительную информацию о памяти с помощью

MemoryMXBean memBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heap = memBean.getHeapMemoryUsage();
MemoryUsage nonheap = memBean.getNonHeapMemoryUsage();

каждого MemoryUsage предоставляет начальные, используемые, зафиксированные и максимальные значения. Это может быть полезно, если создать поток монитора памяти, который опрашивает память и регистрирует ее, предоставляя вам историю использования памяти с течением времени. Иногда полезно видеть, как использование памяти с течением времени приводит к ошибкам.

Если вы действительно хотите довести это до крайности, создайте поток дампа кучи. Контролируйте использование памяти с течением времени и когда она превышает определенные пороговые значения, выполните следующие действия (это работает на JBoss 5.0-ваш пробег может отличаться):

// init code
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
HotSpotDiagnosticMXBean diagBean = ManagementFactory.newPlatformMXBeanProxy(server, "com.sun.management:type=HotSpotDiagnostic", HotSpotDiagnosticMXBean.class); 

// loop code
// add some code to figure if we have passed some threshold, then

File heapFile = new File(outputDir, "heap-" + curThreshold + ".hprof");
log.info("Dumping heap file " + heapFile.getAbsolutePath());
diagBean.dumpHeap(heapFile.getAbsolutePath(), true);

позже вы можете просмотреть эти файлы дампа кучи с eclipse анализатор памяти или аналогичные инструменты для проверки утечек памяти и т. д.

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

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

чтобы получить доступную память для всей ОС, добавьте OSHI С помощью следующей зависимости maven:

<dependency>
    <groupId>com.github.dblock</groupId>
    <artifactId>oshi-core</artifactId>
    <version>LATEST</version>
</dependency>

затем в Java, используйте следующий код:

SystemInfo si = new SystemInfo();
HardwareAbstractionLayer hal = si.getHardware();
long availableMemory = hal.getMemory().getAvailable();

вы всегда можете позвонить Runtime.getRuntime().freeMemory().

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

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

long freeMemory = Runtime.getRuntime().freeMemory();

Runtime.getRuntime().freeMemory() до свободная память для JVM в этот момент во время выполнения. Это хороший способ (или) не зависит полностью от вашего приложения.