Как остановить процесс Java изящно?
Как изящно остановить процесс Java в Linux и Windows?
Когда делает Runtime.getRuntime().addShutdownHook
позвонить, а когда нет?
А как насчет финализаторов, они здесь помогают?
Могу ли я отправить какой-то сигнал процессу Java из оболочки?
Я ищу предпочтительно портативные решения.
7 ответов
Завершающие зацепки выполняются во всех случаях, когда виртуальная машина не уничтожается принудительно. Так что, если вы должны были выпустить "стандартное" уничтожение (SIGTERM
из команды kill) затем они будут выполнены. Точно так же они будут выполняться после вызова System.exit(int)
,
Однако тяжело убить (kill -9
или же kill -SIGKILL
) тогда они не будут выполнены. Точно так же (и очевидно) они не будут работать, если вы отключите питание компьютера, бросите его в чан с кипящей лавой или разбите процессор на куски кувалдой. Вы, наверное, уже знали это, хотя.
Финализаторы действительно должны работать, но лучше не полагаться на это при очистке выключения, а полагаться на свои крюки отключения, чтобы остановить все чисто. И, как всегда, будьте осторожны с взаимоблокировками (я видел, что слишком много хуков отключения зависают весь процесс)!
Хорошо, после всех возможностей, которые я выбрал для работы с "Мониторинг и управление Java"
Обзор здесь
Это позволяет вам относительно легко управлять одним приложением из другого. Вы можете вызвать управляющее приложение из скрипта, чтобы изящно остановить контролируемое приложение перед его уничтожением.
Вот упрощенный код:
Контролируемое приложение:
запустите его с параметрами VM:
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port = 9999
-Dcom.sun.management.jmxremote.authenticate = ложь
-Dcom.sun.management.jmxremote.ssl= ложь
//ThreadMonitorMBean.java
public interface ThreadMonitorMBean
{
String getName();
void start();
void stop();
boolean isRunning();
}
// ThreadMonitor.java
public class ThreadMonitor implements ThreadMonitorMBean
{
private Thread m_thrd = null;
public ThreadMonitor(Thread thrd)
{
m_thrd = thrd;
}
@Override
public String getName()
{
return "JMX Controlled App";
}
@Override
public void start()
{
// TODO: start application here
System.out.println("remote start called");
}
@Override
public void stop()
{
// TODO: stop application here
System.out.println("remote stop called");
m_thrd.interrupt();
}
public boolean isRunning()
{
return Thread.currentThread().isAlive();
}
public static void main(String[] args)
{
try
{
System.out.println("JMX started");
ThreadMonitorMBean monitor = new ThreadMonitor(Thread.currentThread());
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
ObjectName name = new ObjectName("com.example:type=ThreadMonitor");
server.registerMBean(monitor, name);
while(!Thread.interrupted())
{
// loop until interrupted
System.out.println(".");
try
{
Thread.sleep(1000);
}
catch(InterruptedException ex)
{
Thread.currentThread().interrupt();
}
}
}
catch(Exception e)
{
e.printStackTrace();
}
finally
{
// TODO: some final clean up could be here also
System.out.println("JMX stopped");
}
}
}
Контролирующее приложение:
запустите его с остановкой или запустите в качестве аргумента командной строки
public class ThreadMonitorConsole
{
public static void main(String[] args)
{
try
{
// connecting to JMX
System.out.println("Connect to JMX service.");
JMXServiceURL url = new JMXServiceURL("service:jmx:rmi:///jndi/rmi://:9999/jmxrmi");
JMXConnector jmxc = JMXConnectorFactory.connect(url, null);
MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
// Construct proxy for the the MBean object
ObjectName mbeanName = new ObjectName("com.example:type=ThreadMonitor");
ThreadMonitorMBean mbeanProxy = JMX.newMBeanProxy(mbsc, mbeanName, ThreadMonitorMBean.class, true);
System.out.println("Connected to: "+mbeanProxy.getName()+", the app is "+(mbeanProxy.isRunning() ? "" : "not ")+"running");
// parse command line arguments
if(args[0].equalsIgnoreCase("start"))
{
System.out.println("Invoke \"start\" method");
mbeanProxy.start();
}
else if(args[0].equalsIgnoreCase("stop"))
{
System.out.println("Invoke \"stop\" method");
mbeanProxy.stop();
}
// clean up and exit
jmxc.close();
System.out.println("Done.");
}
catch(Exception e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Вот и все.:-)
Другой способ: ваше приложение может открыть сервер socet и дождаться поступления информации на него. Например, строка с "волшебным" словом:), а затем реагирует на завершение работы: System.exit(). Вы можете отправить такую информацию на сокет с помощью внешнего приложения, такого как telnet.
Вот немного хитрое, но портативное решение:
- В вашем приложении реализовайте хук отключения
- Если вы хотите корректно завершить работу JVM, установите Java-агент, который вызывает System.exit() с помощью Attach API.
Я реализовал Java-агент. Он доступен на Github: https://github.com/everit-org/javaagent-shutdown
Подробное описание решения доступно здесь: https://everitorg.wordpress.com/2016/06/15/shutting-down-a-jvm-process/
Подобный вопрос здесь
Финализаторы на Java плохие. Они добавляют много накладных расходов на сборку мусора. Избегайте их, когда это возможно.
ShutdownHook будет вызываться только при выключении виртуальной машины. Я думаю, что это очень хорошо, может делать то, что вы хотите.
Спасибо за ваши ответы. Отключение крючков швов, как то, что будет работать в моем случае. Но я также натолкнулся на то, что называется бинами мониторинга и управления:
http://java.sun.com/j2se/1.5.0/docs/guide/management/overview.html
Это дает хорошие возможности для удаленного мониторинга и управления процессом Java. (Был представлен в Java 5)
Сигнализация в Linux может быть выполнена с помощью команды "kill" (man kill для доступных сигналов), для этого вам понадобится идентификатор процесса. (ps ax | grep java) или что-то в этом роде, или сохраните идентификатор процесса, когда процесс будет создан (это используется в большинстве файлов запуска linux, см. /etc/init.d)
Портативная сигнализация может быть сделана путем интеграции SocketServer в ваше Java-приложение. Это не так сложно и дает вам свободу отправлять любые команды, которые вы хотите.
Если вы имели в виду, наконец, предложения вместо финализаторов; они не получают расширения при вызове System.exit(). Финализаторы должны работать, но на самом деле не должны делать ничего более значительного, кроме как печатать оператор отладки. Они опасны.