Related
I'm writing a little Java program which uses PsExec.exe from cmd launched using ProcessBuilder to copy and install an application on networked PC (the number of PC that will need to be installed can vary from 5 to 50).
The program works fine if I launched ProcessBuilder for each PC sequentially.
However to speed things up I would like to implement some form of MultiThreading which could allow me to install 5 PC's at the time concurrently (one "batch" of 5 X Processbuilder processes untill all PC's have been installed).
I was thinking of using a Fixed Thread Pool in combination with a Callable interface (each execution of PsExec returns a value which indicates if the execution was succesfull and which I have to evaluate).
The code used for the ProcessBuilder is:
// Start iterating over all PC in the list:
for(String pc : pcList)
{
counter++;
logger.info("Starting the installation of remote pc: " + pc);
updateMessage("Starting the installation of remote pc: " + pc);
int exitVal = 99;
logger.debug("Exit Value set to 99");
try
{
ProcessBuilder pB = new ProcessBuilder();
pB.command("cmd", "/c",
"\""+psExecPath+"\"" + " \\\\" + pc + userName + userPassword + " -c" + " -f" + " -h" + " -n 60 " +
"\""+forumViewerPath+"\"" + " -q "+ forumAddress + remotePath + "-overwrite");
logger.debug(pB.command().toString());
pB.redirectError();
Process p = pB.start();
InputStream stErr = p.getErrorStream();
InputStreamReader esr = new InputStreamReader(stErr);
BufferedReader bre = new BufferedReader(esr);
String line = null;
line = bre.readLine();
while (line != null)
{
if(!line.equals(""))
logger.info(line);
line = bre.readLine();
}
exitVal = p.waitFor();
} catch (IOException ex)
{
logger.info("Exception occurred during installation of PC: \n"+pc+"\n "+ ex);
notInstalledPc.add(pc);
}
if(exitVal != 0)
{
notInstalledPc.add(pc);
ret = exitVal;
updateMessage("");
updateMessage("The remote pc: " + pc + " was not installed");
logger.info("The remote pc: " + pc + " was not installed. The error message returned was: \n"+getError(exitVal) + "\nProcess exit code was: " + exitVal);
}
else
{
updateMessage("");
updateMessage("The remote pc: " + pc + " was succesfully installed");
logger.info("The remote pc: " + pc + " was succesfully installed");
}
Now I've read some info on how to implement Callable and I would like to enclose my ProcessBuilder in a Callable interface and then submit all the Tasks for running in the for loop.
Am I on the right track?
You can surely do that. I suppose you want to use Callable instead of runnable to get the result of your exitVal ?
It doesn't seem like you have any shared data between your threads, so I think you should be fine. Since you even know how many Callables you are going to make you could create a collection of your Callables and then do
List<Future<SomeType>> results = pool.invokeAll(collection)
This would allow for easier handling of your result. Probably the most important thing you need to figure out when deciding on whether or not to use a threadpool is what to do if your program terminates while threads are still running; Do you HAVE to finish what you're doing in the threads, do you need to have seamless handling of errors etc.
Check out java threadpools doc: https://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html
or search the web, there are tons of posts/blogs about when or not to use threadpools.
But seems like you're on the right track!
Thank you for your reply! It definitely put me on the right track. I ended up implementing it like this:
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5); //NEW
List<Future<List<String>>> resultList = new ArrayList<>();
updateMessage("Starting the installation of all remote pc entered...");
// Start iterating over all PC in the list:
for(String pc : pcList)
{
counter++;
logger.debug("Starting the installation of remote pc: " + pc);
psExe p = new psExe(pc);
Future<List<String>> result = executor.submit(p);//NEW
resultList.add(result);
}
for(Future<List<String>> future : resultList)
{.......
in the last for loop I read the result of my operations and write them on screen or act according to the result returned.
I still have a couple of questions as it is not really clear to me:
1 - If I have 20 PC and submit all the callable threads to the pool in my first For loop, do I get it correctly that only 5 threads will be started (threadpool size = 5) but all will already be created and put in Wait status, and only as soon as the first running Thread is complete and returns a result value the next one will automatically start until all PC are finished?
2 - What is the difference (advantage) of using invokeall() as you suggested compared to the method I used (submit() inside the for loop)?
Thank you once more for your help...I really Love this Java stuff!! ;-)
I'd like to generate alarms on my Java desktop application :
alarms set with a specific date/time which can be in 5 minutes or 5 months
I need to be able to create a SWT application when the alarm is triggered
I need this to be able to work on any OS. The software users will likely have Windows (90% of them), and the rest Mac OS (including me)
the software license must allow me to use it in a commercial program, without requiring to open source it (hence, no GPL)
I cannot require the users to install Cygwin, so the implementation needs to be native to Windows and Unix
I am developing using Java, Eclipse, SWT and my application is deployed from my server using Java Web Start. I'm using Mac OS X.6 for developing.
I think I have a few options:
Run my application at startup, and handle everything myself;
Use a system service.
Use the cron table on Unix, and Scheduled Tasks on Windows
Run at startup
I don't really like this solution, I'm hoping for something more elegant.
Refs: I would like to run my Java program on System Startup on Mac OS/Windows. How can I do this?
System service
If I run it as a system service, I can benefit from this, because the OS will ensure that my software:
is always running
doesn't have/need a GUI
restarts on failure
I've researched some resources that I can use:
run4j — CPL — runs on Windows only, seems like a valid candidate
jsvc — Apache 2.0 — Unix only, seems like a valid candidate
Java Service Wrapper — Various — I cannot afford paid licenses, and the free one is a GPL. Hence, I don't want to/can't use this
My questions in the system service options are:
Are there other options?
Is my planned implementation correct:
at the application startup, check for existence of the service
if it is not installed:
escalate the user to install the service (root on Unix, UAC on Windows)
if the host OS is Windows, use run4j to register the service
if the host OS is Unix, use jsvc to register the service
if it is not running, start it
Thus, at the first run, the application will install the service and start it. When the application closes the service is still running and won't need the application ever again, except if it is unregistered.
However, I think I still miss the "run on startup" feature.
Am I right? Am I missing something?
cron / Task Scheduler
On Unix, I can easily use the cron table without needing the application to escalate the user to root. I don't need to handle restarts, system date changes, etc. Seems nice.
On Windows, I can use the Task Scheduler, even in command-line using At or SchTasks. This seems nice, but I need this to be compatible from XP up to 7, and I can't easily test this.
So what would you do? Did I miss something? Do you have any advice that could help me pick the best and most elegant solution?
Bicou: Great that you shared your solution!
Note that the "schtasks.exe" has some localization issues, if you want to create a daily trigger with it, on an English Windows you'd have to use "daily", on a German one (for example) you'd have to use "täglich" instead.
To resolve this issue I've implemented the call to schtasks.exe with the /xml-option, providing a temporary xml-file which I create by template.
The easiest way to create such a template is to create a task "by hand" and use the "export"-function in the task management GUI tool.
Of the available options you have listed, IMHO Option 3 is better.
As you are looking only for an external trigger to execute the application, CRON or Scheduled tasks are better solutions than other options you have listed. By this way, you remove a complexity from your application and also your application need not be running always. It could be triggered externally and when the execution is over, your application will stop. Hence, unnecessary resource consumption is avoided.
Here's what I ended up implementing:
public class AlarmManager {
public static final String ALARM_CLI_FORMAT = "startalarm:";
public static SupportedOS currentOS = SupportedOS.UNSUPPORTED_OS;
public enum SupportedOS {
UNSUPPORTED_OS,
MAC_OS,
WINDOWS,
}
public AlarmManager() {
final String osName = System.getProperty("os.name");
if (osName == null) {
L.e("Unable to retrieve OS!");
} else if ("Mac OS X".equals(osName)) {
currentOS = SupportedOS.MAC_OS;
} else if (osName.contains("Windows")) {
currentOS = SupportedOS.WINDOWS;
} else {
L.e("Unsupported OS: "+osName);
}
}
/**
* Windows only: name of the scheduled task
*/
private String getAlarmName(final long alarmId) {
return new StringBuilder("My_Alarm_").append(alarmId).toString();
}
/**
* Gets the command line to trigger an alarm
* #param alarmId
* #return
*/
private String getAlarmCommandLine(final long alarmId) {
return new StringBuilder("javaws -open ").append(ALARM_CLI_FORMAT).append(alarmId).append(" ").append(G.JNLP_URL).toString();
}
/**
* Adds an alarm to the system list of scheduled tasks
* #param when
*/
public void createAlarm(final Calendar when) {
// Create alarm
// ... stuff here
final long alarmId = 42;
// Schedule alarm
String[] commandLine;
Process child;
final String alarmCL = getAlarmCommandLine(alarmId);
try {
switch (currentOS) {
case MAC_OS:
final String cron = new SimpleDateFormat("mm HH d M '*' ").format(when.getTime()) + alarmCL;
commandLine = new String[] {
"/bin/sh", "-c",
"crontab -l | (cat; echo \"" + cron + "\") | crontab"
};
child = Runtime.getRuntime().exec(commandLine);
break;
case WINDOWS:
commandLine = new String[] {
"schtasks",
"/Create",
"/ST "+when.get(Calendar.HOUR_OF_DAY) + ":" + when.get(Calendar.MINUTE),
"/SC ONCE",
"/SD "+new SimpleDateFormat("dd/MM/yyyy").format(when.getTime()), // careful with locale here! dd/MM/yyyy or MM/dd/yyyy? I'm French! :)
"/TR \""+alarmCL+"\"",
"/TN \""+getAlarmName(alarmId)+"\"",
"/F",
};
L.d("create command: "+Util.join(commandLine, " "));
child = Runtime.getRuntime().exec(commandLine);
break;
}
} catch (final IOException e) {
L.e("Unable to schedule alarm #"+alarmId, e);
return;
}
L.i("Created alarm #"+alarmId);
}
/**
* Removes an alarm from the system list of scheduled tasks
* #param alarmId
*/
public void removeAlarm(final long alarmId) {
L.i("Removing alarm #"+alarmId);
String[] commandLine;
Process child;
try {
switch (currentOS) {
case MAC_OS:
commandLine = new String[] {
"/bin/sh", "-c",
"crontab -l | (grep -v \""+ALARM_CLI_FORMAT+"\") | crontab"
};
child = Runtime.getRuntime().exec(commandLine);
break;
case WINDOWS:
commandLine = new String[] {
"schtasks",
"/Delete",
"/TN \""+getAlarmName(alarmId)+"\"",
"/F",
};
child = Runtime.getRuntime().exec(commandLine);
break;
}
} catch (final IOException e) {
L.e("Unable to remove alarm #"+alarmId, e);
}
}
public void triggerAlarm(final long alarmId) {
// Do stuff
//...
L.i("Hi! I'm alarm #"+alarmId);
// Remove alarm
removeAlarm(alarmId);
}
}
Usage is simple. Schedule a new alarm using:
final AlarmManager m = new AlarmManager();
final Calendar cal = new GregorianCalendar();
cal.add(Calendar.MINUTE, 1);
m.createAlarm(cal);
Trigger an alarm like this:
public static void main(final String[] args) {
if (args.length >= 2 && args[1] != null && args[1].contains(AlarmManager.ALARM_CLI_FORMAT)) {
try {
final long alarmId = Long.parseLong(args[1].replace(AlarmManager.ALARM_CLI_FORMAT, ""));
final AlarmManager m = new AlarmManager();
m.triggerAlarm(alarmId);
} catch (final NumberFormatException e) {
L.e("Unable to parse alarm !", e);
}
}
}
Tested on Mac OS X.6 and Windows Vista. The class L is an helper to System.out.println and G holds my global constants (here, my JNLP file on my server used to launch my application).
You can also try using Quartz http://quartz-scheduler.org/ . It has a CRON like syntax to schedule jobs.
I believe your scenario is correct. Since services are system specific things, IMHO you should not user a generic package to cover them all, but have a specific mechanism for every system.
I'd like to test which of two implementation in java of a problem is the fastest.
I've 2 jar files with the two implementation I can execute from the terminal. I want to execute both about 100 times and analyse which one is the fastest to do that or that task.
In the output one of the line is "executing time : xx", I need to catch this xx to put in an array or something like that to analyse it later
While I'm executing the jar, I've also to give some input commands (like a name to search or a number).
I don't with which language is it the easiest to do it.
I know the basis in Bash and Python
thank you
Excuse me, but Why you dont make a jar that call n-times any jar?
For Example:
public static void main(String[] args) {
for(int i=0;i<1000;i++) {
params1 = randomNumber();
params2 = randomName();
...
paramsN = randomTime();
MYJAR1 test1 = new MYJAR1(params1,params2,...,paramsN);
timerStart();
test1.start();
timerEnd();
printTimer();
}
}
and make the same for the second jar.
I hope that my idea can help you.
Bye
If you use (say) Perl, you can spawn the process off, capture the output via a redirection and filter the times. e.g.
if (open(PROCESS, "myproc |")) {
while(<PROCESS>) {
if (/executing time : (\d+)/) {
# $1 has the time now
}
}
}
(not compiled or tested!)
Perhaps the simplest way of analysing the data is to redirect the above output to a file, and then load it into Excel. You can then use Excel to calculate averages/max/mins and std. devs (if you wish) trivially.
Ok I've found with 3 different scripts ^^
in the java code, for each function :
long time1 = System.currentTimeMillis();
// some code for function 1
long time2 = System.currentTimeMillis();
try {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(new File("log.txt"),true));
osw.write("function1,"+(time2 - time1)+"\n");
osw.close();
} catch (FileNotFoundException ex) {
ex.printStackTrace();
} catch (IOException ex) {
ex.printStackTrace();
}
a bash code to run the 500 times the two algorithms
#!/bin/bash
i=0
while [ $i -lt 500 ]
do
./algo1.ex
./algo2.ex
i=$(( $i+1 ))
done
an (or two actually, one for each algorithm) expect code to do the command during the execution
#!/usr/bin/expect -f
spawn java -jar algo1.jar
expect "enter your choice" {send "1\n"}
expect "enter a name :" {send "Peju, M.\n"}
expect "enter your choice" {send "2\n"}
expect "enter a name :" {send "Creasy, R. J.\n"}
expect "enter your choice" {send "0\n"}
exit
As I didn't know how to do it in bash, to count I used a python code
#!/usr/bin/python
# -*- coding: utf8 -*-
import sys
if __name__ == "__main__":
if sys.argv[1:]:
arg = sys.argv[1]
filin = open(arg, 'r')
line = filin.readline()
res= 0, 0, 0
int n = 1
while line !='':
t = line.partition(',')
if t[0] == "function1":
res = (res[0] + int(t[2]), res[1], res[2])
if t[0] == "function2":
res = (res[0], res[1] + int(t[2]), res[2])
if t[0] == "function3":
res = (res[0], res[1], res[2] + int(t[2]))
ligne = filin.readline()
n = n+1
print res
print (res[0]/(n/3.0), res[1]/(n/3.0), res[2]/(n/3.0))
filin.close()
and it works
but thanks for your propositions
We ship a Java application whose memory demand can vary quite a lot depending on the size of the data it is processing. If you don't set the max VM (virtual memory) size, quite often
the JVM quits with an GC failure on big data.
What we'd like to see, is the JVM requesting more memory, as GC fails to provide enough, until the total available VM is exhausted. e.g., start with 128Mb, and increase geometrically (or some other step) whenever the GC failed.
The JVM ("Java") command line allows explicit setting of max VM sizes (various -Xm* commands), and you'd think that would be designed to be adequate. We try to do this in a .cmd file that we ship with the application. But if you pick any specific number,
you get one of two bad behaviors: 1) if your number is small enough to work on most
target systems (e.g., 1Gb), it isn't big enough for big data, or 2) if you make it very large, the JVM refuses to run on those systems whose actual VM is smaller than specified.
How does one set up Java to use the available VM when needed, without knowing that number in advance, and without grabbing it all on startup?
You can also use the option:
-XX:+AggressiveHeap
This according to the [documentation][1]:
The -XX:+AggressiveHeap option
inspects the machine resources (size
of memory and number of processors)
and attempts to set various parameters
to be optimal for long-running, memory
allocation-intensive jobs. It was
originally intended for machines with
large amounts of memory and a large
number of CPUs, but in the J2SE
platform, version 1.4.1 and later it
has shown itself to be useful even on
four processor machines. With this
option the throughput collector
(-XX:+UseParallelGC) is used along
with adaptive sizing
(-XX:+UseAdaptiveSizePolicy). The
physical memory on the machines must
be at least 256MB before
AggressiveHeap can be used. The size
of the initial heap is calculated
based on the size of the physical
memory and attempts to make maximal
use of the physical memory for the
heap (i.e., the algorithms attempt to
use heaps nearly as large as the total
physical memory).
[1]: http://java.sun.com/docs/hotspot/gc1.4.2/#4.2.2. AggressiveHeap|outline
The max VM sizes indeed answer to that need (it sets the max value, but the VM will take only necessary, step by step), but if you need several configurations, besides supplying different "cmd" files, I don't really see a way (though i'll search a bit more)
[edit]
How about using a first program/script (or even another java program), which would check the available resources for the system, and then only call your program with the appropriate -Xm, according to what it retrieved from system ?
That way it would adapt to machines, even if you don't know them before. Could be an idea...
[second edit]
Ok, this has been proposed already by skaffman, my bad.
We have a small C application that we use for launching all of our Java applications via JNI. This allows us to:
Have a meaningful process name (esp important under windows)
Have our own icons (again, important for Windows)
Dynamically build classpaths (we parse out the contents of the /lib file to auto-include all jars)
For our apps, we just hard code the heap limit, but you could easily dynamically configure max heap size based on available memory.
This sort of little app is actually pretty easy to do (it's one of the easiest things to do with JNI). A good starting point would be the source for the JDK (there's a sub-folder for java.exe itself that you can use - that's what we did). Most folks are quite surprised to find that java.exe is a little tiny application (< 200 lines of code) that just invokes JNI and passes command line arguments in (heck, even the use of a method called main() is pretty optional once you start launching things yourself).
Here's code that not only starts up the JVM, etc... but also determines the maximum heap space based on available RAM of the computer. This is a lot of code for an SO post, and it's not at all pretty - but this is battle hardened code - it's been used for almost a decade over many hundreds of installs, etc... Enjoy :
#include <windows.h>
#include <jni.h>
#include <string>
#include <sstream>
using namespace std;
#define STARTUP_CLASS "some/path/to/YourStartupClass"
void vShowError(string sErrorMessage);
void vShowJREError(string sErrorMessage);
void vShowLastError(string sErrorMessage);
void vDestroyVM(JNIEnv *env, JavaVM *jvm);
void vAddOption(string& sName);
string GetClassPath(string root);
string GetJREPath();
int getMaxHeapAvailable(int permGenMB, int maxHeapMB);
JavaVMOption* vm_options;
int mctOptions = 0;
int mctOptionCapacity = 0;
boolean GetApplicationHome(char *buf, jint sz);
typedef jint (CALLBACK *CreateJavaVM)(JavaVM
**pvm, JNIEnv **penv, void *args);
boolean PathExists(string &path)
{
DWORD dwAttr = GetFileAttributes(path.c_str());
if (dwAttr == 0xffffffff)
return FALSE;
else
return TRUE;
}
// returns TRUE is there was an exception, FALSE otherwise
BOOL GetExceptionString(JNIEnv* jenv, string &result)
{
jthrowable ex;
if (NULL != (ex = jenv->ExceptionOccurred())) {
// clear exception
jenv->ExceptionClear();
jmethodID gmID = jenv->GetMethodID(
jenv->FindClass("java/lang/Throwable"),
"getMessage",
"()Ljava/lang/String;");
jstring jerrStr = (jstring)jenv->CallObjectMethod(ex,gmID);
// now you can look at the error message string
if (jerrStr != NULL){ // make sure getMessage() didn't return null
const char *errStr = jenv->GetStringUTFChars(jerrStr,0);
result = errStr;
jenv->ReleaseStringUTFChars(jerrStr, errStr);
} else {
result = "null";
}
return TRUE;
} else {
return FALSE;
}
}
BOOL GetJRESystemProperty(JNIEnv *env, string propname, string &propval, string &errmessage)
{
// now check for minimum JRE version requirement
jclass cls = env->FindClass("java/lang/System");
if (cls == NULL){
errmessage = "Unable to interact with Java Virtual Machine - please visit www.java.com and confirm that your Java installation is valid.";
return FALSE;
}
jmethodID mid = env->GetStaticMethodID(cls, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
if (mid == NULL){
errmessage = "Unable to obtain Java runtime system properties - please visit www.java.net and confirm that your Java installation is valid.";
return FALSE;
}
jstring propName = env->NewStringUTF( propname.c_str() );
jstring result = (jstring) env->CallStaticObjectMethod(cls, mid, propName);
const char* utfResult = env->GetStringUTFChars( result, NULL );
if (utfResult == NULL){
errmessage = "Unable to obtain Java runtime system property " + propname + " - please visit www.java.net and confirm that your Java installation is valid.";
return FALSE;
}
propval = utfResult;
env->ReleaseStringUTFChars( result, utfResult );
return TRUE;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) {
JNIEnv *env;
JavaVM *jvm;
jint jintVMStartupReturnValue;
jclass jclassStartup;
jmethodID midStartup;
// Path Determination
// --- application home
char home[2000];
if (!GetApplicationHome(home, sizeof(home))) {
vShowError("Unable to determine application home.");
return 0;
}
string sAppHome(home);
string sOption_AppHome = "-Dapplication.home=" + sAppHome;
string sJREPath = GetJREPath();
// --- VM Path
string sRuntimePath = sJREPath + "\\bin\\client\\"; // must contain jvm.dll
string sJVMpath = sRuntimePath + "jvm.dll";
// --- boot path
string sBootPath = sJREPath + "\\lib";
string sOption_BootPath = "-Dsun.boot.class.path=" + sBootPath;
// --- class path
//string sClassPath = sAppHome + "\\lib;" + sAppHome + "\\lib\\" + APP_JAR + ";" + sAppHome + "\\lib\\log4j-1.2.7.jar";
string cpRoot = sAppHome + "\\";
string sClassPath = GetClassPath(cpRoot);
string sOption_ClassPath = "-Djava.class.path=" + sClassPath;
string sOption_JavaLibraryPath = "-Djava.library.path=" + sAppHome + "\\lib";
int maxHeapBM = 768;
int argStart = 1; // the first argument passed in that should be passed along to the JVM
if(__argc > 1){
string maxheapstr = __argv[1];
if (maxheapstr.substr(0, 9).compare("/maxheap=") == 0){
maxheapstr = maxheapstr.substr(9);
maxHeapBM = atoi(maxheapstr.c_str());
argStart++;
}
}
// we now use adaptive max heap size determination - we try for 768MB of heap, but if we don't get it, we can back off and use less instead of failing the launch
// note: we had problems going for 1024 heap at TrueNorth - it would throttle back to 848 and fail with error -4 no matter what I did
int maxHeapMB = getMaxHeapAvailable(62, maxHeapBM);
stringstream ss;
ss << "-Xmx";
ss << maxHeapMB;
ss << "m";
string sOption_HeapSpace = ss.str();
string sOption_PermSize = "-XX:MaxPermSize=62m";
string sOption_HeapDump = "-XX:+HeapDumpOnOutOfMemoryError";
if (strstr(szCmdLine, "/launcher_verbose") != NULL){
string msg = "App Home = ";
msg += sAppHome;
msg += "\nJRE Path = ";
msg += sJREPath;
msg += "\nRuntime Path = ";
msg += sRuntimePath;
msg += "\nClass Path = ";
msg += sClassPath;
msg += "\nHeap argument = ";
msg += sOption_HeapSpace;
msg += "\nPermsize argument = ";
msg += sOption_PermSize;
msg += "\nHeap dump = ";
msg += sOption_HeapDump;
msg += "\njava.library.path = ";
msg += sOption_JavaLibraryPath;
msg += "\nCommand line = ";
msg += szCmdLine;
FILE *f = fopen("launcher.txt", "w");
fprintf(f, "%s", msg.c_str());
fclose(f);
MessageBox(0, msg.c_str(), "Launcher Verbose Info", MB_OK);
}
// setup VM options
// vAddOption(string("-verbose"));
vAddOption(sOption_ClassPath);
vAddOption(sOption_AppHome);
vAddOption(sOption_HeapSpace);
vAddOption(sOption_PermSize);
vAddOption(sOption_HeapDump);
vAddOption(sOption_JavaLibraryPath);
// initialize args
JavaVMInitArgs vm_args;
vm_args.version = 0x00010002;
vm_args.options = vm_options;
vm_args.nOptions = mctOptions;
vm_args.ignoreUnrecognized = JNI_TRUE;
// need to diddle with paths to ensure that jvm can find correct libraries - see http://www.duckware.com/tech/java6msvcr71.html
string sBinPath = sJREPath + "\\bin";
char originalCurrentDirectory[4096];
GetCurrentDirectory(4095, originalCurrentDirectory);
SetCurrentDirectory(sBinPath.c_str());
// Dynamic binding to SetDllDirectory()
typedef BOOL (WINAPI *LPFNSDD)(LPCTSTR lpPathname);
HINSTANCE hKernel32 = GetModuleHandle("kernel32");
LPFNSDD lpfnSetDllDirectory = (LPFNSDD)GetProcAddress(hKernel32, "SetDllDirectoryA");
if (lpfnSetDllDirectory){
lpfnSetDllDirectory(sBinPath.c_str());
}
// load jvm library
HINSTANCE hJVM = LoadLibrary(sJVMpath.c_str());
SetCurrentDirectory(originalCurrentDirectory);
if (lpfnSetDllDirectory){
lpfnSetDllDirectory(NULL);
}
if( hJVM == NULL ){
vShowJREError("Java does not appear to be installed on this machine. Click OK to go to www.java.com where you can download and install Java");
return 0;
}
// try to start 1.2/3/4 VM
// uses handle above to locate entry point
CreateJavaVM lpfnCreateJavaVM = (CreateJavaVM)
GetProcAddress(hJVM, "JNI_CreateJavaVM");
jintVMStartupReturnValue = (*lpfnCreateJavaVM)(&jvm, &env, &vm_args);
// test for success
if (jintVMStartupReturnValue < 0) {
stringstream ss;
ss << "There is a problem with the 32 bit Java installation on this computer (";
ss << jintVMStartupReturnValue;
ss << "). Click OK to go to www.java.com where you can download and re-install 32 bit Java";
vShowJREError(ss.str());
// I don't think we should destroy the VM - it never was created...
//vDestroyVM(env, jvm);
return 0;
}
//now check for minimum jvm version
string version = "";
string errormsg = "";
if (!GetJRESystemProperty(env, "java.specification.version", version, errormsg)){
vShowJREError(errormsg);
vDestroyVM(env, jvm);
return 0;
}
double verf = atof(version.c_str());
if (verf < 1.599f){
string sErrorMessage = "This application requires Java Runtime version 1.6 or above, but your runtime is version " + version + "\n\nClick OK to go to www.java.com and update to the latest Java Runtime Environment";
vShowJREError(sErrorMessage);
vDestroyVM(env, jvm);
return 0;
}
// find startup class
string sStartupClass = STARTUP_CLASS;
// notice dots are translated to slashes
jclassStartup = env->FindClass(sStartupClass.c_str());
if (jclassStartup == NULL) {
string sErrorMessage = "Unable to find startup class [" + sStartupClass + "]";
vShowError(sErrorMessage);
vDestroyVM(env, jvm);
return 0;
}
// find startup method
string sStartupMethod_Identifier = "main";
string sStartupMethod_TypeDescriptor =
"([Ljava/lang/String;)V";
midStartup =
env->GetStaticMethodID(jclassStartup,
sStartupMethod_Identifier.c_str(),
sStartupMethod_TypeDescriptor.c_str());
if (midStartup == NULL) {
string sErrorMessage =
"Unable to find startup method ["
+ sStartupClass + "."
+ sStartupMethod_Identifier
+ "] with type descriptor [" +
sStartupMethod_TypeDescriptor + "]";
vShowError(sErrorMessage);
vDestroyVM(env, jvm);
return 0;
}
// create array of args to startup method
jstring jstringExampleArg;
jclass jclassString;
jobjectArray jobjectArray_args;
jstringExampleArg = env->NewStringUTF("example string");
if (jstringExampleArg == NULL){
vDestroyVM(env, jvm);
return 0;
}
jclassString = env->FindClass("java/lang/String");
jobjectArray_args = env->NewObjectArray(__argc-argStart, jclassString, jstringExampleArg);
if (jobjectArray_args == NULL){
vDestroyVM(env, jvm);
return 0;
}
int count;
for (count = argStart; count < __argc; count++){
env->SetObjectArrayElement(jobjectArray_args, count-1, env->NewStringUTF(__argv[count]));
}
// call the startup method -
// this starts the Java program
env->CallStaticVoidMethod(jclassStartup, midStartup, jobjectArray_args);
string errstr;
if (GetExceptionString(env, errstr)){
vShowError(errstr);
}
// attempt to detach main thread before exiting
if (jvm->DetachCurrentThread() != 0) {
vShowError("Could not detach main thread.\n");
}
// this call will hang as long as there are
// non-daemon threads remaining
jvm->DestroyJavaVM();
return 0;
}
void vDestroyVM(JNIEnv *env, JavaVM *jvm)
{
if (env->ExceptionOccurred()) {
env->ExceptionDescribe();
}
jvm->DestroyJavaVM();
}
void vShowError(string sError) {
MessageBox(NULL, sError.c_str(), "Startup Error", MB_OK);
}
void vShowJREError(string sError) {
MessageBox(NULL, sError.c_str(), "Startup Error", MB_OK);
ShellExecute(NULL, "open", "http://www.java.com", NULL, NULL, SW_SHOWNORMAL);
}
/* Shows an error message in an OK box with the
system GetLastError appended in brackets */
void vShowLastError(string sLocalError) {
LPVOID lpSystemMsgBuf;
FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &lpSystemMsgBuf, 0, NULL );
string sSystemError = string((LPTSTR)lpSystemMsgBuf);
vShowError(sLocalError + " [" + sSystemError + "]");
}
void vAddOption(string& sValue) {
mctOptions++;
if (mctOptions >= mctOptionCapacity) {
if (mctOptionCapacity == 0) {
mctOptionCapacity = 3;
vm_options = (JavaVMOption*)malloc(mctOptionCapacity * sizeof(JavaVMOption));
} else {
JavaVMOption *tmp;
mctOptionCapacity *= 2;
tmp = (JavaVMOption*)malloc(mctOptionCapacity * sizeof(JavaVMOption));
memcpy(tmp, vm_options, (mctOptions-1) * sizeof(JavaVMOption));
free(vm_options);
vm_options = tmp;
}
}
vm_options[mctOptions-1].optionString = (char*)sValue.c_str();
}
/* If buffer is "c:\app\bin\java",
* then put "c:\app" into buf. */
jboolean GetApplicationHome(char *buf, jint sz) {
char *cp;
GetModuleFileName(0, buf, sz);
*strrchr(buf, '\\') = '\0';
if ((cp = strrchr(buf, '\\')) == 0) {
// This happens if the application is in a
// drive root, and there is no bin directory.
buf[0] = '\0';
return JNI_FALSE;
}
return JNI_TRUE;
}
string GetClassPath(string root){
string rootWithBackslash = root;
if (rootWithBackslash[rootWithBackslash.length()-1] != '\\')
rootWithBackslash += "\\";
string cp = rootWithBackslash + "classes\\"; //first entry in the cp
string libPathWithBackslash = rootWithBackslash + "lib\\";
// now find all jar files...
string searchSpec = libPathWithBackslash;
searchSpec = libPathWithBackslash + "*.jar";
WIN32_FIND_DATA fd;
HANDLE find = FindFirstFile(searchSpec.c_str(), &fd);
while (find != NULL){
cp += ";";
cp += libPathWithBackslash;
cp += fd.cFileName;
if (!FindNextFile(find, &fd)){
FindClose(find);
find = NULL;
}
}
return cp;
}
string GetJREPath(){
// first, check for JRE in application directory
char home[2000];
if (!GetApplicationHome(home, sizeof(home))) {
vShowError("Unable to determine application home.");
return 0;
}
string sJREPath(home);
sJREPath += "\\jre";
if (PathExists(sJREPath)){
return sJREPath;
}
/* - don't check JAVA_HOME - it may be incorrect...
// next, check the JAVA_HOME environment variable
GetEnvironmentVariable("JAVA_HOME", home, sizeof(home));
sJREPath = home;
if (PathExists(sJREPath)){
return sJREPath;
}
*/
// next, check registry
HKEY hKeyJRERoot;
HKEY hKeyJREInstance;
DWORD dwType;
DWORD dwSize;
BYTE *pData;
string valueName;
string value;
LONG regRslt;
sJREPath = "";
regRslt = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\JavaSoft\\Java Runtime Environment", 0, KEY_READ, &hKeyJRERoot);
if (regRslt == ERROR_SUCCESS){
valueName = "CurrentVersion";
regRslt = RegQueryValueEx(hKeyJRERoot, valueName.c_str(), NULL, &dwType, NULL, &dwSize);
if (regRslt == ERROR_SUCCESS){
pData = (BYTE *)malloc(dwSize);
value = "";
regRslt = RegQueryValueEx(hKeyJRERoot, valueName.c_str(), NULL, &dwType, pData, &dwSize);
if (regRslt == ERROR_SUCCESS){
value = (LPCSTR)pData;
}
free(pData);
if (value != ""){
regRslt = RegOpenKeyEx(hKeyJRERoot, value.c_str(), 0, KEY_READ, &hKeyJREInstance);
if (regRslt == ERROR_SUCCESS){
valueName = "JavaHome";
value = "";
regRslt = RegQueryValueEx(hKeyJREInstance, valueName.c_str(), NULL, &dwType, NULL, &dwSize);
if (regRslt == ERROR_SUCCESS){
pData = (BYTE *)malloc(dwSize);
regRslt = RegQueryValueEx(hKeyJREInstance, valueName.c_str(), NULL, &dwType, pData, &dwSize);
if (regRslt == ERROR_SUCCESS){
value = (LPCSTR)pData;
sJREPath = value;
}
free(pData);
}
RegCloseKey(hKeyJREInstance);
}
}
}
RegCloseKey(hKeyJRERoot);
}
return sJREPath;
}
static const DWORD NUM_BYTES_PER_MB = 1024 * 1024;
bool canAllocate(DWORD bytes)
{
LPVOID lpvBase;
lpvBase = VirtualAlloc(NULL, bytes, MEM_RESERVE, PAGE_READWRITE);
if (lpvBase == NULL) return false;
VirtualFree(lpvBase, 0, MEM_RELEASE);
return true;
}
int getMaxHeapAvailable(int permGenMB, int maxHeapMB)
{
DWORD originalMaxHeapBytes = 0;
DWORD maxHeapBytes = 0;
int numMemChunks = 0;
SYSTEM_INFO sSysInfo;
DWORD maxPermBytes = permGenMB * NUM_BYTES_PER_MB; // Perm space is in addition to the heap size
DWORD numBytesNeeded = 0;
GetSystemInfo(&sSysInfo);
// jvm aligns as follows:
// quoted from size_t GenCollectorPolicy::compute_max_alignment() of jdk 7 hotspot code:
// The card marking array and the offset arrays for old generations are
// committed in os pages as well. Make sure they are entirely full (to
// avoid partial page problems), e.g. if 512 bytes heap corresponds to 1
// byte entry and the os page size is 4096, the maximum heap size should
// be 512*4096 = 2MB aligned.
// card_size computation from CardTableModRefBS::SomePublicConstants of jdk 7 hotspot code
int card_shift = 9;
int card_size = 1 << card_shift;
DWORD alignmentBytes = sSysInfo.dwPageSize * card_size;
maxHeapBytes = maxHeapMB * NUM_BYTES_PER_MB + 50*NUM_BYTES_PER_MB; // 50 is an overhead fudge factory per https://forums.oracle.com/forums/thread.jspa?messageID=6463655 (they had 28, I'm bumping it 'just in case')
// make it fit in the alignment structure
maxHeapBytes = maxHeapBytes + (maxHeapBytes % alignmentBytes);
numMemChunks = maxHeapBytes / alignmentBytes;
originalMaxHeapBytes = maxHeapBytes;
// loop and decrement requested amount by one chunk
// until the available amount is found
numBytesNeeded = maxHeapBytes + maxPermBytes;
while (!canAllocate(numBytesNeeded) && numMemChunks > 0)
{
numMemChunks --;
maxHeapBytes = numMemChunks * alignmentBytes;
numBytesNeeded = maxHeapBytes + maxPermBytes;
}
if (numMemChunks == 0) return 0;
// we can allocate the requested size, return it now
if (maxHeapBytes == originalMaxHeapBytes) return maxHeapMB;
// calculate the new MaxHeapSize in megabytes
return maxHeapBytes / NUM_BYTES_PER_MB;
}
One more option... I work on a launcher called WinRun4J, which allows you to specify a max heap size as a percentage of the available memory on the machine its running on (ie. it does a check for the amount of memory available and sets the -Xmx parameter dynamically on startup).
The INI option is "vm.heapsize.max.percent". There is also another option "vm.heapsize.preferred", which sets the -Xmx parameter as the maximum available memory on the machine up to this amount.
I believe some of the other launchers (eg. Launch4J, Janel) offer the same functionality.
I think you're out of luck :-( The -Xms and -Xmx options don't provide that flexibility.
So I think you will need to wrap your JVM invocation with a script that can determine the maximum amount of memory, and then set -Xmx appropriately (probably a .vbs script using WMI on Windows). Or perhaps it asks the users the first time it's run ?
A bit of a pain, I fear.
I think the easiest way to do this would be to launch the JVM via some wrapper application, which would check system resources to determine memory availability, and then launch the JVM with the appropriate -Xmx parameter.
The question then becomes how that wrapper would be written. It may even be possible for the wrapper app to itself be a JVM, although I don't think the API or system properties would expose the necessary information. Maybe a shell script or your choice could get the information.
if you have a lot of time on your hand you could try the following :
Try to obtain what is the needed memory vs input dataset. With this you can split processing in a different set of classes and create a new JVM process to actually process the data. Basically a Manager and a Worker. The Manager would do a basic analysis on the demanded dataset and spawn a Worker with the appropriate memory requirements. You could probably also set your Manager to be aware of the environment and warn the user when they are trying to operate on a dataset their machine cannot handle.
This is pretty much an extension on the answer provided by skaffman but will happen all within the same app as far as the user is concerned.
There is two options in the virtual machine arguments that can be used : -Xms to set the memory size at startup and -Xmx to set the maximum memory size...
You can set a low startup memory and a big maximum one, so the VM will allocate new memory only if needed.
I do not think either the Sun or IBM JVM can do this (I know that the AS/400 one can, but that is most likely not relevant to you).
I would suggest using Java WebStart (and before you discard this, then notice that it has been updated with Java 6 u 10 and is much better suited for launching "local" applications and applet) since it allows you to provide a "small instance", "larger instance", "gigantic instance" as links/icons.
You will most likely look into the "inject application in webstart cache" and "offline"options.
I don't think you can do what you are trying to do; instead you'll have to ship instructions specific to your customers, their systems and their demands of how they can modify your .cmd file to allow for more memory.
Of course, if your product is aimed at very non-technical users, you may wish to hide this behind some more user-friendly config file. E.g.
# HIGH, MEDIUM, LOW - please change as appropriate. The guidelines are:
# * HIGH - users who generate 500 items per day
# * MEDIUM - 200-500 items etc
memoryUsage=MEDIUM
or possibly deploy different config files depending on which product option a user specifies when they order the product in the first place.
In comments you say that the amount of memory that your application actually depends on the input dataset size provided by the user. This suggests that instead of trying to grab all available virtual memory (which may cause problems for the user's other applications) you should be looking at the input dataset size before you start the JVM and using that to estimate the amount of memory the application will need.
Suppose that the user's machine is configured with modest physical memory and a huge swap space. If you launch the JVM with a huge VM size, it could cause severe "thrashing" as the JVM tries to access data in non-resident pages. By contrast, if you give the JVM something more than the application needs and less than the available physical memory, you should be able to run comfortably without thrashing.
I read through the threads but didn't see anything which indicated that the application had undergone some sort of profiling. Normally I'd profile the apps under certain conditions to find hot spots in performance or memory usage. There's probably things that could be improved in most cases.
If you could establish the limits and understand the behavior of the application you could be in a position to better tell your customers what they can or cannot do with the application thereby reducing the amount of support calls and giving you a better idea of what minimum or maximum heap size to ship the product with.
Maybe you could start with this: http://www.eclipse.org/mat/
Have you looked at running jps to give you the PID for your process and then calling jinfo to change the mx option? Not sure if this will work but it may.
[Edit] This would mean that when you think you have a big dataset, you read the total amount of ram somehow (OS dependent I think. See http://forums.sun.com/thread.jspa?messageID=10306570) or you just increase the size until you don't think it is low anymore (if it blows up first, try to capture and display a helpful message such as "your machine is inadequate, time to make a run to Frys").
This discussion is moot if you think that your clients can ask for 2-3GB of RAM on their 32-bit machine. The OS and other apps will be taking their pound of flesh to run as well.
Sounds like your app is reaching the point where it needs a 64-bit operating system and lots more RAM.
I'm currently building a Java app that could end up being run on many different platforms, but primarily variants of Solaris, Linux and Windows.
Has anyone been able to successfully extract information such as the current disk space used, CPU utilisation and memory used in the underlying OS? What about just what the Java app itself is consuming?
Preferrably I'd like to get this information without using JNI.
You can get some limited memory information from the Runtime class. It really isn't exactly what you are looking for, but I thought I would provide it for the sake of completeness. Here is a small example. Edit: You can also get disk usage information from the java.io.File class. The disk space usage stuff requires Java 1.6 or higher.
public class Main {
public static void main(String[] args) {
/* Total number of processors or cores available to the JVM */
System.out.println("Available processors (cores): " +
Runtime.getRuntime().availableProcessors());
/* Total amount of free memory available to the JVM */
System.out.println("Free memory (bytes): " +
Runtime.getRuntime().freeMemory());
/* This will return Long.MAX_VALUE if there is no preset limit */
long maxMemory = Runtime.getRuntime().maxMemory();
/* Maximum amount of memory the JVM will attempt to use */
System.out.println("Maximum memory (bytes): " +
(maxMemory == Long.MAX_VALUE ? "no limit" : maxMemory));
/* Total memory currently available to the JVM */
System.out.println("Total memory available to JVM (bytes): " +
Runtime.getRuntime().totalMemory());
/* Get a list of all filesystem roots on this system */
File[] roots = File.listRoots();
/* For each filesystem root, print some info */
for (File root : roots) {
System.out.println("File system root: " + root.getAbsolutePath());
System.out.println("Total space (bytes): " + root.getTotalSpace());
System.out.println("Free space (bytes): " + root.getFreeSpace());
System.out.println("Usable space (bytes): " + root.getUsableSpace());
}
}
}
The java.lang.management package does give you a whole lot more info than Runtime - for example it will give you heap memory (ManagementFactory.getMemoryMXBean().getHeapMemoryUsage()) separate from non-heap memory (ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage()).
You can also get process CPU usage (without writing your own JNI code), but you need to cast the java.lang.management.OperatingSystemMXBean to a com.sun.management.OperatingSystemMXBean. This works on Windows and Linux, I haven't tested it elsewhere.
For example ... call the get getCpuUsage() method more frequently to get more accurate readings.
public class PerformanceMonitor {
private int availableProcessors = getOperatingSystemMXBean().getAvailableProcessors();
private long lastSystemTime = 0;
private long lastProcessCpuTime = 0;
public synchronized double getCpuUsage()
{
if ( lastSystemTime == 0 )
{
baselineCounters();
return;
}
long systemTime = System.nanoTime();
long processCpuTime = 0;
if ( getOperatingSystemMXBean() instanceof OperatingSystemMXBean )
{
processCpuTime = ( (OperatingSystemMXBean) getOperatingSystemMXBean() ).getProcessCpuTime();
}
double cpuUsage = (double) ( processCpuTime - lastProcessCpuTime ) / ( systemTime - lastSystemTime );
lastSystemTime = systemTime;
lastProcessCpuTime = processCpuTime;
return cpuUsage / availableProcessors;
}
private void baselineCounters()
{
lastSystemTime = System.nanoTime();
if ( getOperatingSystemMXBean() instanceof OperatingSystemMXBean )
{
lastProcessCpuTime = ( (OperatingSystemMXBean) getOperatingSystemMXBean() ).getProcessCpuTime();
}
}
}
I think the best method out there is to implement the SIGAR API by Hyperic. It works for most of the major operating systems ( darn near anything modern ) and is very easy to work with. The developer(s) are very responsive on their forum and mailing lists. I also like that it is GPL2 Apache licensed. They provide a ton of examples in Java too!
SIGAR == System Information, Gathering And Reporting tool.
There's a Java project that uses JNA (so no native libraries to install) and is in active development. It currently supports Linux, OSX, Windows, Solaris and FreeBSD and provides RAM, CPU, Battery and file system information.
https://github.com/oshi/oshi
For windows I went this way.
com.sun.management.OperatingSystemMXBean os = (com.sun.management.OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
long physicalMemorySize = os.getTotalPhysicalMemorySize();
long freePhysicalMemory = os.getFreePhysicalMemorySize();
long freeSwapSize = os.getFreeSwapSpaceSize();
long commitedVirtualMemorySize = os.getCommittedVirtualMemorySize();
Here is the link with details.
You can get some system-level information by using System.getenv(), passing the relevant environment variable name as a parameter. For example, on Windows:
System.getenv("PROCESSOR_IDENTIFIER")
System.getenv("PROCESSOR_ARCHITECTURE")
System.getenv("PROCESSOR_ARCHITEW6432")
System.getenv("NUMBER_OF_PROCESSORS")
For other operating systems the presence/absence and names of the relevant environment variables will differ.
Add OSHI dependency via maven:
<dependency>
<groupId>com.github.dblock</groupId>
<artifactId>oshi-core</artifactId>
<version>2.2</version>
</dependency>
Get a battery capacity left in percentage:
SystemInfo si = new SystemInfo();
HardwareAbstractionLayer hal = si.getHardware();
for (PowerSource pSource : hal.getPowerSources()) {
System.out.println(String.format("%n %s # %.1f%%", pSource.getName(), pSource.getRemainingCapacity() * 100d));
}
Have a look at the APIs available in the java.lang.management package. For example:
OperatingSystemMXBean.getSystemLoadAverage()
ThreadMXBean.getCurrentThreadCpuTime()
ThreadMXBean.getCurrentThreadUserTime()
There are loads of other useful things in there as well.
Usually, to get low level OS information you can call OS specific commands which give you the information you want with Runtime.exec() or read files such as /proc/* in Linux.
CPU usage isn't straightforward -- java.lang.management via com.sun.management.OperatingSystemMXBean.getProcessCpuTime comes close (see Patrick's excellent code snippet above) but note that it only gives access to time the CPU spent in your process. it won't tell you about CPU time spent in other processes, or even CPU time spent doing system activities related to your process.
for instance i have a network-intensive java process -- it's the only thing running and the CPU is at 99% but only 55% of that is reported as "processor CPU".
don't even get me started on "load average" as it's next to useless, despite being the only cpu-related item on the MX bean. if only sun in their occasional wisdom exposed something like "getTotalCpuTime"...
for serious CPU monitoring SIGAR mentioned by Matt seems the best bet.
On Windows, you can run the systeminfo command and retrieves its output for instance with the following code:
private static class WindowsSystemInformation
{
static String get() throws IOException
{
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec("systeminfo");
BufferedReader systemInformationReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder stringBuilder = new StringBuilder();
String line;
while ((line = systemInformationReader.readLine()) != null)
{
stringBuilder.append(line);
stringBuilder.append(System.lineSeparator());
}
return stringBuilder.toString().trim();
}
}
If you are using Jrockit VM then here is an other way of getting VM CPU usage. Runtime bean can also give you CPU load per processor. I have used this only on Red Hat Linux to observer Tomcat performance. You have to enable JMX remote in catalina.sh for this to work.
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://my.tomcat.host:8080/jmxrmi");
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
MBeanServerConnection conn = jmxc.getMBeanServerConnection();
ObjectName name = new ObjectName("oracle.jrockit.management:type=Runtime");
Double jvmCpuLoad =(Double)conn.getAttribute(name, "VMGeneratedCPULoad");
It is still under development but you can already use jHardware
It is a simple library that scraps system data using Java. It works in both Linux and Windows.
ProcessorInfo info = HardwareInfo.getProcessorInfo();
//Get named info
System.out.println("Cache size: " + info.getCacheSize());
System.out.println("Family: " + info.getFamily());
System.out.println("Speed (Mhz): " + info.getMhz());
//[...]
One simple way which can be used to get the OS level information and I tested in my Mac which works well :
OperatingSystemMXBean osBean =
(OperatingSystemMXBean)ManagementFactory.getOperatingSystemMXBean();
return osBean.getProcessCpuLoad();
You can find many relevant metrics of the operating system here
To get the System Load average of 1 minute, 5 minutes and 15 minutes inside the java code, you can do this by executing the command cat /proc/loadavg using and interpreting it as below:
Runtime runtime = Runtime.getRuntime();
BufferedReader br = new BufferedReader(
new InputStreamReader(runtime.exec("cat /proc/loadavg").getInputStream()));
String avgLine = br.readLine();
System.out.println(avgLine);
List<String> avgLineList = Arrays.asList(avgLine.split("\\s+"));
System.out.println(avgLineList);
System.out.println("Average load 1 minute : " + avgLineList.get(0));
System.out.println("Average load 5 minutes : " + avgLineList.get(1));
System.out.println("Average load 15 minutes : " + avgLineList.get(2));
And to get the physical system memory by executing the command free -m and then interpreting it as below:
Runtime runtime = Runtime.getRuntime();
BufferedReader br = new BufferedReader(
new InputStreamReader(runtime.exec("free -m").getInputStream()));
String line;
String memLine = "";
int index = 0;
while ((line = br.readLine()) != null) {
if (index == 1) {
memLine = line;
}
index++;
}
// total used free shared buff/cache available
// Mem: 15933 3153 9683 310 3097 12148
// Swap: 3814 0 3814
List<String> memInfoList = Arrays.asList(memLine.split("\\s+"));
int totalSystemMemory = Integer.parseInt(memInfoList.get(1));
int totalSystemUsedMemory = Integer.parseInt(memInfoList.get(2));
int totalSystemFreeMemory = Integer.parseInt(memInfoList.get(3));
System.out.println("Total system memory in mb: " + totalSystemMemory);
System.out.println("Total system used memory in mb: " + totalSystemUsedMemory);
System.out.println("Total system free memory in mb: " + totalSystemFreeMemory);
Hey you can do this with java/com integration. By accessing WMI features you can get all the information.
Not exactly what you asked for, but I'd recommend checking out ArchUtils and SystemUtils from commons-lang3. These also contain some relevant helper facilities, e.g.:
import static org.apache.commons.lang3.ArchUtils.*;
import static org.apache.commons.lang3.SystemUtils.*;
System.out.printf("OS architecture: %s\n", OS_ARCH); // OS architecture: amd64
System.out.printf("OS name: %s\n", OS_NAME); // OS name: Linux
System.out.printf("OS version: %s\n", OS_VERSION); // OS version: 5.18.16-200.fc36.x86_64
System.out.printf("Is Linux? - %b\n", IS_OS_LINUX); // Is Linux? - true
System.out.printf("Is Mac? - %b\n", IS_OS_MAC); // Is Mac? - false
System.out.printf("Is Windows? - %b\n", IS_OS_WINDOWS); // Is Windows? - false
System.out.printf("JVM name: %s\n", JAVA_VM_NAME); // JVM name: Java HotSpot(TM) 64-Bit Server VM
System.out.printf("JVM vendor: %s\n", JAVA_VM_VENDOR); // JVM vendor: Oracle Corporation
System.out.printf("JVM version: %s\n", JAVA_VM_VERSION); // JVM version: 11.0.12+8-LTS-237
System.out.printf("Username: %s\n", getUserName()); // Username: johndoe
System.out.printf("Hostname: %s\n", getHostName()); // Hostname: garage-pc
var processor = getProcessor();
System.out.printf("CPU arch: %s\n", processor.getArch()) // CPU arch: BIT_64
System.out.printf("CPU type: %s\n", processor.getType()); // CPU type: X86