服务热线:13616026886

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

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

在java应用程序中监视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 jnicalljava_com_vladium_utils_systeminformation_getprocesscputime (jnienv * env, jclass cls)
  
  二、本地方法实现
  在大多数的win32平台上,该方法可以使用getprocesstimes()系统调用实现,差不多仅需要3行代码就可以了:
  
  jniexport jlong jnicalljava_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 jnicalljni_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。
  
  [code]  public static void main (string [] args) throws exception  {    if (args.length == 0)      throw new illegalargumentexception ("usage: cpumon ");
  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});  }[/code]
  
  另外,为了能够在启动要监视的应用程序之前开始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使用信息,这儿的要点是现在我们可以以编程方式记录该信息。对于长时间运行测试和服务器应用诊断程序,迟早会派上用场。本文附带的完整的库中添加了其它一些有用的本地方法,其中一个可以得到进程的pid(用于与外部工具整合)。

扫描关注微信公众号