Bagaimana cara menghentikan proses java dengan anggun?

88

Bagaimana cara menghentikan proses Java dengan baik di Linux dan Windows?

Kapan Runtime.getRuntime().addShutdownHook dipanggil, dan kapan tidak?

Bagaimana dengan finalisator, apakah mereka membantu di sini?

Dapatkah saya mengirim semacam sinyal ke proses Java dari shell?

Saya mencari solusi yang lebih disukai portabel.

Ma99uS
sumber
Anda harus benar-benar mendefinisikan apa yang Anda maksud dengan anggun. (tolong)
Henry B
7
Saya berasumsi maksudnya mereka ingin diberi kesempatan untuk membersihkan sumber daya, melepaskan, mengunci, dan membuang data yang ada ke disk sebelum program dimatikan.
Steve g

Jawaban:

82

Hook shutdown dijalankan di semua kasus di mana VM tidak dibunuh secara paksa. Jadi, jika Anda mengeluarkan kill "standar" ( SIGTERMdari perintah kill) maka mereka akan mengeksekusi. Demikian pula, mereka akan mengeksekusi setelah memanggil System.exit(int).

Bagaimanapun hard kill ( kill -9atau kill -SIGKILL) maka mereka tidak akan mengeksekusi. Demikian pula (dan jelas) mereka tidak akan dijalankan jika Anda menarik daya dari komputer, menjatuhkannya ke dalam tong lava mendidih, atau memukul CPU menjadi beberapa bagian dengan palu godam. Anda mungkin sudah tahu itu.

Finalizer benar-benar harus berjalan juga, tetapi yang terbaik adalah tidak bergantung pada itu untuk pembersihan shutdown, melainkan mengandalkan hook shutdown Anda untuk menghentikan semuanya dengan bersih. Dan, seperti biasa, berhati-hatilah dengan kebuntuan (saya telah melihat terlalu banyak kait penghentian yang menggantung seluruh proses)!

jsight
sumber
1
Sayangnya, ini sepertinya tidak berfungsi pada Windows 7 (64bit). Saya telah mencoba menggunakan taskill, tanpa flag force, dan menemukan error berikut: "ERROR: Proses dengan PID 14324 tidak dapat dihentikan. Alasan: Proses ini hanya dapat dihentikan secara paksa (dengan opsi / F)." Memberikan opsi gaya, "/ f", jelas akan menutup proses secara instan.
Jason Huntley
Anda tidak dapat mengandalkan finalisator yang dijalankan.
Thorbjørn Ravn Andersen
47

Ok, setelah semua kemungkinan yang saya pilih untuk bekerja dengan "Pemantauan dan Manajemen Java"
Ikhtisar ada di sini
Yang memungkinkan Anda untuk mengontrol satu aplikasi dari yang lain dengan cara yang relatif mudah. Anda dapat memanggil aplikasi pengontrol dari skrip untuk menghentikan aplikasi yang dikontrol dengan baik sebelum mematikannya.

Berikut kode yang disederhanakan:

Aplikasi yang dikontrol:
jalankan dengan parameter VM berikut:
-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");
    }
}
}

Mengontrol aplikasi:
jalankan dengan berhenti atau mulai sebagai argumen baris perintah

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


Itu dia. :-)

Ma99uS
sumber
7

Cara lain: aplikasi Anda dapat membuka server socet dan menunggu informasi sampai padanya. Misal string dengan kata "ajaib" :) kemudian bereaksi membuat shutdown: System.exit (). Anda dapat mengirimkan informasi tersebut ke kaus kaki menggunakan aplikasi eksternal seperti telnet.


sumber
3

Berikut ini solusi yang agak rumit, tetapi portabel:

  • Dalam aplikasi Anda, implementasikan hook shutdown
  • Jika Anda ingin mematikan JVM dengan baik, instal Agen Java yang memanggil System.exit () menggunakan API Lampirkan .

Saya menerapkan Agen Java. Ini tersedia di Github: https://github.com/everit-org/javaagent-shutdown

Penjelasan rinci tentang solusi tersedia di sini: https://everitorg.wordpress.com/2016/06/15/shutting-down-a-jvm-process/

Balazs Zsoldos
sumber
2

Pertanyaan Serupa Disini

Finalisator di Java buruk. Mereka menambahkan banyak biaya tambahan untuk pengumpulan sampah. Hindari mereka jika memungkinkan.

ShutdownHook hanya akan dipanggil saat VM dimatikan. Saya pikir itu sangat baik mungkin melakukan apa yang Anda inginkan.

Steve g
sumber
1
Jadi shutdownHook dipanggil dalam SEMUA kasus? Bagaimana jika 'membunuh' proses dari shell?
Ma99uS
Yah, tidak jika Anda mencobanya -9 :)
Steve g
0

Pensinyalan di Linux dapat dilakukan dengan "kill" (man kill untuk sinyal yang tersedia), Anda memerlukan ID proses untuk melakukannya. (ps ax | grep java) atau semacamnya, atau simpan id proses saat proses dibuat (ini digunakan di sebagian besar file startup linux, lihat /etc/init.d)

Pensinyalan portabel dapat dilakukan dengan mengintegrasikan SocketServer dalam aplikasi java Anda. Tidak sesulit itu dan memberi Anda kebebasan untuk mengirim perintah apa pun yang Anda inginkan.

Jika yang Anda maksud akhirnya adalah klausul sebagai ganti finalizer; mereka tidak dieksekusi ketika System.exit () dipanggil. Finalizer seharusnya berfungsi, tetapi seharusnya tidak melakukan sesuatu yang lebih signifikan selain mencetak pernyataan debug. Mereka berbahaya.

extraneon
sumber
0

Terima kasih atas jawaban Anda. Penutup kait penutup seperti sesuatu yang akan bekerja dalam kasus saya. Tetapi saya juga menemukan sesuatu yang disebut kacang Pemantauan dan Manajemen:
http://java.sun.com/j2se/1.5.0/docs/guide/management/overview.html
Itu memberikan beberapa kemungkinan bagus, untuk pemantauan jarak jauh, dan manipulasi dari proses java. (Diperkenalkan di Java 5)

Ma99uS
sumber