如何在Java中监控计算机的CPU、内存和磁盘使用情况?

206
我希望在Java中监视以下系统信息:
  • 当前CPU使用率**(百分比)
  • 可用内存*(空闲/总共)
  • 可用磁盘空间(空闲/总共)

    *注意,我的意思是整个系统可用的内存,而不仅仅是JVM。

我正在寻找一种跨平台解决方案(Linux、Mac和Windows),它不依赖于我自己调用外部程序或使用JNI。尽管这些是可行的选择,但如果有人已经有更好的解决方案,我更愿意不自己维护特定于操作系统的代码。

如果有一个免费的库以可靠且跨平台的方式实现此功能,那就太好了(即使它会调用外部程序或使用本地代码)。

非常感谢任何建议。

要澄清的是,我想获取整个系统的当前CPU使用率,而不仅仅是Java进程。

SIGAR API提供了我要寻找的所有功能,因此它是迄今为止对我的问题最好的答案。但是,由于其在GPL下许可,我不能将其用于我的原始目的(封闭源商业产品)。Hyperic可能会为商业用途许可SIGAR,但我没有研究过。对于我的GPL项目,我肯定会在将来考虑使用SIGAR。

对于我目前的需求,我倾向于以下内容:

  • 对于CPU使用率,OperatingSystemMXBean.getSystemLoadAverage() / OperatingSystemMXBean.getAvailableProcessors()(每个CPU的负载平均值)
  • 对于内存,OperatingSystemMXBean.getTotalPhysicalMemorySize()OperatingSystemMXBean.getFreePhysicalMemorySize()
  • 对于磁盘空间,File.getTotalSpace()File.getUsableSpace()

限制:

getSystemLoadAverage()和磁盘空间查询方法仅在Java 6下可用。另外,一些JMX功能可能无法在所有平台上使用(即已有报告称getSystemLoadAverage()在Windows上返回-1)。

尽管最初是根据GPL许可证发布的,但它已更改Apache 2.0,通常可用于闭源、商业产品。


澄清一下,Sigar API 可以获取系统信息。如果您想要 JVM 信息,请使用 JMX。 - Matt Cummings
SIGAR的GPL许可并不意味着您无法使用它,只是意味着您需要联系作者并请求其他许可证。作者通常很乐意接受少量费用并允许商业许可。 - Alec Thomas
7
自1.6.4版本起,SIGAR采用Apache许可证。 - Soundlink
你知道如何获取每个独立处理器的负载吗? - zcaudate
12个回答

68

在我之前提到的这篇文章中,我建议您使用SIGAR API。我在自己的一个应用程序中使用了SIGAR API,它非常棒。你会发现它很稳定、得到良好支持,并且有很多有用的例子。它是开源的,采用的是GPL 2Apache 2.0许可证。去试试吧,我有一种感觉它会满足您的需求。

使用Java和Sigar API,您可以获取内存、CPU、磁盘、负载平均值、网络接口信息和指标、进程表信息、路由信息等。


14
使用Sigar时要小心,在x64机器上可能会出现问题... https://dev59.com/ZH_aa4cB1Zd3GeqP00R6,而且似乎自2010年以来该库没有得到更新。 - Alvaro

59

下面的代码据说可以获取CPU和RAM。详细信息请参见ManagementFactory

import java.lang.management.ManagementFactory;
import java.lang.management.OperatingSystemMXBean;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

private static void printUsage() {
  OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean();
  for (Method method : operatingSystemMXBean.getClass().getDeclaredMethods()) {
    method.setAccessible(true);
    if (method.getName().startsWith("get")
        && Modifier.isPublic(method.getModifiers())) {
            Object value;
        try {
            value = method.invoke(operatingSystemMXBean);
        } catch (Exception e) {
            value = e;
        } // try
        System.out.println(method.getName() + " = " + value);
    } // if
  } // for
}

