2013-10-18 69 views
3

我想實現一個ApplicationChangeMonitor,它監視文件系統中當前執行的jar文件中的更改。當檢測到更改時,應用程序應該重新啓動。我正在使用WatchService來檢測更改。運行新鮮jar文件時從openjdk發生致命錯誤

的設置:

  • 發展(Windows)中與Samba共享工作區的Eclipse(Linux系統)
  • 由Eclipse的行家(M2E)對桑巴的份額產生的jar文件
  • jar文件從Linux系統上殼(使用OpenJDK的)

所以每次創建一個新的JAR文件,運行應用程序應該在Linux系統上重新啓動執行。首先,我試圖讓應用程序自行重啓,但大部分時間我都遇到了來自JVM的致命錯誤。於是我選擇了一個更簡單的方法:我只是做了檢測到變化後結束應用程序本身並實施使用bash的重啓機制:

while true ; do java -jar application.jar ; done 

奇怪的是,我還申請後得到致命錯誤一次或兩次更改。例如:

  • Java的罐子application.jar < - 初始啓動,應用程序運行
  • 新的JAR文件創建
  • Java的罐子application.jar < - 致命錯誤
  • 的java -jar application.jar < - 致命錯誤
  • java -jar application.jar < - 應用程序啓動
  • 新的JAR文件創建
  • Java的罐子application.jar < - 致命錯誤
  • Java的罐子application.jar < - 應用程序啓動

輸出:

# 
# A fatal error has been detected by the Java Runtime Environment: 
# 
# SIGBUS (0x7) at pc=0x00007f46d5e2416d, pid=28351, tid=139942266005248 
# 
# JRE version: 7.0_25-b30 
# Java VM: OpenJDK 64-Bit Server VM (23.7-b01 mixed mode linux-amd64 compressed oops) 
# Problematic frame: 
# C [libzip.so+0x516d] Java_java_util_zip_ZipFile_getZipMessage+0x114d 
# 
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again 
# 
# An error report file with more information is saved as: 
# /home/workspace/.../target/hs_err_pid28351.log 
# 
# If you would like to submit a bug report, please include 
# instructions on how to reproduce the bug and visit: 
# http://icedtea.classpath.org/bugzilla 
# The crash happened outside the Java Virtual Machine in native code. 
# See problematic frame for where to report the bug. 
# 

OpenJDK創建轉儲文件,我猜想相關部分是導致此致命錯誤的堆棧跟蹤:

- Stack: [0x00007fbc9398f000,0x00007fbc93a90000], sp=0x00007fbc93a8bd90, free space=1011k 
- Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code) 
- C [libzip.so+0x516d] Java_java_util_zip_ZipFile_getZipMessage+0x114d 
- C [libzip.so+0x5eb0] ZIP_GetEntry+0xd0 
- C [libzip.so+0x3af3] Java_java_util_zip_ZipFile_getEntry+0xb3 
- j java.util.zip.ZipFile.getEntry(J[BZ)J+0 
- j java.util.zip.ZipFile.getEntry(Ljava/lang/String;)Ljava/util/zip/ZipEntry;+38 
- j java.util.jar.JarFile.getEntry(Ljava/lang/String;)Ljava/util/zip/ZipEntry;+2 
- j java.util.jar.JarFile.getJarEntry(Ljava/lang/String;)Ljava/util/jar/JarEntry;+2 
- j sun.misc.URLClassPath$JarLoader.getResource(Ljava/lang/String;Z)Lsun/misc/Resource;+48 
- j sun.misc.URLClassPath.getResource(Ljava/lang/String;Z)Lsun/misc/Resource;+53 
- j java.net.URLClassLoader$1.run()Ljava/lang/Class;+26 
- j java.net.URLClassLoader$1.run()Ljava/lang/Object;+1 
- ... 

現在,有沒有人有任何想法,爲什麼我得到這些致命的錯誤?我想也許是因爲jar文件沒有完全寫入(這可以解釋爲什麼問題來自Java_java_util_zip_ZipFile_getZipMessage)。但事實並非如此,因爲執行後jar的md5sum保持不變,導致致命錯誤和正在執行。

while true; do md5sum application.jar ; java -jar application.jar ; done 
+0

如果罐子沒有完全寫我得到(''錯誤:無效或損壞的jar文件'')。如果是我有時會得到''無法實例化SLF4J LoggerFactory''(java.lang.NoClassDefFoundError:ch/qos/logback/core/joran/spi/JoranException''),有時會丟失不同的類,有時致命錯誤(''Java Runtime Environment檢測到致命錯誤:'')。等待幾秒鐘後,我可以運行應用程序沒有問題。 – steffen

+0

解鎖文件後,您是否嘗試退出Java,如我的答案中所建議的? – UDPLover

+0

並且還從bash循環中移除檢查校驗和並根據我的答案嘗試。 – UDPLover

回答

2

這是因爲當文件正在寫入磁盤時,您會收到新文件的通知。這對於WatchService來說是件壞事,一旦新文件被創建但是尚未完全寫入磁盤,它會立即通知您。

當新的jar文件寫入磁盤時,jar文件被進程鎖定,該進程將該jar文件寫入磁盤。在文件創建者進程未解鎖文件之前,您無法訪問文件。

解決方法:您必須嘗試打開文件,如果文件被打開,則文件已完全寫入磁盤。如果您無法打開文件,請等待一段時間(或等待,然後嘗試下一步),然後嘗試打開文件。

要解鎖文件,實施這樣的事情:

public void unlockFile(String jarFileName){ 
    FileInputStream fis = null; 
    while(true){ 
     try{ 
      // try to open file 
      fis = new FileInputStream(jarFileName); 
      // you succeed to open file 
      // return 
      // file will be closed in finally block, as it will always executed 
      return; 
     }catch(Exception e){ 
      // file is still locked 
      // you may sleep for sometime to let other process finish with file and 
      // file gets unlocked 

      // if you dont have problem with this process utilizing CPU, dont sleep! 
      try{ 
       Thread.sleep(100); 
      }catch(InterruptedException ie){ 
      } 
     }finally{ 
      if(fis != null){ 
       try{ 
        fis.close(); 
       }catch(Exception e){ 
       } 
      } 
     } 
    } 

您的問題,

你告訴:「我只是做了該應用程序在檢測到更改後結束本身並實施了用bash重啓機制「

所以,在你結束java進程之前,按照上面的方法建議解鎖文件。我相信錯誤會消失。試着讓我知道結果。

事情是這樣的:

void shutDownMethod(){ 
    // get file name from watcher, below line will depend on your logic and code. 
    String jarFileName = watcherThread.getNewNotifiedFile(); 
    // unlock new jar file. 
    unlockFile(jarFileName); 
    // shutdown JVM 
    System.exit(0); 
    // bash will restart JVM 
} 
+0

這就是我想的(見最後一段)。但是(1)我在最後一次改變之後的500ms延遲後緩衝改變並採取行動(所以「WatchService」不會給出任何更改事件),(2)爲什麼md5sum會打印出相同的總和它的工作與否)然後,(3)不會給java錯誤,如「無效或損壞的jarfile」而不是致命的退出? [我實現了(1),因爲這個過程實際上可以獲得鎖,但是Eclipse給出了錯誤消息。也許這些現象是由於桑巴舞的具體情況而發生的。] – steffen

+0

真的,我沒有得到你想要的,你在哪個過程中檢查校驗和? – UDPLover

+0

你正在獲得校驗和,因爲那不是java進程! – UDPLover