Java获得可用内存

在运行时有没有什么好的办法让剩余内存可用于JVM? 这种情况的用例是当Web服务在接近内存限制时失败,拒绝新的连接,出现一个很好的错误消息“太多人使用它,稍后重试”,而不是突然死于OutOfMemory错误。

注意,这与事先计算/估计每个对象的成本无关。 原则上,我可以根据这个估计估计我的对象有多less内存,并拒绝新的连接,但是这似乎是一种哈克/脆弱。

威廉·布伦德尔的这个例子可能有些用处。

编辑:我最初提供这个样本(链接到威廉·布伦德尔的另一个话题的答案)。 该主题的创build者(Steve M)想要创build一个多平台的Java应用程序。 具体来说,用户试图find评估运行机器资源(磁盘空间,CPU和内存使用情况)的方法。

这是该主题中给出的答案的在线logging。 然而,在这个话题上已经指出,尽pipe我的答案被标记为接受,但这不是理想的解决scheme。

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声称这个function可能会被误解。 他声称,在内存不足错误发生之前(空闲内存)可以分配的大致内存量可能由下式给出:

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

allocatedMemory的内存由以下给出:

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

这里的关键是自由记忆的概念之间的差异。 有一点是操作系统提供Java虚拟机的内存。 另一个是由Java虚拟机本身实际使用的内存块组成的字节总量。

考虑到Java应用程序的内存是由Java虚拟机以块的formspipe理的,Java虚拟机可用的可用内存量可能与Java应用程序可用的内存量不完全匹配。

具体而言,Christian Fries表示使用-mx-Xmx标志来设置Java虚拟机可用的最大内存量。 他注意到以下function差异:

 /* 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

最后,正确的使用方法将有不同程度的依赖于您的应用程序的具体情况。

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

注意:到目前为止所有的答案,即使是接受的答案,似乎回答这个问题,说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已经保留的空闲内存,但可能只是less量。 你可能会得到presumableFreeMemory 。 当然,即使您尝试分配小于presumableFreeMemory的数量,您也可能会遇到内存不足的情况。 如果JVM没有从系统获取下一个内存块,则可能会发生这种情况。 但是,在大多数系统中,这种情况永远不会发生,系统宁愿开始交换 – 这是您想避免的情况。 对于原来的问题:如果将-mx设置为合理的值,则可用内存是可用内存的一个很好的指标。

除了其他的答案,我想指出的是,这样做不一定是个好主意,因为你的应用中可能有一个使用SoftReference的caching。

一旦JVM达到其内存限制,这样的caching将释放内存。 即使没有足够的可用内存,分配内存也会首先导致内存被软引用释放,并使其可用于分配。

除了使用运行时方法,您还可以使用一些额外的内存信息

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

每个MemoryUsage都提供init,used,committed和max值。 这可能是有用的,如果创build一个内存监视器线程轮询内存并logging它,随着时间的推移向您提供内存使用的历史。 有时,查看导致错误的内存使用情况会有所帮助。

如果你真的想把这个做到极致,可以创build一个堆转储线程。 随着时间的推移监视你的内存使用情况,当它超过一定的阈值时,请执行以下操作(这在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 内存分析器或类似的工具来查看这些堆转储文件,以检查内存泄漏等。

通过使用Maven依赖性来获得可用的内存OSHI v2.2:

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

你总是可以调用Runtime.getRuntime().freeMemory()

另一半的问题,得到的对象的成本,似乎对我来说更成问题。

我认为一个更好的解决scheme是弄清楚如何群集和扩展您的Web服务,以便他们可以优雅地接受额定负载的150%而不会拒绝新的连接。 听起来像一个大小的练习会给你一个比代码破解更好的解决scheme。

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

Runtime.getRuntime().freeMemory()是一种在运行时为JVM获取空闲内存的方法。 这是好方法(或)不完全取决于你的应用程序。