Java отримує доступну пам'ять


80

Чи є якийсь хороший спосіб отримати залишок пам'яті, доступний для JVM під час виконання? Прикладом цього може бути веб-сервіси, які виходять з ладу, коли вони наближаються до своїх меж пам’яті, відмовляючи в нових з’єднаннях із приємним повідомленням про помилку «занадто багато людей використовують це, спробуйте пізніше», а не різко вмираючи з помилкою OutOfMemory .

Зауважте, це не має нічого спільного з попереднім обчисленням / оцінкою вартості кожного об’єкта. В принципі, я міг би оцінити, скільки пам’яті забирають мої об’єкти, та відмовитись у нових з’єднаннях, виходячи з цієї оцінки, але це здається наче хакі / крихким.

Відповіді:


99

Цей зразок Вільяма Бренделя може бути корисним.

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());
  }
 }
}

Користувач Крістіан Фріс зазначає, що помилково вважати, що це Runtime.getRuntime().freeMemory()дає вам обсяг пам'яті, який може бути виділений доти, доки не станеться помилка, коли немає пам'яті.

З документації повернення підпису Runtime.getRuntime().freeMemory()як таке:

Повертає: наближення до загального обсягу пам'яті, доступного на даний момент для майбутніх виділених об'єктів, вимірюється в байтах.

Однак користувач Крістіан Фріс стверджує, що ця функція може бути сприйнята неправильно. Він стверджує, що приблизний обсяг пам'яті, який може бути виділений до виникнення помилки поза пам'яттю (вільна пам'ять), швидше за все, буде наданий:

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 ще не отримала фактичний шматок пам'яті, призначений основною системою, java.lang.OutOfMemoryErrorвсе одно може бути створено.

Зрештою, правильний метод, який буде використовуватися, буде мати різну ступінь залежності від особливостей вашої програми.

Я надаю ще одне посилання, яке може бути корисним. Це питання, задане користувачем Річардом Дормандом на яке відповів stones333, щодо визначення розміру купи Java за замовчуванням.


1
@LeonardoGaldioli що ти маєш на увазі? Перераховані методи виконуються недостатньо швидко для того, що вам потрібно? Який контекст?
Blitzkoder

1
Я просто хочу знати, чи ці методи значно сповільнюють роботу програми
Леонардо Гальдіолі,

2
@LeonardoGaldioli Я на той момент не знаю. Чому б вам не спробувати виконати цей зразок за допомогою: long startTime = System.currentTimeMillis (); / * ВСТАВІТЬ ТУТ код оригінального коду пам'яті * / long stopTime = System.currentTimeMillis (); long elapsedTime = stopTime - час початку;
Blitzkoder

3
Питання полягало в тому, яке вказівку використовувати для обсягу пам’яті, доки не з’явиться помилка, пов’язана з відсутністю пам’яті. Примітка "Runtime.getRuntime (). FreeMemory ());" НЕ є відповіддю на це питання. - (Я додав більш точну відповідь на питання).
Крістіан Фріс

72

Примітка: На сьогодні всі відповіді, навіть прийняті, відповідають на запитання, кажучи, що це Runtime.getRuntime().freeMemory()дає вам обсяг пам’яті, який може бути виділений, доки не з’явиться помилка, пов’язана з відсутністю пам’яті. Однак: це неправильно.

Приблизна кількість пам'яті , яке може бути виділено до тих пір , поки помилка відбувається з- за нестачі пам'яті, тобто вільної пам'яті, швидше за все ,

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 не отримає наступний фрагмент пам'яті від системи. Однак у більшості систем цього ніколи не трапиться, і система скоріше почне обмін - ситуації, якої ви хочете уникати. Повернемося до вихідного питання: якщо -mx встановлено на розумне значення, то presumableFreeMemoryце хороший показник для вільної пам'яті.


1
@Vasili Я припускаю, що я страждаю від подвійного ізму ... занадто багато працюю з дублями. Дякую, що вказали на це, я зробив редагування відповіді.
Крістіан Фріс

Документи для maxMemory()стану: "Якщо немає власного обмеження, буде повернуто значення Long.MAX_VALUE.". Напевно, варто перевірити це, оскільки "безкоштовний" номер, який він вам дасть, буде нісенітницею.
mrec

Я зробив цей повністю робочий приклад програми, яка отримує інформацію про пам’ять на основі того, що Крістіан пояснює у своїй відповіді: github.com/luciopaiva/java-simple-system-info
Lucio Paiva

8

На додаток до використання методів виконання, ви можете отримати додаткову інформацію про пам'ять за допомогою

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

Кожне MemoryUsage надає значення init, used, commit і max. Це може бути корисно, якщо створити потік монітора пам'яті, який опитує пам'ять та реєструє її, надаючи вам історію використання пам'яті з часом. Іноді корисно бачити використання пам'яті з часом, що призводить до помилок.

Якщо ви дійсно хочете довести це до крайності, створіть нитку дампа купи. Відстежуйте використання пам'яті з часом, і коли воно перевищує певні пороги, виконайте наступне (це працює на 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 або подібних інструментів для перевірки витоків пам'яті тощо.


6

На додаток до іншої відповіді, я хотів би зазначити, що робити це не обов’язково є гарною ідеєю, оскільки у вашому додатку може бути кеш, який використовує SoftReferences .

Такий кеш звільняє пам'ять, як тільки JVM досягає межі пам'яті. Виділення пам’яті, навіть якщо вільної пам’яті недостатньо, спочатку спричиняє звільнення пам’яті м’якими посиланнями та робить її доступною для розподілу.


6

Щоб отримати доступну пам’ять для всієї ОС, додайте OSHI, використовуючи таку залежність maven:

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

Тоді в Java використовуйте такий код:

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

Це отримує доступну операційну пам’ять, що саме я шукав, приємно. :)
BullyWiiPlaza

0

Ви завжди можете зателефонувати Runtime.getRuntime().freeMemory().

Інша половина проблеми, отримання вартості об’єктів, мені здається більш проблематичною.

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




0

Щоб отримати загальну , використану та звільнену інформацію про фізичну машину , ми також можемо отримати її, використовуючи java Runtime.exec()з аргументом команди, free -mа потім інтерпретуючи її, як показано нижче:

Runtime runtime = Runtime.getRuntime();

BufferedReader br = new BufferedReader(
    new InputStreamReader(runtime.exec("free -m").getInputStream()));

String line;
String memLine = "";
int index = 0;
while ((line = br.readLine()) != null) {
  if (index == 1) {
    memLine = line;
  }
  index++;
}
//                  total        used        free      shared  buff/cache   available
//    Mem:          15933        3153        9683         310        3097       12148
//    Swap:          3814           0        3814

List<String> memInfoList = Arrays.asList(memLine.split("\\s+"));
int totalSystemMemory = Integer.parseInt(memInfoList.get(1));
int totalSystemUsedMemory = Integer.parseInt(memInfoList.get(2));
int totalSystemFreeMemory = Integer.parseInt(memInfoList.get(3));

System.out.println("Total system memory in mb: " + totalSystemMemory);
System.out.println("Total system used memory in mb: " + totalSystemUsedMemory);
System.out.println("Total system free memory in mb: "   + totalSystemFreeMemory);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.