分类: Java
2009-04-17 16:42:08
测量CPU和内存的占用率常常是检查应用程序是否达到特定性能的一个重要环节。尽管Java提供了一些重要的方法用于测量其堆栈大小,但是使用标准的API是无法测量本机Java进程的大小和 CPU当前的使用率的。这种测量的结果对于开发人员来说非常重要,它会提供应用程序的实时性能和效率信息。不幸的是,这样的信息只能从操作系统直接获取,而这已经超出了Java标准的可移植能力。
一个主要的解决方案是使用操作系统自带的本机系统调用,将数据通过JNI(Java Native Interface,Java本机接口)传输给Java。与调用各个平台专用的外部命令(比如ps)并分析输出结果不同,这种方案始终是一种很可靠的方式。以前碰到这样的问题时,我尝试过使用自己编写的一个很小的库,它只能在Win32系统下测量进程的CPU占用率。但是,这个库的能力十分有限,所以我需要某种方式能够同时在Windows和Solaris平台上测量CPU和内存的占用率。
我扩展了这个库的能力,在Windows和Solaris 8平台上实现了所有功能。新的库能够测量纯CPU使用时间、CPU使用的百分比、本机剩余内存和已经使用的内存、Java进程的本机内存大小、系统信息(比如操作系统的名字、补丁程序、硬件信息等)。它由三部分实现: Java通用的部分、Windows实现,以及Solaris实现。依靠操作系统的部分用纯C语言实现。
编辑提示:本文可以下载,所有的源代码都以单独的文本方式列出来了。
库
所以,我们将创建一个简单的JNI库,用于同C层里的操作系统进行沟通,并把生成的数据提供给Java应用程序。首先,我们要创建一个SystemInformation类(列表A),为测量和记录CPU的使用率和其他与系统相关的信息提供一个简单的API。这个类是抽象的,因为它公开的是一个完全静态的API。
列表A
package com.vladium.utils;public abstract class SystemInformation
{
// public: ................................................................
/**
* A simple class to represent data snapshots taken by {@link #makeCPUUsageSnapshot}.
*/
public static final class CPUUsageSnapshot
{
public final long m_time, m_CPUTime;
// constructor is private to ensure that makeCPUUsageSnapshot()
// is used as the factory method for this class:
private CPUUsageSnapshot (final long time, final long CPUTime)
{
m_time = time;
m_CPUTime = CPUTime;
}
} // end of nested class// Custom exception class for throwing
public static final class NegativeCPUTime extends Exception {
}/**
* Minimum time difference [in milliseconds] enforced for the inputs into
* {@link #getProcessCPUUsage(SystemInformation.CPUUsageSnapshot,SystemInformation.CPUUsageSnapshot)}.
* The motivation for this restriction is the fact thatSystem.currentTimeMillis()
* on some systems has a low resolution (e.g., 10ms on win32). The current value
* is 100 ms.
*/
public static final int MIN_ELAPSED_TIME = 100;
/**
* Creates a CPU usage data snapshot by associating CPU time used with system
* time. The resulting data can be fed into
* {@link #getProcessCPUUsage(SystemInformation.CPUUsageSnapshot,SystemInformation.CPUUsageSnapshot)}.
*/
public static CPUUsageSnapshot makeCPUUsageSnapshot() throws SystemInformation.NegativeCPUTime
{
long prCPUTime = getProcessCPUTime ();
if (prCPUTime<0) throw new NegativeCPUTime();
return new CPUUsageSnapshot (System.currentTimeMillis (), getProcessCPUTime ());
}
/**
* Computes CPU usage (fraction of 1.0) betweenstart.m_CPUTime
and
*end.m_CPUTime
time points [1.0 corresponds to 100% utilization of
* all processors].
*
* @throws IllegalArgumentException if start and end time points are less than
* {@link #MIN_ELAPSED_TIME} ms apart.
* @throws IllegalArgumentException if either argument is null;
*/
public static double getProcessCPUUsage (final CPUUsageSnapshot start, final CPUUsageSnapshot end)
{
if (start == null) throw new IllegalArgumentException ("null input: start");
if (end == null) throw new IllegalArgumentException ("null input: end");
if (end.m_time < start.m_time + MIN_ELAPSED_TIME)
throw new IllegalArgumentException ("end time must be at least " + MIN_ELAPSED_TIME + " ms later than start time");
return ((double)(end.m_CPUTime - start.m_CPUTime)) / (end.m_time - start.m_time);
}
/**
* Returns the PID of the current process. The result is useful when you need
* to integrate a Java app with external tools.
*/
public static native int getProcessID ();/**
* Returns the number of processors on machine
*/
public static native int getCPUs ();/**
* Returns CPU (kernel + user) time used by the current process [in milliseconds].
* The returned value is adjusted for the number of processors in the system.
*/
public static native long getProcessCPUTime ();/**
* Returns CPU (kernel + user) time used by the current process [in perecents].
* The returned value is either CPU percentage, or zero if this is not supported by OS.
* Currently it is supported by Solaris8, and not supported by Windows XP
*/
public static native double getProcessCPUPercentage();/**
* Returns maximum memory available in the system.
*/
public static native long getMaxMem ();/**
* Returns current free memory in the system.
*/
public static native long getFreeMem ();/**
* Returns system name info like "uname" command output
*/
public static native String getSysInfo ();/**
* Returns CPU usage (fraction of 1.0) so far by the current process. This is a total
* for all processors since the process creation time.
*/
public static native double getProcessCPUUsage ();/**
* Returns current space allocated for the process, in Kbytes. Those pages may or may not be in memory.
*/
public static native long getMemoryUsage();/**
* Returns current process space being resident in memory, in Kbytes.
*/
public static native long getMemoryResident();/**
* Sets the system native process PID for which all measurements will be done.
* If this method is not called then the current JVM pid will act as a default.
* Returns the native-dependent error code, or 0 in case of success.
*/
public static native int setPid(int pid);/**
* Closes native-dependent process handle, if necessary.
*/
public static native int detachProcess();// protected: .............................................................
// package: ...............................................................
// private: ...............................................................
private SystemInformation () {} // prevent subclassing
private static final String SILIB = "silib";
static
{
// loading a native lib in a static initializer ensures that it is
// available done before any method in this class is called:
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
}
}} // end of class
最重要的方法是getProcessCPUTime(),它会返回当前进程的CPU时间(内核和用户)的毫秒数,或者是PID在初始化期间被传送给本机库的进程所消耗的CPU时间。返回的值应该根据系统的处理器的个数进行调整;下面我们来看看这是如何在本机代码里做到的。我们用修改符native来声明这个方法,这意味着它必须在JNI代码里实现。getProcessCPUTime()方法用来给CPU的使用率数据进行快照,方式是将经过测量的CPU时间与当前的系统时间进行关联。这样的数据库快照在makeCPUUsageSnapshot()方法里进行,并输出一个CPUUsageSnapshot容器对象。这样,测量CPU使用率的原理就很容易理解了:我们按照给定的时间间隔进行两次CPU快照,按1.0的分数来计算两个时间点之间的CPU使用率,方式是两点所在的CPU时间差除以两点所在系统时间差。下面就是getProcessCPUUsage()方法的工作原理:
public static double getProcessCPUUsage (final CPUUsageSnapshot start, final CPUUsageSnapshot end)
{
if (start == null) throw new IllegalArgumentException ("null input: start");
if (end == null) throw new IllegalArgumentException ("null input: end");
if (end.m_time < start.m_time + MIN_ELAPSED_TIME)
throw new IllegalArgumentException ("end time must be at least " + MIN_ELAPSED_TIME + " ms later than start time");
return ((double)(end.m_CPUTime - start.m_CPUTime)) / (end.m_time - start.m_time);
}
只要我们知道分母里的快照之间的时间间隔,以及分子里进程花在活动状态上的CPU时间,我们就会得到所测量的时间间隔过程中进程的CPU使用率;1.0就代表所有处理器100%的使用率。
事实上这种方式可以用在所有版本的UNIX的ps工具和Windows的任务管理器上,这两个都是用于监视特定进程的CPU使用率的程序。很显然,时间间隔越长,我们得到的结果就越平均、越不准确。但是最小时差应该被强制输入getProcessCPUUsage()。这种限制的原因是某些系统上的System.currentTimeMillis()的解析度很低。Solaris 8操作系统提供了一个系统调用,用于从内核表里直接获得CPU使用率。出于这个目的,我们拥有的getProcessCPUPercentage()方法会以百分比的形式返回进程所使用的CPU时间。如果这个特性不被操作系统支持(比如在Windows下),那么JNI库就会根据我们应用程序的设计返回一个负值。
还有其他一些本机声明要在本机部分实现:
所有这些方法对于不同的Java开发人员来说常常都是非常有用的。为了确保本机JNI库被调入内存并在调用任何本机方法之前被初始化,这个库被加载到一个静态初始值里:
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
}
}
在初始化一个.dll(或者.so)库之后,我们可以直接使用本机声明的方法:
final SystemInformation.CPUUsageSnapshot m_prevSnapshot =
SystemInformation.makeCPUUsageSnapshot ();
Thread.sleep(1000);
final SystemInformation.CPUUsageSnapshot event =
SystemInformation.makeCPUUsageSnapshot ();
final long memorySize = SystemInformation.getMemoryUsage();
final long residentSize = SystemInformation.getMemoryResident();
long freemem = SystemInformation.getFreeMem()/1024;
long maxmem = SystemInformation.getMaxMem()/1024;
double receivedCPUUsage = 100.0 * SystemInformation.getProcessCPUUsage (m_prevSnapshot, event);
System.out.println("Current CPU usage is "+receivedCPUUsage+"%”);
现在让我们来分别看看针对Windows和Solaris的JNI本机实现。C头文件silib.h(列表B)能够用JDK里的Javah工具生成,或者手动编写。
列表B
/* DO NOT EDIT THIS FILE - it is machine generated */ #include/* Header for class com_vladium_utils_SystemInformation */ #ifndef _Included_com_vladium_utils_SystemInformation #define _Included_com_vladium_utils_SystemInformation #ifdef __cplusplus extern "C" { #endif /* * Class: com_vladium_utils_SystemInformation * Method: getProcessID * Signature: ()I */ JNIEXPORT jint JNICALL Java_com_vladium_utils_SystemInformation_getProcessID (JNIEnv *, jclass); /* * Class: com_vladium_utils_SystemInformation * Method: getCPUs * Signature: ()I */ JNIEXPORT jint JNICALL Java_com_vladium_utils_SystemInformation_getCPUs (JNIEnv *, jclass); /* * Class: com_vladium_utils_SystemInformation * Method: getProcessCPUTime * Signature: ()J */ JNIEXPORT jlong JNICALL Java_com_vladium_utils_SystemInformation_getProcessCPUTime (JNIEnv *, jclass); /* * Class: com_vladium_utils_SystemInformation * Method: getProcessCPUUsage * Signature: ()D */ JNIEXPORT jdouble JNICALL Java_com_vladium_utils_SystemInformation_getProcessCPUUsage (JNIEnv *, jclass); /* * Class: com_vladium_utils_SystemInformation * Method: getPagefileUsage * Signature: ()J */ JNIEXPORT jlong JNICALL Java_com_vladium_utils_SystemInformation_getPagefileUsage (JNIEnv *, jclass); #ifdef __cplusplus } #endif #endif
Windows
首先我们来看看Windows的实现(列表C)。
列表C
/* ------------------------------------------------------------------------- */ /* * An implementation of JNI methods in com.vladium.utils.SystemInformation * class. The author compiled it using Microsoft Visual C++ and GCC for Win32 but the code * should be easy to use with any compiler for win32 platform. * * For simplicity, this implementaion assumes JNI 1.2+ and omits error handling. * * Enhanced by Peter V. Mikhalenko (C) 2004, Deutsche Bank [peter@mikhalenko.com] * Original source (C) 2002, Vladimir Roubtsov [vlad@trilogy.com] */ /* ------------------------------------------------------------------------- */ #include#include #include #include #include #include #include "com_vladium_utils_SystemInformation.h" static jint s_PID; static HANDLE s_currentProcess; static int alreadyDetached; static int s_numberOfProcessors; static SYSTEM_INFO systemInfo; static WORD processorArchitecture; static DWORD pageSize; static DWORD processorType; static WORD processorLevel; static WORD processorRevision; #define INFO_BUFFER_SIZE 32768 #define BUFSIZE 2048 /* ------------------------------------------------------------------------- */ /* * A helper function for converting FILETIME to a LONGLONG [safe from memory * alignment point of view]. */ static LONGLONG fileTimeToInt64 (const FILETIME * time) { ULARGE_INTEGER _time; _time.LowPart = time->dwLowDateTime; _time.HighPart = time->dwHighDateTime; return _time.QuadPart; } /* ......................................................................... */ /* * This method was added in JNI 1.2. It is executed once before any other * methods are called and is ostensibly for negotiating JNI spec versions, but * can also be conveniently used for initializing variables that will not * change throughout the lifetime of this process. */ JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM * vm, void * reserved) { s_PID = _getpid (); s_currentProcess = GetCurrentProcess (); externalCPUmon = 0; alreadyDetached = 0; GetSystemInfo (& systemInfo); s_numberOfProcessors = systemInfo.dwNumberOfProcessors; processorArchitecture = systemInfo.wProcessorArchitecture; pageSize = systemInfo.dwPageSize; processorType = systemInfo.dwProcessorType; processorLevel = systemInfo.wProcessorLevel; processorRevision = systemInfo.wProcessorRevision; return JNI_VERSION_1_2; } /* ......................................................................... */ JNIEXPORT void JNICALL JNI_OnUnload (JavaVM * vm, void * reserved) { if (!alreadyDetached && s_currentProcess!=NULL) { CloseHandle(s_currentProcess); printf("[JNI Unload] Detached from native process. "); fflush(stdout); } } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: getCPUs * Signature: ()I */ JNIEXPORT jint JNICALL Java_com_vladium_utils_SystemInformation_getCPUs (JNIEnv * env, jclass cls) { return (jint)s_numberOfProcessors; } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: getSysInfo * Signature: ()S */ JNIEXPORT jstring JNICALL Java_com_vladium_utils_SystemInformation_getSysInfo (JNIEnv * env, jclass cls) { char buf[2048]; char buf2[512]; *buf=0; OSVERSIONINFOEX osvi; BOOL bOsVersionInfoEx; TCHAR infoBuf[INFO_BUFFER_SIZE]; DWORD bufCharCount = INFO_BUFFER_SIZE; // Try calling GetVersionEx using the OSVERSIONINFOEX structure. // If that fails, try using the OSVERSIONINFO structure. ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); if( !(bOsVersionInfoEx = GetVersionEx ((OSVERSIONINFO *) &osvi)) ) { osvi.dwOSVersionInfoSize = sizeof (OSVERSIONINFO); if (! GetVersionEx ( (OSVERSIONINFO *) &osvi) ) { // Return empty string in case of problems goto next_label; } } switch (osvi.dwPlatformId) { // Test for the Windows NT product family. case VER_PLATFORM_WIN32_NT: // Test for the specific product. if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2 ) strcat(buf,"WinServer2003, "); if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1 ) strcat(buf,"WinXP "); if ( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 ) strcat(buf,"Win2K "); if ( osvi.dwMajorVersion <= 4 ) strcat(buf,"WinNT "); // Test for specific product on Windows NT 4.0 SP6 and later. if( bOsVersionInfoEx ) { // Test for the workstation type. if ( osvi.wProductType == VER_NT_WORKSTATION ) { if( osvi.dwMajorVersion == 4 ) strcat(buf,"Workstation 4.0 " ); else if( osvi.wSuiteMask & VER_SUITE_PERSONAL ) strcat(buf,"Home Edition " ); else strcat(buf,"Professional " ); } // Test for the server type. else if ( osvi.wProductType == VER_NT_SERVER || osvi.wProductType == VER_NT_DOMAIN_CONTROLLER ) { if( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 2 ) { if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) strcat(buf,"Datacenter Edition " ); else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) strcat(buf,"Enterprise Edition " ); else if ( osvi.wSuiteMask == VER_SUITE_BLADE ) strcat(buf,"Web Edition " ); else strcat(buf,"Standard Edition " ); } else if( osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 0 ) { if( osvi.wSuiteMask & VER_SUITE_DATACENTER ) strcat(buf,"Datacenter Server " ); else if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) strcat(buf,"Advanced Server " ); else strcat(buf,"Server " ); } else // Windows NT 4.0 { if( osvi.wSuiteMask & VER_SUITE_ENTERPRISE ) strcat(buf,"Server 4.0, Enterprise Edition " ); else strcat(buf,"Server 4.0 " ); } } } else // Test for specific product on Windows NT 4.0 SP5 and earlier { HKEY hKey; char szProductType[BUFSIZE]; DWORD dwBufLen=BUFSIZE; LONG lRet; lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE, "SYSTEMCurrentControlSetControlProductOptions", 0, KEY_QUERY_VALUE, &hKey ); if( lRet != ERROR_SUCCESS ) { goto next_label; } lRet = RegQueryValueEx( hKey, "ProductType", NULL, NULL, (LPBYTE) szProductType, &dwBufLen); if( (lRet != ERROR_SUCCESS) || (dwBufLen > BUFSIZE) ) { goto next_label; } RegCloseKey( hKey ); if ( lstrcmpi( "WINNT", szProductType) == 0 ) strcat(buf,"Workstation " ); if ( lstrcmpi( "LANMANNT", szProductType) == 0 ) strcat(buf,"Server " ); if ( lstrcmpi( "SERVERNT", szProductType) == 0 ) strcat(buf,"Advanced Server " ); sprintf(buf2, "%d.%d ", (int)osvi.dwMajorVersion, (int)osvi.dwMinorVersion ); strcat(buf,buf2); } // Display service pack (if any) and build number. if( osvi.dwMajorVersion == 4 && lstrcmpi( osvi.szCSDVersion, "Service Pack 6" ) == 0 ) { HKEY hKey; LONG lRet; // Test for SP6 versus SP6a. lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE, "SOFTWAREMicrosoftWindows NTCurrentVersionHotfixQ246009", 0, KEY_QUERY_VALUE, &hKey ); if( lRet == ERROR_SUCCESS ) { sprintf(buf2, "SP 6a (Build %d), ",
(int)(osvi.dwBuildNumber & 0xFFFF) ); strcat(buf,buf2); } else // Windows NT 4.0 prior to SP6a { sprintf(buf2, "%s (Build %d), ", osvi.szCSDVersion, (int)(osvi.dwBuildNumber & 0xFFFF)); strcat(buf,buf2); } RegCloseKey( hKey ); } else // not Windows NT 4.0 { sprintf(buf2, "%s (Build %d), ", osvi.szCSDVersion, (int)(osvi.dwBuildNumber & 0xFFFF)); strcat(buf,buf2); } break; // Test for the Windows Me/98/95. case VER_PLATFORM_WIN32_WINDOWS: if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0) { strcat(buf,"Win95 "); if ( osvi.szCSDVersion[1] == 'C' || osvi.szCSDVersion[1] == 'B' ) strcat(buf,"OSR2 " ); } if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 10) { strcat(buf,"Win98 "); if ( osvi.szCSDVersion[1] == 'A' ) strcat(buf,"SE " ); } if (osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 90) { strcat(buf,"WinME "); } break; case VER_PLATFORM_WIN32s: strcat(buf,"Win32s "); break; } next_label: strcat(buf," on "); // Get and display the name of the computer. bufCharCount = INFO_BUFFER_SIZE; if( !GetComputerName( infoBuf, &bufCharCount ) ) goto next_label_2; strcat(buf, infoBuf ); next_label_2: strcat(buf," ("); if (!(osvi.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS &&
osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0)) { // Win95 does not keep CPU info in registry LONG lRet; HKEY hKey; char szOrigCPUType[BUFSIZE]; int i=0; DWORD dwBufLen=BUFSIZE; lRet = RegOpenKeyEx( HKEY_LOCAL_MACHINE, "HARDWAREDESCRIPTIONSystemCentralProcessor", 0, KEY_QUERY_VALUE, &hKey ); if( lRet != ERROR_SUCCESS ) { goto next_label_3; } lRet = RegQueryValueEx( hKey, "ProcessorNameString", NULL, NULL, (LPBYTE) szOrigCPUType, &dwBufLen); if( (lRet != ERROR_SUCCESS) || (dwBufLen > BUFSIZE) ) { goto next_label_3; } RegCloseKey( hKey ); if (strlen(szOrigCPUType)>0) { while(szOrigCPUType[i]==' ' && szOrigCPUType[i]!=0) i++; strcat(buf,szOrigCPUType+i); } else goto next_label_3; } else { next_label_3: if (processorArchitecture==PROCESSOR_ARCHITECTURE_UNKNOWN) strcat(buf,"unknown_arch"); else if (processorArchitecture==PROCESSOR_ARCHITECTURE_INTEL) { strcat(buf,"Intel "); sprintf(buf2,"level %d ",processorLevel); strcat(buf,buf2); } else if (processorArchitecture==PROCESSOR_ARCHITECTURE_IA64) strcat(buf,"IA64 "); else if (processorArchitecture==PROCESSOR_ARCHITECTURE_MIPS) strcat(buf,"MIPS "); else if (processorArchitecture==PROCESSOR_ARCHITECTURE_ALPHA) strcat(buf,"Alpha "); else if (processorArchitecture==PROCESSOR_ARCHITECTURE_PPC) strcat(buf,"PowerPC "); else if (processorArchitecture==PROCESSOR_ARCHITECTURE_SHX) strcat(buf,"SHX "); else if (processorArchitecture==PROCESSOR_ARCHITECTURE_ALPHA64) strcat(buf,"Alpha64 "); else strcat(buf,"unknown_arch "); } strcat(buf,")"); jstring retval = (*env)->NewStringUTF(env,buf); return retval; } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: getProcessID * Signature: ()I */ JNIEXPORT jint JNICALL Java_com_vladium_utils_SystemInformation_getProcessID (JNIEnv * env, jclass cls) { return s_PID; } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: setPid * Signature: ()I */ JNIEXPORT jint JNICALL Java_com_vladium_utils_SystemInformation_setPid (JNIEnv * env, jclass cls, jint pid) { DWORD errCode; LPVOID lpMsgBuf; s_PID = pid; s_currentProcess = OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid); if (s_currentProcess==NULL) { errCode = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, errCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); printf("[CPUmon] Could not attach to native process.
Error code: %ld Error description: %s ",errCode,lpMsgBuf); fflush(stdout); LocalFree(lpMsgBuf); return errCode; } printf("[CPUmon] Attached to native process. "); fflush(stdout); return 0; } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: detachProcess * Signature: ()I */ JNIEXPORT jint JNICALL Java_com_vladium_utils_SystemInformation_detachProcess (JNIEnv * env, jclass cls) { if (!alreadyDetached && s_currentProcess!=NULL) { CloseHandle(s_currentProcess); alreadyDetached = 1; printf("[CPUmon] Detached from native process. "); fflush(stdout); } return 0; } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: getProcessCPUTime * Signature: ()J */ JNIEXPORT jlong JNICALL Java_com_vladium_utils_SystemInformation_getProcessCPUTime (JNIEnv * env, jclass cls) { FILETIME creationTime, exitTime, kernelTime, userTime; DWORD errCode; LPVOID lpMsgBuf; BOOL resultSuccessful = GetProcessTimes (s_currentProcess, &
creationTime, & exitTime, & kernelTime, & userTime); if (!resultSuccessful) { errCode = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, errCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); printf("[CPUmon] An error occured while trying to get CPU time.
Error code: %ld Error description: %s ",errCode,lpMsgBuf); fflush(stdout); LocalFree(lpMsgBuf); return -1; } return (jlong) ((fileTimeToInt64 (& kernelTime) + fileTimeToInt64 (& userTime)) / (s_numberOfProcessors * 10000)); } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: getMaxMem * Signature: ()J */ JNIEXPORT jlong JNICALL Java_com_vladium_utils_SystemInformation_getMaxMem (JNIEnv * env, jclass cls) { MEMORYSTATUS stat; GlobalMemoryStatus (&stat); return (jlong)(stat.dwTotalPhys/1024); } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: getFreeMem * Signature: ()J */ JNIEXPORT jlong JNICALL Java_com_vladium_utils_SystemInformation_getFreeMem (JNIEnv * env, jclass cls) { MEMORYSTATUS stat; GlobalMemoryStatus (&stat); return (jlong)(stat.dwAvailPhys/1024); } /* ......................................................................... */ /* define min elapsed time (in units of 10E-7 sec): */ #define MIN_ELAPSED_TIME (10000) /* * Class: com_vladium_utils_SystemInformation * Method: getProcessCPUUsage * Signature: ()D */ JNIEXPORT jdouble JNICALL Java_com_vladium_utils_SystemInformation_getProcessCPUUsage (JNIEnv * env, jclass cls) { FILETIME creationTime, exitTime, kernelTime, userTime, nowTime; LONGLONG elapsedTime; DWORD errCode; LPVOID lpMsgBuf; BOOL resultSuccessful = GetProcessTimes (s_currentProcess, &
creationTime, & exitTime, & kernelTime, & userTime); if (!resultSuccessful) { errCode = GetLastError(); FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, errCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL ); printf("[CPUmon] An error occured while trying to get CPU time.
Error code: %ld Error description: %s ",errCode,lpMsgBuf); fflush(stdout); LocalFree(lpMsgBuf); return -1.0; } GetSystemTimeAsFileTime (& nowTime); /* NOTE: win32 system time is not very precise [~10ms resolution], use sufficiently long sampling intervals if you make use of this method. */ elapsedTime = fileTimeToInt64 (& nowTime) - fileTimeToInt64 (& creationTime); if (elapsedTime < MIN_ELAPSED_TIME) return 0.0; else return ((jdouble) (fileTimeToInt64 (& kernelTime) + fileTimeToInt64 (& userTime))) / (s_numberOfProcessors * elapsedTime); } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: getProcessCPUPercentage * Signature: ()D */ JNIEXPORT jdouble JNICALL Java_com_vladium_utils_SystemInformation_getProcessCPUPercentage (JNIEnv * env, jclass cls) { // Not implemented on Windows return (jdouble)(-1.0); } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: getMemoryUsage * Signature: ()J */ JNIEXPORT jlong JNICALL Java_com_vladium_utils_SystemInformation_getMemoryUsage (JNIEnv * env, jclass cls) { PROCESS_MEMORY_COUNTERS pmc; if ( GetProcessMemoryInfo( s_currentProcess, &pmc, sizeof(pmc)) ) { return (jlong)(pmc.PagefileUsage/1024); } else { return (jlong)(0); } } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: getMemoryResident * Signature: ()J */ JNIEXPORT jlong JNICALL Java_com_vladium_utils_SystemInformation_getMemoryResident (JNIEnv * env, jclass cls) { PROCESS_MEMORY_COUNTERS pmc; if ( GetProcessMemoryInfo( s_currentProcess, &pmc, sizeof(pmc)) ) { return (jlong)(pmc.WorkingSetSize/1024); } else { return (jlong)(0); } } #undef MIN_ELAPSED_TIME /* ------------------------------------------------------------------------- */ /* end of file */JNI里有两个特殊的函数——JNI_OnLoad和JNI_OnUnload,它们分别在加载和卸载库的时候被调用。JNI_OnLoad在调用其他任何方法之前被执行,而且能够很方便地用于初始化在这一进程的生命周期中没有发生变化的变量,并用于协调JNI规范的版本。
在默认情况下,库会测量它自己的进程的参数,但是通过调用systemInformation.setPid()方法它可以从Java应用程序被重载。s_PID C变量用来保存PID,而s_currentProcess用来保存进程句柄(用于Windows的是HANDLE类型,而用于Solaris的是int类型)。为了读取的一些参数,应该首先打开进程句柄,我们需要在库关闭使用的时候停止同一个进程句柄(通常它在JVM因为相同的原因而关闭的时候发生)。
这就是JNI_OnUnload()函数起作用的地方。但是,JVM的一些实现事实上没有调用JNI_OnUnload(),还有发生句柄会永远打开的危险。为了降低这种可能性,我们应该在Java应用程序里加入一个明确调用detachProcess() C函数的关闭挂钩。下面就是我们加入关闭挂钩的方法:
if (pid!=-1) {
int result = SystemInformation.setPid(pid);
if (result!=0) {
return;
}
hasPid = true;
// Create shutdown hook for proper process detach
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
SystemInformation.detachProcess();
}
});
}通过调用WinAPI里的GetSystemInfo(),我们还可以获得关于中央处理器的一些信息。只要它是CPU的占用率根据这个值来调节,测量进程最重要的值就是处理器的个数(s_numberOfProcessors)。SystemInformation.getSysInfo()的Win32实现相当麻烦,因为在各个版本的Windows里,关于操作系统的版本、补丁、服务包以及相关硬件等信息被以不同的方式保存。所以需要读者来分析相关的源代码和代码中的注释。下面就是Windows XP上代码输出的示例:
System.out.println("SysInfo: ”+SystemInformation.getSysInfo()):
SysInfo: WinXP Professional Service Pack 1 (Build 2600),
on DBMOSWS2132 (Intel(R) Xeon(TM) CPU 1.70GHz)And the same code on Solaris will give:
SysInfo: SunOS 5.8 sxav-dev Generic_108528-29 sun4u sparc
SUNW,Ultra-Enterprise Sun_Microsystems为了获得CPU上进程所占用的总时间,我们使用了WinAPI的函数GetProcessTimes。其他的函数实现都非常简单,直接调用WinAPI,所以没有什么必要讨论它们。列表D里是用于Windows版本的GCC的make.bat文件,用来帮助读者创建相关的.dll库。
列表D
gcc -D_JNI_IMPLEMENTATION_ -Wl,--kill-at -IC:/jdk1.3.1_12/include -IC:/jdk1.3.1_12/include/win32 -shared C:/cpu_profile/src/native/com_vladium_utils_SystemInformation.c -o C:/cpu_profile/dll/silib.dll C:/MinGW/lib/libpsapi.a这个库的Solaris实现见列表E和列表F。这两个都是C语言文件,应该被编译到一个共享库(.so)里。用于编译共享库的帮助器make.sh见列表G。所有基于Solaris系统的调用被移到列表F里,这使得列表E就是一个JNI的简单包装程序。Solaris实现要比Win32实现更加复杂,要求更多的临时数据结构、内核和进程表。列出的代码里有更多的注释。
列表E
/* ------------------------------------------------------------------------- */ /* * An implementation of JNI methods in com.vladium.utils.SystemInformation * class. * This is a ported version from Win32 to Solaris. * * For simplicity, this implementaion assumes JNI 1.2+ and omits error handling. * * Port from Win32 by Peter V. Mikhalenko (C) 2004, Deutsche Bank [peter@mikhalenko.ru] * Original source (C) 2002, Vladimir Roubtsov [vlad@trilogy.com] */ /* ------------------------------------------------------------------------- */ #include#include "com_vladium_utils_SystemInformation.h" // Helper Solaris8-dependent external routines extern int sol_getCPUs(); extern int sol_getFreeMem(); extern int sol_getMaxMem(); extern long int sol_getProcessCPUTime(int pid,int nproc); extern double sol_getProcessCPUPercentage(int pid); extern long sol_getMemoryUsage(int pid); extern long sol_getMemoryResident(int pid); extern char* sol_getSysInfo(); extern void initKVM(); static jint s_PID; static int s_numberOfProcessors; static int externalCPUmon; static int alreadyDetached; /* ------------------------------------------------------------------------- */ /* ......................................................................... */ /* * This method was added in JNI 1.2. It is executed once before any other * methods are called and is ostensibly for negotiating JNI spec versions, but * can also be conveniently used for initializing variables that will not * change throughout the lifetime of this process. */ JNIEXPORT jint JNICALL JNI_OnLoad (JavaVM * vm, void * reserved) { s_PID = _getpid (); externalCPUmon = 0; alreadyDetached = 0; /* use kstat to update all processor information */ s_numberOfProcessors = sol_getCPUs(); initKVM(); return JNI_VERSION_1_2; } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: getCPUs * Signature: ()I */ JNIEXPORT jint JNICALL Java_com_vladium_utils_SystemInformation_getCPUs (JNIEnv * env, jclass cls) { return (jint)s_numberOfProcessors; } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: getSysInfo * Signature: ()S */ JNIEXPORT jstring JNICALL Java_com_vladium_utils_SystemInformation_getSysInfo (JNIEnv * env, jclass cls) { char * buf = sol_getSysInfo(); jstring retval = (*env)->NewStringUTF(env,buf); free(buf); return retval; } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: getProcessID * Signature: ()I */ JNIEXPORT jint JNICALL Java_com_vladium_utils_SystemInformation_getProcessID (JNIEnv * env, jclass cls) { return s_PID; } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: setPid * Signature: ()I */ JNIEXPORT jint JNICALL Java_com_vladium_utils_SystemInformation_setPid (JNIEnv * env, jclass cls, jint pid) { s_PID = pid; externalCPUmon = 1; printf("[CPUmon] Attached to process. "); fflush(stdout); return 0; } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: detachProcess * Signature: ()I */ JNIEXPORT jint JNICALL Java_com_vladium_utils_SystemInformation_detachProcess (JNIEnv * env, jclass cls) { if (externalCPUmon && !alreadyDetached) { alreadyDetached = 1; printf("[CPUmon] Detached from process. "); fflush(stdout); } return 0; } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: getProcessCPUTime * Signature: ()J */ JNIEXPORT jlong JNICALL Java_com_vladium_utils_SystemInformation_getProcessCPUTime (JNIEnv * env, jclass cls) { return (jlong)sol_getProcessCPUTime((int)s_PID,s_numberOfProcessors); } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: getMaxMem * Signature: ()J */ JNIEXPORT jlong JNICALL Java_com_vladium_utils_SystemInformation_getMaxMem (JNIEnv * env, jclass cls) { return (jlong)sol_getMaxMem(); } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: getFreeMem * Signature: ()J */ JNIEXPORT jlong JNICALL Java_com_vladium_utils_SystemInformation_getFreeMem (JNIEnv * env, jclass cls) { return (jlong)sol_getFreeMem(); } /* ......................................................................... */ /* define min elapsed time (in units of 10E-7 sec): */ #define MIN_ELAPSED_TIME (10000) /* * Class: com_vladium_utils_SystemInformation * Method: getProcessCPUUsage * Signature: ()D */ JNIEXPORT jdouble JNICALL Java_com_vladium_utils_SystemInformation_getProcessCPUUsage (JNIEnv * env, jclass cls) { return 0.0; } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: getProcessCPUPercentage * Signature: ()D */ JNIEXPORT jdouble JNICALL Java_com_vladium_utils_SystemInformation_getProcessCPUPercentage (JNIEnv * env, jclass cls) { return (jdouble)sol_getProcessCPUPercentage((int)s_PID); } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: getMemoryUsage * Signature: ()J */ JNIEXPORT jlong JNICALL Java_com_vladium_utils_SystemInformation_getMemoryUsage (JNIEnv * env, jclass cls) { return (jlong)sol_getMemoryUsage((int)s_PID); } /* ......................................................................... */ /* * Class: com_vladium_utils_SystemInformation * Method: getMemoryResident * Signature: ()J */ JNIEXPORT jlong JNICALL Java_com_vladium_utils_SystemInformation_getMemoryResident (JNIEnv * env, jclass cls) { return (jlong)sol_getMemoryResident((int)s_PID); } #undef MIN_ELAPSED_TIME /* ------------------------------------------------------------------------- */ /* end of file */ 列表F
/* ------------------------------------------------------------------------- */ /* * Solaris-dependent system routines and kernel calls * Used for getting memory and CPU consumption statistics * * Author: Peter V. Mikhalenko (C) 2004, Deutsche Bank [peter@mikhalenko.ru] */ /* ------------------------------------------------------------------------- */ #include# ifndef KSTAT_DATA_UINT32 # define ui32 ul # endif #include #include #include #include #include #include #include #include #define _STRUCTURED_PROC 1 #include #define prpsinfo psinfo #define pr_fill pr_nlwp /* These require an ANSI C compiler "Reisser cpp" doesn't like this */ #define pr_state pr_lwp.pr_state #define pr_oldpri pr_lwp.pr_oldpri #define pr_nice pr_lwp.pr_nice #define pr_pri pr_lwp.pr_pri #define pr_onpro pr_lwp.pr_onpro #define ZOMBIE(p) ((p)->pr_nlwp == 0) #define SIZE_K(p) ((p)->pr_size) #define RSS_K(p) ((p)->pr_rssize) #define PROCFS "/proc" /* definitions for indices in the nlist array */ #define X_V 0 #define X_MPID 1 #define X_ANONINFO 2 #define X_MAXMEM 3 #define X_SWAPFS_MINFREE 4 #define X_FREEMEM 5 #define X_AVAILRMEM 6 #define X_AVENRUN 7 #define X_CPU 8 #define X_NPROC 9 #define X_NCPUS 10 static struct nlist nlst[] = { , /* 0 */ /* replaced by dynamic allocation */ , /* 1 */ #if OSREV >= 56 /* this structure really has some extra fields, but the first three match */ , /* 2 */ #else , /* 2 */ #endif , /* 3 */ /* use sysconf */ , /* 4 */ /* used only w/ USE_ANONINFO */ , /* 5 */ /* available from kstat >= 2.5 */ , /* 6 */ /* available from kstat >= 2.5 */ , /* 7 */ /* available from kstat */ , /* 8 */ /* available from kstat */ , /* 9 */ /* available from kstat */ , /* 10 */ /* available from kstat */ }; static kstat_ctl_t *kc = NULL; static kstat_t **cpu_ks; static cpu_stat_t *cpu_stat; static int ncpus; kvm_t *kd; static unsigned long freemem_offset; static unsigned long maxmem_offset; static unsigned long freemem = -1L; static unsigned long maxmem = -1L; /* pagetok function is really a pointer to an appropriate function */ static int pageshift; static int (*p_pagetok) (); #define pagetok(size) ((*p_pagetok)(size)) int pagetok_none(int size) { return(size); } int pagetok_left(int size) { return(size << pageshift); } int pagetok_right(int size) { return(size >> pageshift); } #define UPDKCID(nk,ok) if (nk == -1) { perror("kstat_read "); exit(1); } if (nk != ok) goto kcid_changed; void initKVM() { int i; /* perform the kvm_open - suppress error here */ kd = kvm_open (NULL, NULL, NULL, O_RDONLY, NULL); /* calculate pageshift value */ i = sysconf(_SC_PAGESIZE); pageshift = 0; while ((i >>= 1) > 0) { pageshift++; } /* calculate an amount to shift to K values */ /* remember that log base 2 of 1024 is 10 (i.e.: 2^10 = 1024) */ pageshift -= 10; /* now determine which pageshift function is appropriate for the result (have to because x << y is undefined for y < 0) */ if (pageshift > 0) { /* this is the most likely */ p_pagetok = pagetok_left; } else if (pageshift == 0) { p_pagetok = pagetok_none; } else { p_pagetok = pagetok_right; pageshift = -pageshift; } } #define SI_LEN 512 #define BUFSIZE 256 char * sol_getSysInfo() { char * retbuf = (char*)malloc(SI_LEN); int curend = 0; int maxl = SI_LEN; *retbuf=0; char * buf = (char*)malloc(BUFSIZE); long res = sysinfo(SI_SYSNAME,buf,BUFSIZE); if (res>0 && res<=maxl) { strcat(retbuf,buf); curend=res-1; maxl=SI_LEN-curend; } if (curend 0 && res<=maxl) { strcat(retbuf,buf); curend+=res-1; maxl=SI_LEN-curend; } if (curend 0 && res<=maxl) { strcat(retbuf,buf); curend+=res-1; maxl=SI_LEN-curend; } if (curend 0 && res<=maxl) { strcat(retbuf,buf); curend+=res-1; maxl=SI_LEN-curend; } if (curend 0 && res<=maxl) { strcat(retbuf,buf); curend+=res-1; maxl=SI_LEN-curend; } if (curend 0 && res<=maxl) { strcat(retbuf,buf); curend+=res-1; maxl=SI_LEN-curend; } if (curend 0 && res<=maxl) { strcat(retbuf,buf); curend+=res-1; maxl=SI_LEN-curend; } if (curend 0 && res<=maxl) { strcat(retbuf,buf); curend+=res-1; maxl=SI_LEN-curend; } if (curend value.ui32 > ncpus) { ncpus = kn->value.ui32; cpu_ks = (kstat_t **) realloc (cpu_ks, ncpus * sizeof (kstat_t *)); cpu_stat = (cpu_stat_t *) realloc (cpu_stat, ncpus * sizeof (cpu_stat_t)); } for (ks = kc->kc_chain; ks; ks = ks->ks_next) { if (strncmp(ks->ks_name, "cpu_stat", 8) == 0) { nkcid = kstat_read(kc, ks, NULL); /* if kcid changed, pointer might be invalid */ UPDKCID(nkcid, kcid); cpu_ks[ncpu] = ks; ncpu++; if (ncpu > ncpus) { fprintf(stderr, "kstat finds too many cpus: should be %d ", ncpus); exit(1); } } } /* note that ncpu could be less than ncpus, but that's okay */ changed = 0; } /* return the number of cpus found */ ncpus=ncpu; return ncpu; } unsigned long sol_getMaxMem() { maxmem = pagetok(sysconf(_SC_PHYS_PAGES)); return maxmem; } unsigned long sol_getFreeMem() { kstat_t *ks; kstat_named_t *kn; ks = kstat_lookup(kc, "unix", 0, "system_pages"); if (kstat_read(kc, ks, 0) == -1) { perror("kstat_read"); exit(1); } if (kd != NULL) { /* always get freemem from kvm if we can*/ (void) getkval (freemem_offset, (int *) (&freemem), sizeof (freemem), "freemem"); } else { kn = kstat_data_lookup(ks, "freemem"); if (kn) freemem = kn->value.ul; } return (unsigned long)pagetok(freemem); } // Returns the number of milliseconds (not nanoseconds and seconds) elapsed on processor // since process start. The returned value is adjusted for the number of processors in the system. long int sol_getProcessCPUTime(int pid,int nproc) { struct prpsinfo currproc; int fd; char buf[30]; long int retval=0; snprintf(buf, sizeof(buf), "%s/%d/psinfo", PROCFS, pid); if ((fd = open (buf, O_RDONLY)) < 0) { return 0L; } if (read(fd, &currproc, sizeof(psinfo_t)) != sizeof(psinfo_t)) { (void)close(fd); return 0L; } (void)close(fd); retval = (currproc.pr_time.tv_sec * 1000 + currproc.pr_time.tv_nsec / 1000000) / nproc; return retval; } // Returns percentage CPU by pid // In Solaris 8 it is contained in procfs double sol_getProcessCPUPercentage(int pid) { struct prpsinfo currproc; int fd; char buf[30]; double retval=0.0; snprintf(buf, sizeof(buf), "%s/%d/psinfo", PROCFS, pid); if ((fd = open (buf, O_RDONLY)) < 0) { return 0; } if (read(fd, &currproc, sizeof(psinfo_t)) != sizeof(psinfo_t)) { (void)close(fd); return 0; } (void)close(fd); retval = ((double)currproc.pr_pctcpu)/0x8000*100; return retval; } // Returns current space allocated for the process, in bytes. Those pages may or may not be in memory. long sol_getMemoryUsage(int pid) { struct prpsinfo currproc; int fd; char buf[30]; double retval=0.0; snprintf(buf, sizeof(buf), "%s/%d/psinfo", PROCFS, pid); if ((fd = open (buf, O_RDONLY)) < 0) { return 0; } if (read(fd, &currproc, sizeof(psinfo_t)) != sizeof(psinfo_t)) { (void)close(fd); return 0; } (void)close(fd); retval = currproc.pr_size; return retval; } // Returns current process space being resident in memory. long sol_getMemoryResident(int pid) { struct prpsinfo currproc; int fd; char buf[30]; double retval=0.0; snprintf(buf, sizeof(buf), "%s/%d/psinfo", PROCFS, pid); if ((fd = open (buf, O_RDONLY)) < 0) { return 0; } if (read(fd, &currproc, sizeof(psinfo_t)) != sizeof(psinfo_t)) { (void)close(fd); return 0; } (void)close(fd); retval = currproc.pr_rssize; return retval; } /* * getkval(offset, ptr, size, refstr) - get a value out of the kernel. * "offset" is the byte offset into the kernel for the desired value, * "ptr" points to a buffer into which the value is retrieved, * "size" is the size of the buffer (and the object to retrieve), * "refstr" is a reference string used when printing error meessages, * if "refstr" starts with a '!', then a failure on read will not * be fatal (this may seem like a silly way to do things, but I * really didn't want the overhead of another argument). * */ int getkval (unsigned long offset, int *ptr, int size, char *refstr) { if (kvm_read (kd, offset, (char *) ptr, size) != size) { if (*refstr == '!') { return (0); } else { fprintf (stderr, "top: kvm_read for %s: %s ", refstr, strerror(errno)); exit(23); } } return (1); } 列表G
#!/bin/sh gcc -G -lkvm -lkstat com_vladium_utils_SystemInformation.c -o libsilib.so solaris-extern.c # com_vladium_utils_SystemInformation.c is in Listing-E # solaris-extern.c is in Listing-F在本文里,我已告诉你如何编程测量Java进程的内存和CPU占用率。当然用户可以通过查看Windows任务管理器或者ps的输出来达到相同的目的,但是重要的一点是,用户现在能够进行任何长期运行和/或自动的软件性能测试,这对于开发一个分布式或者可伸缩的,或者实时的性能关键的应用程序十分重要。加入获取系统软件和硬件信息的能力对于开发人员同样十分有用。这个库可以在Win32和Solaris平台上实现。但是,把它移植到其他UNIX平台上应该也不是问题,比如Linux。