web-dev-qa-db-ja.com

Swingアプリケーションを終了するときに時折InterruptedExceptionが発生する

最近、コンピューターをより強力なコンピューターに更新しました。クアッドコアハイパースレッディングプロセッサ(i7)を使用しているため、実際の同時実行性が十分に利用できます。現在、開発中のアプリケーション(Swing GUIを使用)を終了すると(System.exit(0))、ときどき次のエラーが発生します。

_Exception while removing reference: Java.lang.InterruptedException
Java.lang.InterruptedException
        at Java.lang.Object.wait(Native Method)
        at Java.lang.ref.ReferenceQueue.remove(ReferenceQueue.Java:118)
        at Java.lang.ref.ReferenceQueue.remove(ReferenceQueue.Java:134)
        at Sun.Java2d.Disposer.run(Disposer.Java:125)
        at Java.lang.Thread.run(Thread.Java:619)
_

まあ、それはより並行性に対応したハードウェアで起こり始め、スレッドと関係があり、時々起こることを考えると、それは明らかにある種のタイミングの問題です。しかし、問題は、スタックトレースが非常に短いことです。私が持っているのは上記のリストだけです。私自身のコードはまったく含まれていないので、バグがどこにあるかを推測するのは少し難しいです。

誰かが以前にこのようなことを経験したことがありますか?それを解決し始める方法はありますか?

編集:System.exit(0)でSwingアプリケーションを終了するのは「汚れている」かもしれませんが、メインフレームを設定したくないので_EXIT_ON_CLOSE_に、アプリケーションの終了時に重大な問題が発生しないようにしたいので、dispose()を呼び出す前にメインフレームのSystem.exit(0)メソッドを実行するメカニズムを追加しました。 。したがって、今はかなりきれいになっているはずですが、それでも時折例外が発生します。これは、System.exit(0)が呼び出された後に発生します。 dispose()は問題なく動作します。つまり、シャットダウンフックから来ている必要があります。

_mainFrame.dispose(); // No problem! After this returns, all visible GUI is gone.
// In fact, if there were no other threads around, the VM could terminate here.
System.exit(0); // Throws an InterruptedException from Sun.Java2d.Disposer.run
_

Window.getWindows()配列(所有者のいないWindowsなどが含まれています)をループしてすべてのDialogを明示的に破棄しようとしましたが、違いはありませんでした。この問題は、「クリーンさ」(つまり、終了する前にネイティブ画面リソースを明示的に解放すること)とはほとんど関係がないようです。それは別のものですが、何ですか?

編集2:デフォルトのクローズ操作を_EXIT_ON_CLOSE_に設定しても違いはありません。 http://www.google.com/search?q=Sun.Java2d.Disposer.run(Disposer.Java:125) いくつかのバグレポートが見つかるので、これは確かにSunのバグである可能性があります。 Java2Dの実装。このようなバグは実際にはかなり無害であるため、長い間修正されない可能性があると想像できます。シャットダウンフックからの例外は、他の人を傷つけることはほとんどありません。これがGUIアプリで発生することを考えると、stderrがコンソールまたはログに送信されない限り、例外は認識されません。

27
Joonas Pulakka

ディスポーザは、remove()の呼び出し(次のプラットフォームのネイティブリソースの削除)でブロックされます。これは、VMが終了するときにディスポーザスレッド(デーモンスレッド)が自然にシャットダウンされていないことを意味します(System.exit()を介して終了しているため、これを予期する必要があります)。

アプリケーションにデーモン以外のスレッドがあり、すべてのスイングウィンドウが破棄されたときにVMが終了しないようにしています。

解決策:それを見つけて終了させます。

通常、swingアプリケーションは、すべてのswingウィンドウが破棄された場合に正常に終了します。たとえば、このプログラムはウィンドウをポップし、閉じられると終了します(すべて、System.exit()を呼び出さずに)。

public static void main(String args[]) throws Exception {
    JFrame jf = new JFrame();
    jf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
    jf.setVisible(true);
}

キックのためだけに、終了する前にガベージコレクターを実行してみることもできます。

15
Justin