3
上述代码的示例输出。该代码可以在Java 1.5上运行。getCommittedVirtualMemorySize = 28622848 getFreePhysicalMemorySize = 228462592 getFreeSwapSpaceSize = 1129848832 getProcessCpuTime = 390625000 getTotalPhysicalMemorySize = 2147483647 getTotalSwapSpaceSize = 4294967295 - blak3r
据我所知,getProcessCpuTime = 390625000 只是该线程运行的时间。这对于确定处理器使用情况并不真正有用。 - MikeNereson
2
不确定它是否真正可靠。在拥有4GB物理内存的Windows XP上,它仅报告2GB(使用Java 6和Java 7进行测试)。总交换大小也是错误的。 - Emmanuel Bourg
4
@EmmanuelBourg 只是为了记录那些看到这个主题的人,这与此相关的有一个漏洞 - user800014
3
这种方法在Java 9之前非常有效,但由于Java采用了新的访问检查框架,现在会抛出java.lang.reflect.InaccessibleObjectException异常。 - Thor Lancaster

49
在JDK 1.7中,你可以通过com.sun.management.OperatingSystemMXBean获取系统CPU和内存使用情况。这与java.lang.management.OperatingSystemMXBean不同。
long getCommittedVirtualMemorySize()
// Returns the amount of virtual memory that is guaranteed to be available to the running process in bytes, or -1 if this operation is not supported.

long getFreePhysicalMemorySize()
// Returns the amount of free physical memory in bytes.

long getFreeSwapSpaceSize()
// Returns the amount of free swap space in bytes.

double getProcessCpuLoad()
// Returns the "recent cpu usage" for the Java Virtual Machine process.

long getProcessCpuTime()
// Returns the CPU time used by the process on which the Java virtual machine is running in nanoseconds.

double getSystemCpuLoad()
// Returns the "recent cpu usage" for the whole system.

long getTotalPhysicalMemorySize()
// Returns the total amount of physical memory in bytes.

long getTotalSwapSpaceSize()
// Returns the total amount of swap space in bytes.

6
似乎这有好有坏。在FreeBSD 10和OpenJDK 8上,CPU负载得到-1的结果。 - cen
请查看此问题 https://dev59.com/w2Ij5IYBdhLWcg3w4Y6S。它说需要几秒钟才能生效。(注意:我没有尝试过) - Juh_

41

这个对我来说完美无缺,没有任何外部 API 的参与,仅仅是 Java 本身的隐藏功能 :)

import com.sun.management.OperatingSystemMXBean;
...
OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean(
                OperatingSystemMXBean.class);
// What % CPU load this current JVM is taking, from 0.0-1.0
System.out.println(osBean.getProcessCpuLoad());

// What % load the overall system is at, from 0.0-1.0
System.out.println(osBean.getSystemCpuLoad());

我真的认为这是最好的答案,可以在Linux上运行,所以我很开心。 - ArsenArsen
2
任何线索为什么第二次调用显示0.0?在OpenJDK v8上。 - vorburger
1
不要忘记: "import java.lang.management.ManagementFactory;" - Bernd
2
getProcessCpuLoad和getSystemCpuLoad对我返回-1。我正在使用JDK 1.8。 - Burak Akyıldız
1
这个方法在我的Windows 10和macOS High Sierra上都有效。 - gbmhunter
显示剩余3条评论

17

请查看这篇非常详细的文章: http://nadeausoftware.com/articles/2008/03/java_tip_how_get_cpu_and_user_time_benchmarking#UsingaSuninternalclasstogetJVMCPUtime

为了获得CPU使用率百分比,你需要进行一些简单的数学计算:

MBeanServerConnection mbsc = ManagementFactory.getPlatformMBeanServer();

OperatingSystemMXBean osMBean = ManagementFactory.newPlatformMXBeanProxy(
mbsc, ManagementFactory.OPERATING_SYSTEM_MXBEAN_NAME, OperatingSystemMXBean.class);

long nanoBefore = System.nanoTime();
long cpuBefore = osMBean.getProcessCpuTime();

// Call an expensive task, or sleep if you are monitoring a remote process

long cpuAfter = osMBean.getProcessCpuTime();
long nanoAfter = System.nanoTime();

long percent;
if (nanoAfter > nanoBefore)
 percent = ((cpuAfter-cpuBefore)*100L)/
   (nanoAfter-nanoBefore);
else percent = 0;

System.out.println("Cpu usage: "+percent+"%");

注意:您必须导入com.sun.management.OperatingSystemMXBean而不是java.lang.management.OperatingSystemMXBean


这是一个非常好的答案。所有其他技术都会给出非常奇怪和不可靠的结果,但是这个带有一些尾随平均的方法对我来说非常有效。 - Fractaly
当 CPU 时间高于经过的时间(我获得超过 100%)时,这是仅仅因为多线程,还是有其他解释? - Lukas Hanacek
链接已失效,MBeanServerConnection是哪个包? - RonPringadi

