Is it possible to get the current RAM usage of a java Process that is created with Runtime.getRuntime().exec(...);. I am creating a minecraft server instance and I need to monitor the resource usage of the server.
Here is exactly how I am creating the process.
private void runStartCommand(){
try {
lines = new ArrayList<>();
String cmd = "cmd.exe /c cd " + service.getLocation() + "& java -jar -Xmx2G -Xms2G "+service.getLocation()+"spigot-1.9.2.jar";
process = Runtime.getRuntime().exec(cmd);
input = new BufferedReader(new InputStreamReader(process.getInputStream()),8*1024);
writer = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
running = true;
} catch (IOException e) {
e.printStackTrace();
}
}
Bukkit already provides a custom plugin for monitoring and managing Minecraft server performance that should suit you perfectly.
Usually you would use something like Java Melody or a JMX console to monitor the application server process. You can also instrument and monitor it with the built-in VisualVM.
http://linux.die.net/man/1/dstat
if you are under linux, you can try dstat. We use it to take control over cpu, disk e ram usage
You could try to run systeminfo but you should really consider not doing it from Java but running some other tool (your could use nagios) on the OS - that way your monitoring isn't dependent on the app you're running.
Related
I have a cross-platform (Linux and Windows) program, currently running on Java 7. The program will launch one or more workers as Processes using ProcessBuilder (using instructions from here):
String javaHome = System.getProperty("java.home");
String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
String classpath = System.getProperty("java.class.path");
String className = MyClass.class.getCanonicalName();
ProcessBuilder pb = new ProcessBuilder(javaBin, "-cp", classpath, className);
pb.directory(null);
pb.inheritIO();
Process p = pb.start();
The reason for this approach was designed to solve an issue with the older system, which ran the workers in threads. However, due to the nature of the code we're running, it's possible for the workers to enter a loop from which they won't exit. Since Java does not support the idea of terminating a thread, we moved to a completely separate process under the assumption that Java could actually kill a process.
However, it appears that isn't true in Java 7 - Process.destroy() sends a SIGTERM, not a SIGKILL, and our stalled workers aren't being killed as we'd expect. Java 8 implemented Process.destroyForcibly(), which would solve the problem, but upgrading the core Java version is likely to introduce a number of bugs, and upgrading is something we'd like to avoid if at all possible.
Most other solutions involve reflecting into the UNIXProcess class, getting the pid, and piping a kill command to the shell. However, this won't work for us as we run on Windows, where Process does not have any grasp of a pid, and further forces us to include OS-specific code paths which is extremely undesirable.
Is there some reliable way to get Java to terminate something?
You can try this API SIGAR.
Sample code:
final Sigar sigar = new Sigar();
final long[] processes = sigar.getProcList();
for (final long processId : processes) {
System.out.println(processId + " = " + ProcUtil.getDescription(sigar, processId));
final String processDescription = ProcUtil.getDescription(sigar, processId);
if(processDescription.contains("notepad.exe")){
System.out.println("Found notepad.exe with id [" + processId + "] - KILLING IT!");
sigar.kill(processId, -9);
}
}
Code source:Link
The solution in my case, since I was writing both the parent and the child processes, was to add a ShutdownHook which halted the JVM (a simple System.exit(1) was not sufficient):
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
Runtime.getRuntime().halt(5);
}
});
After this, when a SIGTERM was received the process would terminate, as though it had been sent SIGKILL by the parent process.
We are running two separate JAVA programs in the below mentioned OS and JVM.
Operating System : HP-UX 11.11
JVM Used : 1.6
Program 1:
• This program monitors a folder for new files using Apache VFS.
• I am using multithreading in this program ,and it creates 5 threads in runtime to process the files in the folder which is being monitored. ( I use Executor service for this).
• This program runs on an infinite loop.
• I am using “ManagementFactory class“ to get the PID of this program and I write it to a txt file.
Program 2:
• In this program I will get the PID of the “Program 1” from text file and I want to find all the active threads of “Program1”
• Along with the active threads I would like to know the status whether these 5 threads of “Program1” are currently running/completed state.
Please let me know whether we can fetch the threads of another program based on the PID from JVM?
You can get the threads of a program in java by issuing platform specific commands and using JNA (Java Natice Access) to platform specific APIs, for instance under Linux you can issue:
ps uH p <PID_OF_U_PROCESS> | wc -l
when issuing the ps command you have several details on the process, for instance whether it's a zombie process or how much memory and processor it is using..
Under Windows you'll use similar command
You can do this by using JMX. Something like
for (final VirtualMachineDescriptor vmd : VirtualMachine.list()) {
int pid;
try {
if ((pid = Integer.parseInt(vmd.id())) == myPidOfInterest) {
String address = ConnectorAddressLink.importFrom(pid)));
MBeanServerConnection connection = JMXConnectorFactory.connect(address).getMBeanServerConnection();
ThreadMXBean threadMxBean = ManagementFactory.newPlatformMXBeanProxy(connection, "java.lang:type=Threading", ThreadMXBean.class);
// Now you have the ThreadMXBean you can find out all kinds of things about the threads
for (long threadId : threadMxBean.getAllThreadIds()) {
System.out.println(threadMxBean.getThreadInfo(threadId));
}
}
} catch (NumberFormatException e) {
// ignore
}
}
I am building a Java application that calls a system command and executes that command before returning control to the Java thread. The debugging step that I have is that I have made a perl script called test.pl and I call that script from the Java method and in Windows I get the expected output and a return value of 0. In Linux I get no output or error output and I get a return value of 136. I have spent extensive time online trying to figure out where I have gone wrong but as I have said, on Windows it runs. I think it must be a simple error that I am just not catching.
Here is the code derived from the excellent Runtime.exec() tutorial at http://www.javaworld.com/jw-12-2000/jw-1229-traps.html
try {
FileOutputStream fos = new FileOutputStream("output/" + this.startPosition + ".txt");
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(new String[]{"perl", "/data/stat-positive-selection/thaddeus/treesim/chr2YRI/test.pl"});
//System.out.println(commandLine);
// any error message?
StreamGobbler errorGobbler = new
StreamGobbler(proc.getErrorStream(), "ERROR");
// any output?
StreamGobbler outputGobbler = new
StreamGobbler(proc.getInputStream(), "OUTPUT", fos);
// kick them off
errorGobbler.start();
outputGobbler.start();
// any error???
int exitVal = proc.waitFor();
System.out.println("ExitValue: " + exitVal);
fos.flush();
fos.close();
} catch (Throwable e){
e.printStackTrace();
}
}
I have figured it out and fixed the code
new exec call, the use of the shell and the path to perl are required
Process proc = rt.exec(new String[]{"/bin/bash", "-c", "/usr/bin/perl /data/stat-positive-selection/thaddeus/treesim/chr2YRI/test.pl"});
The differences between the old and new versions are:
you are running the command from a shell, and
you are specifying the full pathname of the "perl" command.
In fact, in this context there doesn't seem to be a great deal of point in running the command from a shell. (Hypothetically, your Perl application might depend on environment variables that are set during shell initialization. However, the same environment variables would normally be inherited by and then from the Java command's environment. So unless the Java command is being launched in a strange way, this scenario is ... unlikely.)
So I think the substantative difference1 is that you are using the full pathname for "perl" ... and you don't a subshell to do that.
So, in the interests of science ( :-) ) I suggest you try this:
Process proc = rt.exec(new String[]{
"/usr/bin/perl",
"/data/stat-positive-selection/thaddeus/treesim/chr2YRI/test.pl"});
1 - ... the difference that makes a difference
I replaced the whole Runtime.exec() structure with the Apache Commons Exec library. It fixed the problem I was having.
http://commons.apache.org/proper/commons-exec/tutorial.html
I use the following to launch a Java application from another Java app.
ProcessBuilder pb = new ProcessBuilder(javaPath + javaCommand, maxMemStr,
minMemStr, stackSizeStr, jarCommand, jarfile, jarArg);
try {
Process p = pb.start();
} catch (IOException ex) {
Logger.getLogger(launch.class.getName()).log(Level.SEVERE, null, ex);
}
where javaCommand is either java or javaw (javaPath is empty most of the time unless a user points to an alternate path). The problem is, after the app launches, even when I verify the process list to contain java, it doesn't show the console.
Is it because PrcoessBuilder doesn't invoke the command shell? Is there a way to show the console programatically?
Thanks in advance.
This is because the "command console" itself is a process that attaches to the std-in/-out/-err streams of another process and displays them on the screen. When you launch Java all by itself, no other processes will be handling those streams, hence the lack of a command console. To get the results you want, you will need to launch a new instance of the command console and subsequently have it run your custom java command.
There may be a better way to do this... but I think the solution to this is going to be platform-dependent. In Windows, you could do something like:
ProcessBuilder pb = new ProcessBuilder("start", "\"JAwesomeSauce\"", "cmd.exe",
"/k", javaPath + javaCommand, maxMemStr, minMemStr, stackSizeStr, jarCommand,
jarfile, jarArg);
try {
Process p = pb.start();
} catch (IOException ex) {
Logger.getLogger(launch.class.getName()).log(Level.SEVERE, null, ex);
}
I assume you could do something similar in Linux/Mac if that's the O/S you're using.
You may want to run the command like this:
cmd /K java ...
or
cmd /C java ...
As far as I remember the Processbuilder opens a pipe to a specific process.
Your command window is a process itself with all you see. If you enter commands the cmd/bash usually creates new processes and attaches to them.
I've been trying to use Java's ProcessBuilder to launch an application in Linux that should run "long-term". The way this program runs is to launch a command (in this case, I am launching a media playback application), allow it to run, and check to ensure that it hasn't crashed. For instance, check to see if the PID is still active, and then relaunch the process, if it has died.
The problem I'm getting right now is that the PID remains alive in the system, but the GUI for the application hangs. I tried shifting the ProcessBuilder(cmd).start() into a separate thread, but that doesn't seem to be solving anything, as I hoped it would have.
Basically the result is that, to the user, the program APPEARS to have crashed, but killing the Java process that drives the ProcessBuilder.start() Process actually allows the created Process to resume its normal behavior. This means that something in the Java application is interfering with the spawned Process, but I have absolutely no idea what, at this point. (Hence why I tried separating it into another thread, which didn't seem to resolve anything)
If anyone has any input/thoughts, please let me know, as I can't for the life of me think of how to solve this problem.
Edit: I have no concern over the I/O stream created from the Process, and have thus taken no steps to deal with that--could this cause a hang in the Process itself?
If the process writes to stderr or stdout, and you're not reading it - it will just "hang" , blocking when writing to stdout/err. Either redirect stdout/err to /dev/null using a shell or merge stdout/err with redirectErrorStream(true) and spawn another thread that reads from stdout of the process
You want the trick?
Don't start your process from ProcessBuilder.start(). Don't try to mess with stream redirection/consumption from Java (especially if you give no s**t about it ; )
Use ProcessBuilder.start() to start a little shell script that gobbles all the input/output streams.
Something like that:
#!/bin/bash
nohup $1 >/dev/null 2>error.log &
That is: if you don't care about stdout and still want to log stderr (do you?) to a file (error.log here).
If you don't even care about stderr, just redirect it to stdout:
#!/bin/bash
nohup $1 >/dev/null 2>1 &
And you call that tiny script from Java, giving it as an argument the name of the process you want to run.
If a process running on Linux that is redirecting both stdout and stderr to /dev/null still produce anything then you've got a broken, non-compliant, Linux install ;)
In other word: the above Just Works [TM] and get rid of the problematic "you need to consume the streams in this and that order bla bla bla Java-specific non-sense".
The thread running the process may block if it does not handle the output. This can be done by spawning a new thread that reads the output of the process.
final ProcessBuilder builder = new ProcessBuilder("script")
.redirectErrorStream(true)
.directory(workDirectory);
final Process process = builder.start();
final StringWriter writer = new StringWriter();
new Thread(new Runnable() {
public void run() {
IOUtils.copy(process.getInputStream(), writer);
}
}).start();
final int exitValue = process.waitFor();
final String processOutput = writer.toString();
Just stumbled on this after I had a similar issue. Agreeing with nos, you need to handle the output. I had something like this:
ProcessBuilder myProc2 = new ProcessBuilder(command);
final Process process = myProc2.start();
and it was working great. The spawned process even did output some output but not much. When I started to output a lot more, it appeared my process wasn't even getting launched anymore. I updated to this:
ProcessBuilder myProc2 = new ProcessBuilder(command);
myProc2.redirectErrorStream(true);
final Process process = myProc2.start();
InputStream myIS = process.getInputStream();
String tempOut = convertStreamToStr(myIS);
and it started working again. (Refer to this link for convertStreamToStr() code)
Edit: I have no concern over the I/O stream created from the Process, and have thus taken no steps to deal with that--could this cause a hang in the Process itself?
If you don't read the output streams created by the process then it is possible that the application will block once the application's buffers are full. I've never seen this happen on Linux (although I'm not saying that it doesn't) but I have seen this exact problem on Windows. I think this is likely related.
JDK7 will have builtin support for subprocess I/O redirection:
http://download.oracle.com/javase/7/docs/api/java/lang/ProcessBuilder.html
In the meantime, if you really want to discard stdout/stderr, it seems best (on Linux) to invoke ProcessBuilder on a command that looks like:
["/bin/bash", "-c", "exec YOUR_COMMAND_HERE >/dev/null 2>&1"]
Another solution is to start the process with Redirect.PIPE and close the InputStream like this:
ProcessBuilder builder = new ProcessBuilder(cmd);
builder.redirectOutput(Redirect.PIPE);
builder.redirectErrorStream(true); // redirect the SysErr to SysOut
Process proc = builder.start();
proc.getInputStream().close(); // this will close the pipe and the output will "flow"
proc.waitFor(); //wait
I tested this in Windows and Linux, and works!
In case you need to capture stdout and stderr and monitor the process then using Apache Commons Exec helped me a lot.
I believe the problem is the buffering pipe from Linux itself.
Try to use stdbuf with your executable
new ProcessBuilder().command("/usr/bin/stdbuf","-o0","*executable*","*arguments*");**
The -o0 says not to buffer the output.
The same goes to -i0 and -e0 if you want to unbuffer the input and error pipe.
you need to read the output before waiting to finish the cycle. You will not be notified If the output doesn't fill the buffer. If it does, it will wait until you read the output.
Suppose you have some errors or responses regarding your command which you are not reading. This would cause the application to stop and waitFor to wait forever. A simple way around is to re-direct the errors to the regular output.
I was spent 2 days on this issue.
public static void exeCuteCommand(String command) {
try {
boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
ProcessBuilder builder = new ProcessBuilder();
if (isWindows) {
builder.command("cmd.exe", "/c", command);
} else {
builder.command("sh", "-c", command);
}
Process process = builder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null)
System.out.println("Cmd Response: " + line);
process.waitFor();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}