Problem
If you need to kill the same process that you’re in (for testing purposes or whatever) this code will do it.
A definitive, quick, unmerciful dead of the current Java program/app.
Not a System.exit(0) or a graceful dead.
A headshot to the running JVM
public class KillMe {
private static final String CMD_WINDOWS = "taskkill /F /PID %d";
private static final String CMD_LINUX = "kill -9 %d";
public static void tryNow() {
Thread t = new Thread( () -> {
try {
now();
} catch (CantKillMeException e) {
//TODO whatever
e.printStackTrace();
}
});
t.setName("killmethread");
t.setDaemon(true);
t.start();
}
public static void now() throws CantKillMeException {
int pid = obtainPid();
String command = getCommand(pid);
executeCommand(command);
}
static int obtainPid() throws CantKillMeException {
try {
java.lang.management.RuntimeMXBean runtime = java.lang.management.ManagementFactory.getRuntimeMXBean();
java.lang.reflect.Field jvm = runtime.getClass().getDeclaredField("jvm");
jvm.setAccessible(true);
sun.management.VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime);
java.lang.reflect.Method pid_method = mgmt.getClass().getDeclaredMethod("getProcessId");
pid_method.setAccessible(true);
int pid = (Integer) pid_method.invoke(mgmt);
return pid;
} catch (IllegalAccessException | java.lang.reflect.InvocationTargetException | NoSuchMethodException | NoSuchFieldException e) {
throw new CantKillMeException("Cant obtain pid", e);
}
}
// https://stackoverflow.com/questions/9573696/kill-a-process-based-on-pid-in-java
static String getCommand(int pid) {
String command = isWindows() ? CMD_WINDOWS : CMD_LINUX;
return String.format(command,pid);
}
// https://stackoverflow.com/questions/14288185/detecting-windows-or-linux
static boolean isWindows() {
String osname = System.getProperty("os.name").toLowerCase();
return osname.contains("win");
}
static void executeCommand(String command) throws CantKillMeException {
try {
Process process = Runtime.getRuntime().exec(command);
process.waitFor();
} catch (java.io.IOException | InterruptedException e) {
throw new CantKillMeException("Cant execute command " + command, e);
}
}
public static class CantKillMeException extends Exception {
CantKillMeException(String message, Throwable cause) {
super(message, cause);
}
}
}
Will work on Linux. Windows should work (not tested). There’s room for improvement, for sure.
Solution
Interesting little plot twist in a Java program.
First up, tryNow()
is dead code, it’s not used, get rid of it, it makes the rest of the class harder to understand by putting red-herrings in the code.
If it is really used somewhere else in your code (it is public static
), then it should be moved out of this class in to a more usefully named location…. and it has other issues too, which I will ignore… except for …. never extend Thread
directly, use Runnable
….
Next up, you should ensure you are using Java 9.x or newer, and then use the more convenient ProcessHandle
API for getting your current processID
This reduces the code:
static int obtainPid() throws CantKillMeException {
try {
java.lang.management.RuntimeMXBean runtime = java.lang.management.ManagementFactory.getRuntimeMXBean();
java.lang.reflect.Field jvm = runtime.getClass().getDeclaredField("jvm");
jvm.setAccessible(true);
sun.management.VMManagement mgmt = (sun.management.VMManagement) jvm.get(runtime);
java.lang.reflect.Method pid_method = mgmt.getClass().getDeclaredMethod("getProcessId");
pid_method.setAccessible(true);
int pid = (Integer) pid_method.invoke(mgmt);
return pid;
} catch (IllegalAccessException | java.lang.reflect.InvocationTargetException | NoSuchMethodException |
NoSuchFieldException e) {
throw new CantKillMeException("Cant obtain pid", e);
}
}
to just:
static int obtainPid() throws CantKillMeException {
try {
return ProcessHandle.current().pid();
} catch (SecurityException e) {
throw new CantKillMeException("Cant obtain pid", e);
}
}
Although you don’t want to do a System.exit(0)
I would still plan it as a backup…
You have
static void executeCommand(String command) throws CantKillMeException {
try {
Process process = Runtime.getRuntime().exec(command);
process.waitFor();
} catch (java.io.IOException | InterruptedException e) {
throw new CantKillMeException("Cant execute command " + command, e);
}
}
which anticipates exceptions on something that should terminate the process….
I would instead log the exception (actually, catch any Throwable), and exit…. within a finally-block – I would do that in the now()
method:
public static void now() throws CantKillMeException {
try {
int pid = obtainPid();
String command = getCommand(pid);
executeCommand(command);
} catch (Throwable t) {
System.out.println(t);
} finally {
System.exit(1);
}
}
You can choose a different (improved) logging mechanism.
Adding to rolfl’s answer:
there should be a check for a signal handler: make sure there isn’t one or, if there’s one, that it exits the application.