13

8
对于磁盘空间,如果您使用的是Java 6,则可以在File上使用getTotalSpacegetFreeSpace方法。如果您没有使用Java 6,我相信您可以使用Apache Commons IO来部分实现。
很抱歉,我不知道有任何跨平台获取CPU使用率或内存使用率的方式。

6

很多相关信息已经可以通过JMX获取。Java 5中,JMX内置,并且JDK包含了一个JMX控制台查看器。

您可以手动使用JMX进行监控,或者如果需要在自己的运行时中获取此信息,则可以从Java中调用JMX命令。


5
/* YOU CAN TRY THIS TOO */

import java.io.File;
 import java.lang.management.ManagementFactory;
// import java.lang.management.OperatingSystemMXBean;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.lang.management.RuntimeMXBean;
 import java.io.*;
 import java.net.*;
 import java.util.*;
 import java.io.LineNumberReader;
 import java.lang.management.ManagementFactory;
import com.sun.management.OperatingSystemMXBean;
import java.lang.management.ManagementFactory;
import java.util.Random;



 public class Pragati
 {

     public static void printUsage(Runtime runtime)
     {
     long total, free, used;
     int mb = 1024*1024;

     total = runtime.totalMemory();
     free = runtime.freeMemory();
     used = total - free;
     System.out.println("\nTotal Memory: " + total / mb + "MB");
     System.out.println(" Memory Used: " + used / mb + "MB");
     System.out.println(" Memory Free: " + free / mb + "MB");
     System.out.println("Percent Used: " + ((double)used/(double)total)*100 + "%");
     System.out.println("Percent Free: " + ((double)free/(double)total)*100 + "%");
    }
    public static void log(Object message)
         {
            System.out.println(message);
         }

        public static int calcCPU(long cpuStartTime, long elapsedStartTime, int cpuCount)
        {
             long end = System.nanoTime();
             long totalAvailCPUTime = cpuCount * (end-elapsedStartTime);
             long totalUsedCPUTime = ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime()-cpuStartTime;
             //log("Total CPU Time:" + totalUsedCPUTime + " ns.");
             //log("Total Avail CPU Time:" + totalAvailCPUTime + " ns.");
             float per = ((float)totalUsedCPUTime*100)/(float)totalAvailCPUTime;
             log( per);
             return (int)per;
        }

        static boolean isPrime(int n)
        {
     // 2 is the smallest prime
            if (n <= 2)
            {
                return n == 2;
            }
     // even numbers other than 2 are not prime
            if (n % 2 == 0)
            {
                return false;
            }
     // check odd divisors from 3
     // to the square root of n
         for (int i = 3, end = (int)Math.sqrt(n); i <= end; i += 2)
         {
            if (n % i == 0)
         {
         return false;
        }
        }
 return true;
}
    public static void main(String [] args)
    {
            int mb = 1024*1024;
            int gb = 1024*1024*1024;
             /* PHYSICAL MEMORY USAGE */
             System.out.println("\n**** Sizes in Mega Bytes ****\n");
            com.sun.management.OperatingSystemMXBean operatingSystemMXBean = (com.sun.management.OperatingSystemMXBean)ManagementFactory.getOperatingSystemMXBean();
            //RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
            //operatingSystemMXBean = (com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
            com.sun.management.OperatingSystemMXBean os = (com.sun.management.OperatingSystemMXBean)
            java.lang.management.ManagementFactory.getOperatingSystemMXBean();
            long physicalMemorySize = os.getTotalPhysicalMemorySize();
            System.out.println("PHYSICAL MEMORY DETAILS \n");
            System.out.println("total physical memory : " + physicalMemorySize / mb + "MB ");
            long physicalfreeMemorySize = os.getFreePhysicalMemorySize();
            System.out.println("total free physical memory : " + physicalfreeMemorySize / mb + "MB");
            /* DISC SPACE DETAILS */
            File diskPartition = new File("C:");
            File diskPartition1 = new File("D:");
            File diskPartition2 = new File("E:");
            long totalCapacity = diskPartition.getTotalSpace() / gb;
            long totalCapacity1 = diskPartition1.getTotalSpace() / gb;
            double freePartitionSpace = diskPartition.getFreeSpace() / gb;
            double freePartitionSpace1 = diskPartition1.getFreeSpace() / gb;
            double freePartitionSpace2 = diskPartition2.getFreeSpace() / gb;
            double usablePatitionSpace = diskPartition.getUsableSpace() / gb;
            System.out.println("\n**** Sizes in Giga Bytes ****\n");
            System.out.println("DISC SPACE DETAILS \n");
            //System.out.println("Total C partition size : " + totalCapacity + "GB");
            //System.out.println("Usable Space : " + usablePatitionSpace + "GB");
            System.out.println("Free Space in drive C: : " + freePartitionSpace + "GB");
            System.out.println("Free Space in drive D:  : " + freePartitionSpace1 + "GB");
            System.out.println("Free Space in drive E: " + freePartitionSpace2 + "GB");
            if(freePartitionSpace <= totalCapacity%10 || freePartitionSpace1 <= totalCapacity1%10)
            {
                System.out.println(" !!!alert!!!!");
            }
            else
                System.out.println("no alert");

            Runtime runtime;
            byte[] bytes;
            System.out.println("\n \n**MEMORY DETAILS  ** \n");
            // Print initial memory usage.
            runtime = Runtime.getRuntime();
            printUsage(runtime);

            // Allocate a 1 Megabyte and print memory usage
            bytes = new byte[1024*1024];
            printUsage(runtime);

            bytes = null;
            // Invoke garbage collector to reclaim the allocated memory.
            runtime.gc();

            // Wait 5 seconds to give garbage collector a chance to run
            try {
            Thread.sleep(5000);
            } catch(InterruptedException e) {
            e.printStackTrace();
            return;
            }

            // Total memory will probably be the same as the second printUsage call,
            // but the free memory should be about 1 Megabyte larger if garbage
            // collection kicked in.
            printUsage(runtime);
            for(int i = 0; i < 30; i++)
                     {
                         long start = System.nanoTime();
                        // log(start);
                        //number of available processors;
                         int cpuCount = ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors();
                         Random random = new Random(start);
                         int seed = Math.abs(random.nextInt());
                         log("\n \n CPU USAGE DETAILS \n\n");
                         log("Starting Test with " + cpuCount + " CPUs and random number:" + seed);
                         int primes = 10000;
                         //
                         long startCPUTime = ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime();
                         start = System.nanoTime();
                         while(primes != 0)
                         {
                            if(isPrime(seed))
                            {
                                primes--;
                            }
                            seed++;

                        }
                         float cpuPercent = calcCPU(startCPUTime, start, cpuCount);
                         log("CPU USAGE : " + cpuPercent + " % ");


                         try
                         {
                             Thread.sleep(1000);
                         }
                         catch (InterruptedException e) {}
        }

            try
            {
                Thread.sleep(500);
            }`enter code here`
            catch (Exception ignored) { }
        }
    }

