服务热线:13616026886

技术文档 欢迎使用技术文档,我们为你提供从新手到专业开发者的所有资源,你也可以通过它日益精进

位置:首页 > 技术文档 > JAVA > 新手入门 > 基础入门 > 查看文档

在java应用程序中使用jni来监视cpu详解

怎样在java中得到cpu的使用情况呢?这儿同时有一个好消息和一个坏消息。

坏消息是不能使用纯java的方法得到cpu的使用。没有这方面的直接的api。一个建议的替代方法是通过runtime.exec()确定jvm的进程id(pid),调用外部的、平台相关的命令,例如ps,然后在运行结果中解析出感兴趣的pid。但是,这种方法并不理想。

好消息是,可以采用一个更为可靠的方案:跳出java,写几行c代码,然后通过jni进行整合。下面我将向你展示编写一个win32平台的简单的jni库是多么简单。

一般来说,jni有点复杂。但是,如果你仅仅单向调用--从java调用本地代码,并且仅使用基本型进行通讯--事情还是很简单的。有许多jni方面的学习资料,所以这儿就不介绍jni的基础了。仅介绍实现步骤。

一、在java中声明jni方法

开始,创建一个声明了本地方法的类com.vladium.utils.systeminformation,该方法返回当前进程已使用的cpu的毫秒数。

public staticnative long getprocesscputime();

使用jdk内置的javah工具产生将来本地代码实现使用的c头。

jniexport jlong jnicall

java_com_vladium_utils_systeminformation_getprocesscputime (jnienv * env, jclass cls)

二、本地方法实现

在大多数的win32平台上,该方法可以使用getprocesstimes()系统调用实现,差不多仅需要3行代码就可以了:

jniexport jlong jnicall 
java_com_vladium_utils_systeminformation_getprocesscputime(jnienv * env, jclass cls) 
{ 
 filetime creationtime, exittime, kerneltime, usertime; 
     
 getprocesstimes (s_currentprocess, & creationtime, & exittime, & kerneltime, 
& usertime); 
  
 return (jlong) ((filetimetoint64 (& kerneltime) + filetimetoint64 (& usertime)) / 
(s_numberofprocessors * 10000)); 
}

该方法首先累加用于执行当前进程的核心和用户代码耗费的时间,除以处理器的数目,并把结果转换到毫秒。filetimetoint64()是一个辅助函数,用于把filetime结构的数据转换为64位的整数。s_currentprocess 和 s_numberofprocessors是全局变量,当jvm装载本地库时即初始化。

static handle s_currentprocess; 
static int s_numberofprocessors; 
  
jniexport jint jnicall 
jni_onload (javavm * vm, void * reserved) 
{ 
    system_info systeminfo; 
         
    s_currentprocess = getcurrentprocess (); 
  
    getsysteminfo (& systeminfo); 
    s_numberofprocessors = systeminfo.dwnumberofprocessors; 
  
    return jni_version_1_2; 
}

注意,如果你在unix平台上实现getprocesscputime(),你应该以getrusage系统调用开始。

三、调用本地方法

回到java中,在systeminformation类中,装载本地库(silib.dll on win32)最好通过静态初始化代码块完成。

private static final string silib = "silib"; 
     
    static 
    { 
        try 
        { 
            system.loadlibrary (silib); 
        } 
        catch (unsatisfiedlinkerror e) 
        { 
        system.out.println ("native lib '" + silib + "' not found 
in 'java.library.path': " 
            + system.getproperty ("java.library.path")); 
             
            throw e; // re-throw 
        } 
    }

注意,getprocesscputime()返回自jvm进程创建以来使用的cpu时间。就这个数据本身而言,对于这儿并没有太多的用处。还需要更有用的java方法来记录不同的时刻的数据快照(data snapshots),并报告任何两个时间点之间cpu的使用。

public static final class cpuusagesnapshot 
    { 
        private cpuusagesnapshot (long time, long cputime) 
        { 
            m_time = time; 
            m_cputime = cputime; 
        } 
         
        public final long m_time, m_cputime; 
         
    } // end of nested class 
     
    public static cpuusagesnapshot makecpuusagesnapshot() 
    { 
    return new cpuusagesnapshot(system.currenttimemillis(),getprocesscputime ()); 
    } 
     
    public static double getprocesscpuusage(cpuusagesnapshot start, 
cpuusagesnapshot end) 
   { 
    return ((double)(end.m_cputime - start.m_cputime)) / (end.m_time - start.m_time); 
   }

四、一个简单的cpu监视程序

“cpu监视api”基本就完成了!最后,创建了一个singleton的线程类cpuusagethread,它自动地每过一个时间间隔(默认是0.5秒)就拍下一个数据快照,并报告给所有的cpu使用事件的监听者(observer模式)。

public void run () 
    { 
        while (! isinterrupted ()) 
        { 
           final systeminformation.cpuusagesnapshot snapshot = 
systeminformation.makecpuusagesnapshot (); 
            notifylisteners (snapshot); 
             
            try 
            { 
                sleep (sleeptime); 
            } 
            catch (interruptedexception e) 
            { 
                return; 
            } 
        } 
    }

cpumon类是一个示例的监听器,仅简单地把cpu的使用情况打印输出到system.out。

public static void main (string [] args) throws exception 
    { 
        if (args.length == 0) 
       throw new illegalargumentexception ("usage: cpumon <app_main_class> 
<app_main_args...>"); 
         
        cpuusagethread monitor = cpuusagethread.getcputhreadusagethread (); 
        cpumon _this = new cpumon (); 
         
        class app = class.forname (args [0]); 
        method appmain = app.getmethod ("main", new class [] {string[].class}); 
        string [] appargs = new string [args.length - 1]; 
        system.arraycopy (args, 1, appargs, 0, appargs.length); 
         
        monitor.addusageeventlistener (_this); 
        monitor.start (); 
        appmain.invoke (null, new object [] {appargs}); 
    }

另外,为了能够在启动要监视的应用程序之前开始cpuusagethread,cpumon.main()包装了另一个java主类。

作为演示,运行cpumon和jdk1.3.1的swingset2示例程序(不要忘了把silib.dll安装到os的path环境变量或者java.library.path系统属性所覆盖的路径下):

>java -djava.library.path=. -cp silib.jar;(my jdk install dir)\demo\jfc\swingset2\swingset2.jar cpumon swingset2

[pid: 339] cpu usage: 46.8%

[pid: 339] cpu usage: 51.4%

[pid: 339] cpu usage: 54.8%

(while loading, the demo uses nearly 100% of one of the two cpus on my machine)

...

[pid: 339] cpu usage: 46.8%

[pid: 339] cpu usage: 0%

[pid: 339] cpu usage: 0%

(the demo finished loading all of its panels and is mostly idle)

...

[pid: 339] cpu usage: 100%

[pid: 339] cpu usage: 98.4%

[pid: 339] cpu usage: 97%

(i switched to the colorchooserdemo panel which ran a cpu-intensive

animation that used both of my cpus)

...

[pid: 339] cpu usage: 81.4%

[pid: 339] cpu usage: 50%

[pid: 339] cpu usage: 50%

(i used windows nt task manager to adjust the cpu affinity for the

"java" process to use a single cpu)

...

当然,也可以通过任务管理器查看到cpu使用信息,这儿的要点是现在我们可以以编程方式记录该信息。对于长时间运行测试和服务器应用诊断程序,会派上用场。

扫描关注微信公众号