Java获取可用内存


77

有什么好方法可以在运行时将剩余的内存提供给JVM?这种情况的用例是使Web服务在接近内存限制时通过拒绝一个新的错误消息“太多的人使用此,请稍后再试”,而不是因OutOfMemory错误而突然死亡而接近失败,从而优雅地失败。 。

注意,这与事先计算/估算每个对象的成本无关。原则上,我可以根据该估算值估算对象占用并拒绝新连接的内存量,但这似乎有点hacky /脆弱。

Answers:


96

William Brendel的这个样本可能有用。

编辑:我最初提供了此示例(链接到威廉·布伦德尔对另一个主题的答案)。该主题的创建者(Steve M)想要创建一个多平台的Java应用程序。具体地说,用户试图找到一种方法来评估运行中的计算机的资源(磁盘空间,CPU和内存使用情况)。

这是该主题给出的答案的内联成绩单。但是,尽管我的回答被标记为已接受,但已指出该主题不是理想的解决方案。

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虚拟机尚未收到主机系统分配的实际内存块,java.lang.OutOfMemoryError仍可能会产生a。

最后,正确的使用方法将在不同程度上取决于您的应用程序的具体情况。

我提供了另一个可能有用的链接。这是由用户Richard Dormand提出的问题,由stones333回答了有关确定使用的默认Java堆大小的问题。


1
@LeonardoGaldioli是什么意思?列出的方法执行所需的速度不够快?上下文是什么?
Blitzkoder 2014年

1
我只是想知道这些方法是否会使应用程序显着变慢
Leonardo Galdioli 2014年

2
@LeonardoGaldioli我当时不知道。您为什么不尝试使用以下示例执行该示例:long startTime = System.currentTimeMillis(); / *在此处插入原始内存代码* / long stopTime = System.currentTimeMillis(); long elapsedTime = stopTime-startTime;
Blitzkoder 2014年

3
问题是,在出现内存不足错误之前,应使用什么指示来显示内存量。请注意`Runtime.getRuntime()。freeMemory());不是该问题的答案。-(我为问题添加了更精确的答案)。
Christian Fries 2015年

72

注意:到目前为止,所有答案,甚至是已接受的答案,似乎都在回答这个问题,说这Runtime.getRuntime().freeMemory()将为您提供可能分配的内存量,直到发生内存不足错误为止。但是:这是错误的。

直到出现内存不足错误(即可能有空闲内存)之前可以分配的大约内存量

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

哪里

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

说明: 如果通过-mx参数(或-Xmx)启动JVM,则指定可用于JVM的最大数量。Runtime.getRuntime().maxMemory()会给你这个数目。JVM将从此系统内存量中分块分配内存,例如64 mb的块。在开始时,JVM只会从系统中分配这样的块,而不是全部。Runtime.getRuntime().totalMemory()给出了系统分配的总内存,同时Runtime.getRuntime().freeMemory()为您提供免费的内存分配的内存总量。

因此:

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

是JVM已经保留的可用内存,但可能只有少量。而且您可能会得到presumableFreeMemory。当然,即使您尝试分配小于的数量,也可能会遇到内存不足的异常presumableFreeMemory。如果JVM无法从系统获取下一个内存块,则可能会发生这种情况。但是,在大多数系统上,这种情况永远不会发生,并且系统宁愿开始交换-您希望避免这种情况。原始问题的答案:如果-mx设置为合理的值,那么它presumableFreeMemory是空闲内存的良好指示。


1
@Vasili我想我正在遭受双重主义的困扰。感谢您指出来,我对答案进行了编辑。
Christian Fries 2014年

maxMemory()状态文档:“如果没有固有限制,则将返回值Long.MAX_VALUE。”。可能值得检查一下,因为它将为您提供的“免费”号码是胡说八道。
mrec 2015年

我做了一个应用程序的完整工作示例,该应用程序根据Christian在他的回答中解释的内容获取内存信息:github.com/luciopaiva/java-simple-system-info
Lucio Paiva

8

除了使用Runtime方法外,您还可以使用

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

每个MemoryUsage提供init,used,committed和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()

问题的另一半,即获取对象的成本,对我来说似乎更成问题。

我认为,更好的解决方案是弄清楚如何对Web服务进行群集和扩展,以便它们可以优雅地接受150%的额定负载而不会拒绝新的连接。听起来像调整大小的练习会比代码hack为您提供更好的解决方案。



0

Runtime.getRuntime().freeMemory()是在运行时那时为JVM获取可用内存的一种方法。它是好方法吗(或不是)完全取决于您的应用程序。


0

为了获得物理机的总计,已可用内存信息,我们还可以通过将JavaRuntime.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);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.