4
以下代码仅适用于Linux(或许Unix),但能在实际项目中正常运行。
    private double getAverageValueByLinux() throws InterruptedException {
    try {

        long delay = 50;
        List<Double> listValues = new ArrayList<Double>();
        for (int i = 0; i < 100; i++) {
            long cput1 = getCpuT();
            Thread.sleep(delay);
            long cput2 = getCpuT();
            double cpuproc = (1000d * (cput2 - cput1)) / (double) delay;
            listValues.add(cpuproc);
        }
        listValues.remove(0);
        listValues.remove(listValues.size() - 1);
        double sum = 0.0;
        for (Double double1 : listValues) {
            sum += double1;
        }
        return sum / listValues.size();
    } catch (Exception e) {
        e.printStackTrace();
        return 0;
    }

}

private long getCpuT throws FileNotFoundException, IOException {
    BufferedReader reader = new BufferedReader(new FileReader("/proc/stat"));
    String line = reader.readLine();
    Pattern pattern = Pattern.compile("\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)\\D+(\\d+)")
    Matcher m = pattern.matcher(line);

    long cpuUser = 0;
    long cpuSystem = 0;
    if (m.find()) {
        cpuUser = Long.parseLong(m.group(1));
        cpuSystem = Long.parseLong(m.group(3));
    }
    return cpuUser + cpuSystem;
}

1
这实际上是我正在寻找的内容,但代码缺少用于从/proc/stat中查找CPU信息的正则表达式模式。 - Donal Tobin
什么是模式? - hcarrasko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接