有什么好方法可以在运行时将剩余的内存提供给JVM?这种情况的用例是使Web服务在接近内存限制时通过拒绝一个新的错误消息“太多的人使用此,请稍后再试”,而不是因OutOfMemory错误而突然死亡而接近失败,从而优雅地失败。 。
注意,这与事先计算/估算每个对象的成本无关。原则上,我可以根据该估算值估算对象占用并拒绝新连接的内存量,但这似乎有点hacky /脆弱。
Answers:
编辑:我最初提供了此示例(链接到威廉·布伦德尔对另一个主题的答案)。该主题的创建者(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堆大小的问题。
注意:到目前为止,所有答案,甚至是已接受的答案,似乎都在回答这个问题,说这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
是空闲内存的良好指示。
maxMemory()
状态文档:“如果没有固有限制,则将返回值Long.MAX_VALUE。”。可能值得检查一下,因为它将为您提供的“免费”号码是胡说八道。
除了使用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内存分析器或类似工具查看这些堆转储文件,以检查内存泄漏等。
除了其他答案,我想指出的是,这样做不一定是个好主意,因为您的应用程序中可能会有一个使用SoftReferences的缓存。
JVM达到其内存限制后,此类缓存将释放内存。即使没有足够的可用内存,分配内存也会首先导致软引用释放内存,并使之可用于分配。
要获取操作系统范围的可用内存,请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();
long freeMemory = Runtime.getRuntime().freeMemory();
为了获得物理机的总计,已用和可用内存信息,我们还可以通过将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);