スイングアプリケーションを使用している場合は、最初にSystem.gc()を呼び出し、次にdispose()メソッドを呼び出します。うまくいくと思います。私もこれを使っています。

これに賛成票を投じたいのですが、もっと担当者が必要です。理由の説明が見つからず、同僚もそれは意味がないと言っていますが、この解決策は私にとってはうまくいきました。

1.7と作成したswingアプリがあり、ファイルを読み込んで内容を並べ替えてからファイルに出力します。実行して終了するボタンがあります。設定API、ライター、リーダー、およびその他のいくつかのものを使用します。アプリを開いたり閉じたりすると(System.gc()なしで)、すぐに2回だけ連続して、上記と同じ例外が返されます。しかし、System.gc()の直前にdispose()があると、例外を再度スローすることができません。

4
Colin Hilbert

このバグとは関係ないのだろうか:

Javaバグ648954

基本的には、InheritableThreadLocalオブジェクトが存在する場合、またはカスタムcontextClassLoaderが存在する場合にJava2Dが使用されると、それらはキャプチャされて永久に存続し、メモリリークや、場合によってはこの種の奇妙なロックを引き起こします。

この場合の回避策は、メインスレッドでJava2Dアクションをトリガーして(つまり、アプリケーションが起動した直後に)、DisposerThreadが「きしむようなクリーンな」環境にあるようにすることです。これは静的初期化子によって開始されるため、Java2dリソースのダミーロードとそれを解放するだけで、望ましくないものに固執しないDisposerThreadを取得できます。

2
Peter Tillemans

同じ問題があります(Java 6)。デバッガーに不明なSun.awt.image.ImageFetcherスレッドがあります。アイコン付きのJButtonを使用しているためだと思います。ImageFetcherスレッドが消えます。 2秒後。ImageFetcherスレッドが実行されていない場合、プログラムは正常に終了します。

2
Ray Hulha

バグの原因を見つけたと思います!

[ファイル]メニューに[終了]サブメニューがある基本的なJavaアプリケーションがある場合...終了アクションをアクティブ化するためのショートカットがあります...このショートカットは、循環リンクなどを作成する必要があります.。

解決策があります:

まず第一に、これはすべてを明確にするためにオプションです。メインウィンドウからこのインターフェイス「WindowListener」を実装します。

このメインウィンドウの構築時にこれを行います:

JFrame frame=this.getFrame();
if(frame!=null)
{
   Window[] windows=frame.getWindows();
   for(Window window : windows)
   window.addWindowListener(this);
}

インターフェイスから関数を実装します:

public void windowOpened(WindowEvent e) {}

public void windowClosing(WindowEvent e) {
    JFrame frame=this.getFrame();
    if(frame!=null)
    {
        Window[] windows=frame.getOwnedWindows();
        for(Window window : windows)
        {
            window.removeWindowListener(this);
            window.dispose();
        }
    }
    //clear();
    System.gc();
}

public void windowClosed(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowActivated(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}

その後、ショートカットに注意する必要があります! actionPerformedで管理するには、終了メニューからアクションを削除する必要があります。

private void exitMenuItemActionPerformed(Java.awt.event.ActionEvent evt) {
    //this.clear();
    svExitMenuItem.removeActionListener(null);//this call removes the end error from this way to exit.
    javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(etscampide.ETScampIDEApp.class).getContext().getActionMap(ETScampIDEView.class, this);
    actionMap.get("quit").actionPerformed(null);//null to avoid end error too
}

理由は説明しませんが、私にとってはうまくいきます...一種の循環参照があると思います...お役に立てば幸いです。じゃあ。

2
TheTiger

System.exit()は、おそらくSwingベースのアプリを閉じる最もクリーンな方法ではありません

メインフレームをEXIT_ON_CLOSEに設定できません:

mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE )

次に、それを非表示に設定しますか?

ここに関連するQ; JavaのSystem.exit(0)

2
tim_yates

終了しても終了していないスレッドが実行されているようです。特に、スレッドはwait()を実行しており、実行中にスレッドを停止しようとすると、そのメソッドは割り込み例外をスローします。私は常にバックグラウンドスレッドをデーモンとして実行するように設定しますが、これも役立つ可能性があります。

