歡迎您光臨本站 註冊首頁

測量Java應用程序的CPU和內存佔用率

←手機掃碼閱讀     火星人 @ 2014-03-10 , reply:0

測量CPU和內存的佔用率常常是檢查Java應用程序是否達到特定性能的一個重要環節.儘管Java提供了一些重要的方法用於測量其堆棧大小,但是使用標準的API是無法測量本機Java進程的大小和 CPU當前的使用率的.這種測量的結果對於開發人員來說非常重要,它會提供應用程序的實時性能和效率信息.不幸的是,這樣的信息只能從操作系統直接獲取,而這已經超出了Java標準的可移植能力.

一個主要的解決方案是使用操作系統自帶的本機系統調用,將數據通過JNI(Java Native Interface,Java本機介面)傳輸給Java.與調用各個平台專用的外部命令(比如ps)並分析輸出結果不同,這種方案始終是一種很可靠的方式.以前碰到這樣的問題時,我嘗試過使用Vladimir Roubtsov自己編寫的一個很小的庫,它只能在Win32系統下測量進程的CPU佔用率.但是,這個庫的能力十分有限,所以我需要某種方式能夠同時在Windows和Solaris平台上測量CPU和內存的佔用率.

我擴展了這個庫的能力,在Windows和Solaris 8平台上實現了所有功能.新的庫能夠測量純CPU使用時間、CPU使用的百分比、本機剩餘內存和已經使用的內存、Java進程的本機內存大小、系統信息(比如操作系統的名字、補丁程序、硬體信息等).它由三部分實現: Java通用的部分、Windows實現,以及Solaris實現.依靠操作系統的部分用純C語言實現.

編輯提示:本文可以下載,所有的源代碼都以單獨的文本方式列出來了.

所以,我們將創建一個簡單的JNI庫,用於同C層里的操作系統進行溝通,並把生成的數據提供給Java應用程序.,我們要創建一個SystemInformation類(列表A),為測量和記錄CPU的使用率和其他與系統相關的信息提供一個簡單的API.這個類是抽象的,它公開的是一個完全靜態的API.

列表A

<PRE>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 that <CODE>System.currentTimeMillis()</CODE>
* 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&lt;0) throw new NegativeCPUTime();
return new CPUUsageSnapshot (System.currentTimeMillis (), getProcessCPUTime ());
}

/**
* Computes CPU usage (fraction of 1.0) between <CODE>start.m_CPUTime</CODE> and
* <CODE>end.m_CPUTime</CODE> 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 &lt; 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
</PRE>

最重要的方法是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庫就會根據我們應用程序的設計返回一個負值.

還有其他一些本機聲明要在本機部分實現:

getCPUs()用來返回機器上處理器的個數
getMaxMem()用來返回系統上可用的最大物理內存
getFreeMem()用來返回系統上當前可用內存
getSysInfo()用來返回系統信息,包括一些硬體和操作系統的詳細信息
getMemoryUsage()用來返回分配給進程的空間,以KB為單位(這些頁面文件可能在內存里,也有可能不在內存里)
getMemoryResident()用來返回當前進程駐留在內存里的空間,以KB為單位.

所有這些方法對於不同的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 (curend0 && res<=maxl) {
strcat(retbuf,buf);
curend =res-1;
maxl=SI_LEN-curend;
}
if (curend0 && res<=maxl) {
strcat(retbuf,buf);
curend =res-1;
maxl=SI_LEN-curend;
}
if (curend0 && res<=maxl) {
strcat(retbuf,buf);
curend =res-1;
maxl=SI_LEN-curend;
}
if (curend0 && res<=maxl) {
strcat(retbuf,buf);
curend =res-1;
maxl=SI_LEN-curend;
}
if (curend0 && res<=maxl) {
strcat(retbuf,buf);
curend =res-1;
maxl=SI_LEN-curend;
}
if (curend0 && res<=maxl) {
strcat(retbuf,buf);
curend =res-1;
maxl=SI_LEN-curend;
}
if (curend0 && res<=maxl) {
strcat(retbuf,buf);
curend =res-1;
maxl=SI_LEN-curend;
}
if (curendvalue.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.


[火星人 ] 測量Java應用程序的CPU和內存佔用率已經有884次圍觀

http://coctec.com/docs/java/show-post-61848.html