java - pildorasinformaticas - Ocasional InterruptedException al salir de una aplicación Swing
java swing (17)
Si está utilizando la aplicación swing, primero llame a System.gc () y luego llame al método dispose (). Creo que funcionará bien ... También uso esto.
Me gustaría aumentar este voto, pero necesito más representantes. Esta solución funcionó para mí, aunque no puedo encontrar la explicación de por qué y mi compañero de trabajo también dice que no tiene sentido.
Tengo 1.7 y una aplicación swing que he creado, se lee en un archivo, reorganiza el contenido y luego se envía a un archivo. tiene un botón para correr y salir. utiliza preferencias API, escritor, lector y algunas otras cosas. Al abrir y cerrar la aplicación (sin System.gc()
), inmediatamente solo dos veces sucesivas devolverá esta misma excepción como se indicó anteriormente. pero con System.gc()
justo antes de dispose()
no puedo obtener la Excepción para lanzar nuevamente.
Recientemente actualicé mi computadora a una más potente, con un procesador de subprocesamiento de cuatro núcleos (i7), por lo tanto, una gran cantidad de concurrencia real disponible. Ahora de vez en cuando me System.exit(0)
el siguiente error al salir ( System.exit(0)
) una aplicación (con una interfaz gráfica de usuario de Swing) que estoy desarrollando:
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)
Bueno, dado que comenzó a suceder con un hardware más apto para la concurrencia, y tiene que ver con subprocesos, y sucede ocasionalmente, obviamente es una especie de sincronización. Pero el problema es que el seguimiento de la pila es tan corto. Todo lo que tengo es el listado de arriba. No incluye mi propio código, por lo que es un poco difícil adivinar dónde está el error.
¿Alguien ha experimentado algo como esto antes? ¿Alguna idea de cómo empezar a resolverlo?
Edición: ya que salir de una aplicación Swing con System.exit(0)
puede ser "sucio", pero no quiero establecer el marco principal en EXIT_ON_CLOSE
porque quiero asegurarme de que no haya nada crítico en marcha cuando la aplicación se cierra, se agregó un mecanismo para que ejecute el método dispose()
del marco principal antes de llamar a System.exit(0)
. Por lo tanto, debería estar bastante limpio ahora, pero la excepción ocasional todavía ocurre. Sucede después de que se haya llamado a System.exit(0)
; dispose()
trabaja sin problemas. Es decir, debe provenir de un gancho de cierre:
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
Incluso traté de eliminar explícitamente todas las Window
s haciendo un bucle a través de la matriz Window.getWindows()
(contiene Window.getWindows()
y similares), pero no hizo ninguna diferencia. Este problema parece tener poco que ver con la "limpieza" (es decir, la liberación explícita de recursos de pantalla nativos antes de salir). Es otra cosa, pero ¿qué?
Edición 2: establecer la operación de cierre predeterminada en EXIT_ON_CLOSE
no hizo ninguna diferencia. http://www.google.com/search?q=sun.java2d.Disposer.run(Disposer.java:125) encuentra algunos informes de errores, por lo que quizás sea un error en la implementación de Java2D de Sun. Podría imaginar que errores como estos pueden quedar sin solución durante mucho tiempo, porque son bastante inofensivos en la práctica; una excepción de un gancho de cierre apenas hace daño a nadie. Dado que esto sucede en una aplicación GUI, la excepción ni siquiera se nota a menos que el stderr
se dirija a una consola o registro.
¡Creo que he encontrado de dónde viene el error!
Cuando tiene una aplicación java básica con un submenú Salir en un menú Archivo ... Hay un atajo para activar la Acción de salida ... Este atajo debe hacer un enlace circular o algo así ...
Ahí están las soluciones:
En primer lugar, esto es opcional para que quede claro: implementa esta interfaz "WindowListener" desde su ventana principal.
En la construcción de esta ventana principal haga esto:
JFrame frame=this.getFrame();
if(frame!=null)
{
Window[] windows=frame.getWindows();
for(Window window : windows)
window.addWindowListener(this);
}
implementa las funciones desde la interfaz:
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) {}
Después de eso tendrás que tener cuidado con tus atajos! Tendrá que eliminar la Acción del menú Salir para poder gestionarla con una acción Realizada:
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
}
No explico por qué, pero a mí me funciona ... creo que hay un tipo de referencias circulares ... Espero poder ayudarte. Nos vemos.
Acabo de encontrarme con este error usando bases de datos. El problema era que la conexión a la base de datos no estaba cerrada. Resolví esto usando el siguiente código:
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
});
Solo tirando esto para otras personas ...
Estaba recibiendo un error similar y de alguna manera esto funcionó para mí:
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();
}
});
}
Esto parece ser un error resuelto en Java 1.7.
Configuré mi Eclipse para ejecutarse en jdk 1.7 y el error desapareció.
Yoav
Hay errores de nuevo a veces, pero esto parece funcionar bien:
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);
}
Intente usar EventQueue.invokeLater() para cerrar su Swing.
Los subprocesos en java solo terminan cuando todos los métodos run () terminan la ejecución. De lo contrario, siempre tendrás esos objetos de subproceso extendidos en la máquina virtual. Tienes que terminar todos los hilos antes de salir de la aplicación.
También tenga en cuenta que cuando crea su jFrame está iniciando un subproceso (CURRENT_THREAD, creo)
Me pregunto si no está relacionado con este error:
Básicamente, significa que si se usa Java2D cuando existe un objeto InheritableThreadLocal o está presente un contextClassLoader personalizado, se capturan y viven para siempre, lo que provoca pérdidas de memoria y posiblemente este tipo de bloqueos extraños.
Si este es el caso, una solución alternativa sería desencadenar una acción Java2D en el subproceso principal (es decir, inmediatamente cuando se inicia la aplicación), por lo que el DisposerThread vive en un entorno "squeky clean". Se inicia con un inicializador estático, de modo que solo una carga ficticia de un recurso java2d y su liberación sería suficiente para obtener un DisposerThread que no se aferra a cosas indeseables.
Parece que tienes un hilo en ejecución que no ha terminado cuando saliste. Notablemente, el hilo está en espera (), y ese método lanza una excepción interrumpida si intenta detener el hilo mientras se está ejecutando. Siempre configuro los subprocesos de fondo para que se ejecuten como Daemons, lo que también podría ayudar.
Yo haría lo siguiente en tu 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();
}
});
Al salir manualmente del ciclo, permite que termine el método de espera (con suerte no espere demasiado, ¿verdad? Si es así, puede que desee volver a examinar cómo está utilizando sus hilos) y luego obtener hasta un punto en su código en el que es seguro romperlo, es seguro porque lo codificó para hacerlo, y luego el hilo terminará, lo que permitirá que la aplicación termine bien.
Actualización ¿ Quizás esté utilizando el subproceso de envío de eventos de forma inapropiada en algún lugar, y está esperando / sigue funcionando cuando intenta salir? El subproceso del despachador debe estar haciendo el menor trabajo posible y debe pasar cualquier cosa compleja a otro subproceso lo más rápido posible. Admito que estoy acuchillando un poco en la oscuridad, pero me inclino a pensar que no es un error, especialmente considerando que empezaste a notarlo en una máquina más poderosa, que para mí grita "¡Condición de la raza!" no Java bug.
Prueba esto. Esto funcionó para mí. Estaba creando una instancia de JFileChooser que no se eliminó correctamente después de la llamada a System.exit (0).
setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent ev) {
dispose();
System.exit(0);
}
});
Si está utilizando la aplicación swing, primero llame a System.gc () y luego llame al método dispose (). Creo que funcionará bien ... También uso esto.
Si estaba utilizando Swing App Framework , podría anular Application.exit()
para realizar la limpieza, o agregar un ExitListener
también. De lo contrario, también podría agregar un gancho de cierre, Runtime.getRuntime().addShutdownHook()
a su aplicación.
Su Disposer está bloqueado en una llamada a remove () (eliminando el siguiente recurso nativo de la plataforma). Esto significa que el subproceso del eliminador (un subproceso del demonio) no se cierra de forma natural cuando la VM se cierra (lo que debería esperar ya que lo está terminando a través de System.exit ()).
Tiene un subproceso que no es de demonio en su aplicación, lo que impide que la máquina virtual salga cuando se hayan eliminado todas sus ventanas abatibles.
Solución : encuéntralo y haz que salga.
Normalmente, una aplicación de swing se cierra con gracia si todas sus ventanas de swing se han eliminado, por ejemplo, este programa abrirá una ventana y luego se cerrará una vez que se cierre (todo sin llamada a System.exit ()):
public static void main(String args[]) throws Exception {
JFrame jf = new JFrame();
jf.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
jf.setVisible(true);
}
También puede intentar ejecutar el recolector de basura antes de salir, solo por diversión.
System.exit () probablemente no sea la forma más limpia de cerrar una aplicación basada en Swing
¿No puedes establecer tu marco principal en EXIT_ON_CLOSE?
mainFrame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE )
Entonces configúralo en invisible?
Relacionado Q aquí; System.exit (0) en java
Tengo el mismo problema (Java 6). Noté un subproceso de sun.awt.image.ImageFetcher no conocido en el depurador. Creo que viene de mí usando JButtons con iconos. El hilo de ImageFetcher desaparece después de 2 segundos. Si el subproceso de ImageFetcher no se está ejecutando, mi programa sale muy bien.
Tuve el mismo problema y descubrí que acababa de tener un Frame oculto en el fondo (tenía la visibilidad configurada en false). Si me aseguré de llamar a .dispose () en mi marco oculto y luego a .dispose () en mi mainFrame no tuve necesidad de llamar a System.exit (0), la aplicación simplemente se limpió y se apagó. Mi código es Scala, pero la idea es la misma.
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(); }
}