私はあなたのJFrameで次のことをします:

myJFrame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
  public void windowClosing(WindowEvent we) {
    // Some pieces of code to instruct any running threads, possibly including
    // this one, to break out of their loops

    // Use this instead of System.exit(0) - as long as other threads are daemons,
    // and you dispose of all windows, the JVM should terminate.
    dispose();
  }
});

手動でループから抜け出すことで、waitメソッドを終了させ(うまくいけば、それほど長く待機していませんか?そうであれば、スレッドをどのように利用しているかを再検討することをお勧めします)、コード内で安全に中断できるポイントまで(コードを記述したので安全です)、スレッドが終了し、アプリケーションが正常に終了します。

更新おそらくどこかでイベントディスパッチスレッドを不適切に使用していて、終了しようとすると待機中/まだ機能していますか?ディスパッチャスレッドは、実行する作業をできるだけ少なくし、複雑なものをできるだけ早く別のスレッドに渡す必要があります。私は少し暗闇の中で刺していることを認めますが、特にあなたがより強力なマシンでそれに気づき始めたことを考えると、それはバグではないと思う傾向があります-私には「RaceCondition!」と叫びます。 Javaバグではありません。

1
dimo414

同じ問題が発生し、フレームが背景に隠れていることがわかりました(可視性をfalseに設定しました)。非表示のフレームで.dispose()を呼び出してから、mainFrameで.dispose()を呼び出すようにした場合、System.exit(0)を呼び出す必要はなく、アプリケーションはクリーンアップしてシャットダウンしました。私のコードはScalaですが、考え方は同じです。

def top = new MainFrame {

   import javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE
   peer.setDefaultCloseOperation(DO_NOTHING_ON_CLOSE)
   override def closeOperation() { endExecution; PreviewFrame.dispose(); this.dispose(); }
}
1
cessationoftime

時々エラーが再び発生しますが、これは正常に機能しているようです:

private void exitMenuItemActionPerformed(Java.awt.event.ActionEvent evt) {
    svExitMenuItem.removeActionListener(null);
    windowClosing(null);//i add it to clear and call the garbadge collector...
    javax.swing.ActionMap actionMap = org.jdesktop.application.Application.getInstance(etscampide.ETScampIDEApp.class).getContext().getActionMap(ETScampIDEView.class, this);
    actionMap.get("quit").actionPerformed(null);
}
1
TheTiger

私は同様のエラーを受け取りました、そしてどういうわけかこれは私のために働きました:

private static void createAndShowGUI() {
  JFrame jf = new MyProgram();
  jf.setVisible(true);
}

public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {

        public void run() {
          createAndShowGUI();
        }
    });
}
0
rtfminc

これはJava 1.7で解決されたバグのようです。

Eclipseをjdk1.7で実行するように構成すると、エラーはなくなりました。

ヨアヴ

0
Yoav

これを試して。これは私のために働いた。 System.exit(0)呼び出し後に適切に破棄されなかったJFileChooserインスタンスを作成していました。

setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
             public void windowClosing(WindowEvent ev) {                        
                dispose();
                System.exit(0);
                }
 });
0
Alex

データベースを使用してこのエラーが発生しました。問題は、データベースへの接続が閉じられていないことでした。私は次のコードを使用してこれを解決しました:

Runtime.getRuntime().addShutdownHook(new Thread() {
        public void run() {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    });

これを他の人のために捨てるだけです...

0
gedr

Javaのスレッドは、すべてのrun()メソッドの実行が終了したときにのみ終了します。それ以外の場合は、VMのスレッド拡張オブジェクトがいじり回されます。アプリケーションを終了する前に、すべてのスレッドを終了してください。

また、jFrameを作成するときに、スレッドを開始していることも考慮してください(CURRENT_THREAD私は信じています)

0
pampanet

Swing App Framework を使用している場合は、Application.exit()をオーバーライドしてクリーンアップを実行するか、ExitListenerを追加することもできます。それ以外の場合は、シャットダウンフックRuntime.getRuntime().addShutdownHook()をアプリに追加することもできます。

0
JRL

EventQueue.invokeLater() を使用してSwingを閉じてみてください。

0
masm