Java 프로세스를 정상적으로 중지하는 방법은 무엇입니까?


88

Linux 및 Windows에서 Java 프로세스를 정상적으로 중지하려면 어떻게합니까?

언제 Runtime.getRuntime().addShutdownHook전화를 받고 언제 전화를받지 않습니까?

파이널 라이저는 어떻습니까? 여기서 도움이됩니까?

쉘에서 Java 프로세스로 일종의 신호를 보낼 수 있습니까?

선호하는 휴대용 솔루션을 찾고 있습니다.


의미하는 바를 우아하게 정의해야합니다. (제발)
Henry B

7
나는 그들이 프로그램이 죽기 전에 자원을 정리하고, 해제하고, 잠그고, 영구 데이터를 디스크에 플러시 할 기회를 원한다는 것을 의미한다고 가정합니다.
Steve g

답변:


82

종료 후크는 VM이 ​​강제로 종료되지 않은 모든 경우에서 실행됩니다. 따라서 "표준"kill ( SIGTERMkill 명령에서)을 실행하면 실행됩니다. 마찬가지로을 호출 한 후에 실행됩니다 System.exit(int).

그러나 하드 킬 ( kill -9또는 kill -SIGKILL)은 실행되지 않습니다. 마찬가지로 (그리고 분명히) 컴퓨터에서 전원을 끌어 내거나 끓는 용암 통에 넣거나 큰 망치로 CPU를 조각 내면 실행되지 않습니다. 그래도 이미 알고있을 것입니다.

종료 자도 실제로 실행되어야하지만 종료 정리에 의존하는 것이 아니라 종료 후크에 의존하여 작업을 깔끔하게 중지하는 것이 가장 좋습니다. 그리고 항상 그렇듯이 교착 상태에주의하십시오 (너무 많은 종료 후크가 전체 프로세스를 중단시키는 것을 보았습니다)!


1
안타깝게도 Windows 7 (64 비트)에서는 작동하지 않는 것 같습니다. 강제 플래그없이 taskill을 사용하려고했는데 다음 오류가 발생했습니다. "오류 : PID 14324가있는 프로세스를 종료 할 수 없습니다. 이유 :이 프로세스는 강제로만 종료 할 수 있습니다 (/ F 옵션 사용)." 강제 옵션 "/ f"를 제공하면 분명히 프로세스가 즉시 종료됩니다.
Jason Huntley 2015 년

실행중인 종료자를 신뢰할 수 없습니다.
Thorbjørn Ravn Andersen

47

좋아, 내가 "자바 모니터링 및 관리"로 작업하기로 선택한 모든 가능성
이 여기에있다.
이것은 비교적 쉬운 방법으로 하나의 애플리케이션을 다른 애플리케이션에서 제어 할 수있게 해준다. 제어 애플리케이션을 스크립트에서 호출하여 제어 애플리케이션을 종료하기 전에 정상적으로 중지 할 수 있습니다.

다음은 간단한 코드입니다.

제어 된 응용 프로그램 :
다음 VM 매개 변수를 사용하여 실행합니다.
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port = 9999
-Dcom.sun.management.jmxremote.authenticate = false
-Dcom.sun.management. jmxremote.ssl = false

//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();
    }
}
}


그게 다야. :-)


7

다른 방법 : 애플리케이션이 서버 소스를 열고 정보가 도착할 때까지 기다릴 수 있습니다. 예를 들어 "마법의"단어가있는 문자열 :) 그리고 나서 시스템을 종료하기 위해 반응합니다 : System.exit (). 텔넷과 같은 외부 응용 프로그램을 사용하여 이러한 정보를 socke에 보낼 수 있습니다.


3

다음은 약간 까다 롭지 만 이식 가능한 솔루션입니다.

  • 애플리케이션에서 종료 후크 구현
  • JVM을 정상적으로 종료 하려면 Attach API를 사용하여 System.exit () 를 호출 하는 Java 에이전트 를 설치하십시오 .

Java Agent를 구현했습니다. Github에서 사용할 수 있습니다 : https://github.com/everit-org/javaagent-shutdown

솔루션에 대한 자세한 설명은 https://everitorg.wordpress.com/2016/06/15/shutting-down-a-jvm-process/에서 확인할 수 있습니다.


2

여기에 비슷한 질문

Java의 종료자는 잘못되었습니다. 가비지 수집에 많은 오버 헤드를 추가합니다. 가능한 한 피하십시오.

shutdownHook은 VM이 종료 될 때만 호출됩니다. 나는 그것이 당신이 원하는 것을 아주 잘 할 것이라고 생각합니다.


1
그래서 shutdownHook은 모든 경우에 호출됩니까? 쉘에서 프로세스를 '종료'하면 어떨까요?
Ma99uS

글쎄, 당신이 그것을 죽인다면 아니에요 -9 :)
Steve g

0

Linux에서 시그널링은 "kill"(사용 가능한 시그널에 대해 man kill)로 수행 할 수 있습니다.이를 수행하려면 프로세스 ID가 필요합니다. (ps ax | grep java) 또는 이와 유사한 것 또는 프로세스가 생성 될 때 프로세스 ID를 저장합니다 (대부분의 Linux 시작 파일에서 사용됨, /etc/init.d 참조).

이식 가능한 신호는 Java 애플리케이션에 SocketServer를 통합하여 수행 할 수 있습니다. 그렇게 어렵지 않으며 원하는 명령을 자유롭게 보낼 수 있습니다.

종료 자 대신 finally 절을 의미했다면; System.exit ()가 호출 될 때 실행되지 않습니다. 종료자는 작동해야하지만 실제로 더 중요한 작업을 수행해서는 안되지만 디버그 문을 인쇄해야합니다. 위험합니다.


0

답변 해 주셔서 감사합니다. Shutdown은 내 경우에 작동하는 것과 같은 이음새를 연결합니다. 그러나 나는 또한 Monitoring and Management Bean이라는 것에 부딪 혔습니다.
http://java.sun.com/j2se/1.5.0/docs/guide/management/overview.html
원격 모니터링과 조작을위한 좋은 가능성을 제공합니다. 자바 프로세스의. (Java 5에서 도입 됨)

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.