Java获得可用内存

时间:2012-10-09 20:19:14

标签: java performance web-services memory operating-system

有没有什么好方法可以在运行时将剩余内存提供给JVM?这种情况的用例是让Web服务在接近其内存限制时优雅地失败,拒绝新连接时出现错误消息“太多人使用它,稍后再试”,而不是突然死于OutOfMemory错误

请注意,这与预先计算/估算每个对象的成本无关。原则上我可以估计我的对象占用了多少内存并根据该估计拒绝新连接,但这看起来有点hacky / fragile。

9 个答案:

答案 0 :(得分:73)

This sample by William Brendel may be of some use.

编辑:我最初提供了这个样本(链接到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()为您提供在发生内存不足错误之前可能分配的内存量是错误的。

documentation开始,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

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

我提供了另一个可能有用的链接。这是用户Richard Dormand提出的问题,并由stones333 about determining the default Java heap size used.

回答

答案 1 :(得分:61)

注意:到目前为止,所有答案,即使是已接受的答案,似乎都回答了这个问题: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没有从系统获取下一个内存块,则可能会发生这种情况。但是,在大多数系统上,这种情况永远不会发生,系统宁愿开始交换 - 这是您想要避免的情况。 W.r.t.对于原始问题:如果-mx设置为合理的值,则presumableFreeMemory是可用内存的良好指示。

答案 2 :(得分:7)

除了使用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 memory analyzer或类似工具查看这些堆转储文件,以检查内存泄漏等。

答案 3 :(得分:5)

除了其他答案之外,我还想指出,这样做不一定是个好主意,因为您的应用中可能有一个使用SoftReferences的缓存。

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

答案 4 :(得分:5)

要获取操作系统范围内的可用内存,请使用以下maven依赖项添加OSHI

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

答案 5 :(得分:0)

您随时可以致电Runtime.getRuntime().freeMemory()

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

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

答案 6 :(得分:0)

Runtime.getRuntime().freeMemory()是一种在运行时获取free memory for JVM的方法。它是好方法(或)不完全取决于您的应用程序。

答案 7 :(得分:0)

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

答案 8 :(得分: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);