Java 通过 OperatingSystemMXBean 获得 getFreePhysicalMemorySize()
最近有个同事找我来看一个Java 应用的内存问题: 他的一个Java应用运行在一个8G内存的机器上, 配置的 Java heap 最大值是2G, 然而这个应用还有个页面能看到系统剩余空闲内存, 上面显示只有500M多的剩余. 他们想给这个应用加大 heap, 却看到只有500M剩余, 不敢加了.
初步看了下机器的内存, 确实是 free 内存是500多M, 然而那是绝对剩余的内存, 没有去掉随时可以回收的 buffer/cache 占用的内存. 如果去掉 buffer/cache 可以释放的内存, 剩余有近5G.
类似如下图:
那么它看到的500M是哪里来的呢? 经过他们翻找源代码, 发现来自于 MBean:
OperatingSystemMXBean.getFreePhysicalMemorySize()
这个 OperatingSystemMXBean
是根据操作系统不同有不同的实现, 但是根据这个测试类 GetFreePhysicalMemorySize 我们可以发现, 它其实取之于: cat /proc/meminfo
的 MemFree
, 由此推断, 它就是 free -m
里面的 free
列, 并不是 available
还可以通过释放 buffer/cache 去释放很多.
buffer/cache 就是操作系统为了最大化内存的使用价值而做的: buffer - 其实需要直接写到磁盘, 但是现在直接扔到内存, 等需要的时候, 再写到磁盘. cache - 不用直接从磁盘去取了, 而是把常用的放在内存随时快速使用.
找出源代码
写一段使用 OperatingSystemMXBean
获取空闲内存的代码:
import java.lang.management.ManagementFactory;
import com.sun.management.OperatingSystemMXBean;
public class FreeMemoryExample {
@SuppressWarnings("restriction")
public static void main(String[] args) {
OperatingSystemMXBean osBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
long freePhysicalMemorySize = osBean.getFreePhysicalMemorySize();
System.out.println("Free Physical Memory Size: " + freePhysicalMemorySize + " bytes");
}
}
编译并用 jdb
执行:
$ javac -g FreeMemoryExample.java
$ jdb FreeMemoryExample
Initializing jdb ...
> stop at FreeMemoryExample:8
Deferring breakpoint FreeMemoryExample:8.
It will be set after the class is loaded.
> run
run FreeMemoryExample
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
>
VM Started: Set deferred breakpoint FreeMemoryExample:8
Breakpoint hit: "thread=main", FreeMemoryExample.main(), line=8 bci=7
8 long freePhysicalMemorySize = osBean.getFreePhysicalMemorySize();
main[1] locals
Method arguments:
args = instance of java.lang.String[0] (id=831)
Local variables:
osBean = instance of com.sun.management.internal.OperatingSystemImpl(id=832)
main[1] methods com.sun.management.internal.OperatingSystemImpl
** methods list **
com.sun.management.internal.OperatingSystemImpl <init>(sun.management.VMManagement)
com.sun.management.internal.OperatingSystemImpl getCommittedVirtualMemorySize()
com.sun.management.internal.OperatingSystemImpl getFreePhysicalMemorySize()
com.sun.management.internal.OperatingSystemImpl getTotalPhysicalMemorySize()
com.sun.management.internal.OperatingSystemImpl getCommittedVirtualMemorySize0()
com.sun.management.internal.OperatingSystemImpl getFreePhysicalMemorySize0()
com.sun.management.internal.OperatingSystemImpl getFreeSwapSpaceSize0()
...
可以看到 osBean 的类是: com.sun.management.internal.OperatingSystemImpl. 查找 JDK 可以看到有 windows
和 unix
版本. 其中 unix
版本核心的代码是:
#else // solaris / linux
jlong num_avail_physical_pages = sysconf(_SC_AVPHYS_PAGES);
return (num_avail_physical_pages * page_size);
AI 对这2行代码的解释:
这段C代码用于获取系统中可用的物理页面数,并计算可用物理内存的总大小。
jlong num_avail_physical_pages = sysconf(_SC_AVPHYS_PAGES);:
- sysconf() 是一个用于获取系统配置信息的函数,位于 <unistd.h> 头文件中。
- _SC_AVPHYS_PAGES 是一个参数,用于指示我们要获取系统中可用的物理页面数。这个参数是一个宏定义,表示系统中可用的物理内存页面的数量。
- sysconf(_SC_AVPHYS_PAGES) 调用返回的值将被赋给 num_avail_physical_pages 变量,该变量的类型为 jlong,通常在Java Native Interface (JNI) 中用于表示Java中的 long 类型。
return (num_avail_physical_pages * page_size);:
- page_size 变量应该是一个代表页面大小的值。在大多数情况下,页面大小是一个固定的值,通常在操作系统的头文件中定义。例如,在Linux系统中,PAGE_SIZE 宏定义了页面的大小。
- num_avail_physical_pages * page_size 计算了可用物理内存的总大小,即可用的物理页面数乘以页面大小。
- 最后,这个总大小被作为函数的返回值返回。
综上所述,这段代码的主要作用是通过查询系统配置信息获取可用的物理页面数,然后计算可用物理内存的总大小,并将其作为函数的返回值返回。
所以,可以看到这里只是查看绝对空闲内存的数量, 然后再乘以每个页面大小.