2010-02-16 15 views
14

Este código será un punto muerto:Java estática bloque del gancho de cierre con System.exit

public class Main { 
    static public final Object a = new Object(); 
    static { 
     Runtime.getRuntime().addShutdownHook(new Thread() { 
     @Override 
     public void run() { if (a == null); } 
     }); 
     System.exit(0); 
    } 
    static public void main(final String[] args) {} 
} 

Este código se salga con normalidad:

public class Main { 
    static public final Object a = new Object(); 
    static { 
     final Object aa = a; 
     Runtime.getRuntime().addShutdownHook(new Thread() { 
     @Override 
     public void run() { if (aa == null); } 
     }); 
     System.exit(0); 
    } 
    static public void main(final String[] args) {} 
} 

¿Qué está pasando?

Respuesta

1

Aquí está el código de bytes para el ejemplo de un punto muerto:

public class Main extends java.lang.Object{ 
public static final java.lang.Object a; 

public Main(); 
    Code: 
    0: aload_0 
    1: invokespecial #1; //Method java/lang/Object."<init>":()V 
    4: return 

public static void main(java.lang.String[]); 
    Code: 
    0: return 

static {}; 
    Code: 
    0: new  #2; //class java/lang/Object 
    3: dup 
    4: invokespecial #1; //Method java/lang/Object."<init>":()V 
    7: putstatic  #3; //Field a:Ljava/lang/Object; 
    10: invokestatic #4; //Method java/lang/Runtime.getRuntime:()Ljava/lang/Runtime; 
    13: new  #5; //class Main$1 
    16: dup 
    17: invokespecial #6; //Method Main$1."<init>":()V 
    20: invokevirtual #7; //Method java/lang/Runtime.addShutdownHook:(Ljava/lang/Thread;)V 
    23: iconst_0 
    24: invokestatic #8; //Method java/lang/System.exit:(I)V 
    27: return 

} 

Y aquí está el código de bytes para el caso de que termina normalmente:

public class Main extends java.lang.Object{ 
public static final java.lang.Object a; 

public Main(); 
    Code: 
    0: aload_0 
    1: invokespecial #1; //Method java/lang/Object."<init>":()V 
    4: return 

public static void main(java.lang.String[]); 
    Code: 
    0: return 

static {}; 
    Code: 
    0: new  #2; //class java/lang/Object 
    3: dup 
    4: invokespecial #1; //Method java/lang/Object."<init>":()V 
    7: putstatic  #3; //Field a:Ljava/lang/Object; 
    10: getstatic  #3; //Field a:Ljava/lang/Object; 
    13: astore_0 
    14: invokestatic #4; //Method java/lang/Runtime.getRuntime:()Ljava/lang/Runtime; 
    17: new  #5; //class Main$1 
    20: dup 
    21: aload_0 
    22: invokespecial #6; //Method Main$1."<init>":(Ljava/lang/Object;)V 
    25: invokevirtual #7; //Method java/lang/Runtime.addShutdownHook:(Ljava/lang/Thread;)V 
    28: iconst_0 
    29: invokestatic #8; //Method java/lang/System.exit:(I)V 
    32: return 

} 

El código de bytes es obviamente diferente. Voy a encontrar la respuesta o alguien más que entienda los aspectos internos de la JVM me ayudará.

12

Es importante que las clases no se accedan simultáneamente durante la inicialización, por lo que se mantiene un bloqueo.

Creo que lo que está sucediendo en el primer caso es:

  • El hilo principal mantiene el bloqueo de inicialización para Main.
  • Mientras se mantiene el bloqueo, System.exit bloques, ya que no devuelve.
  • Se ejecuta el gancho de cierre.
  • El cierre intenta acceder a la clase Main para leer un campo, pero bloquea a medida que la clase se inicializa.

De ahí el estancamiento. Es un poco más claro si escribe if (a == null); como if (Main.a == null);.

En el segundo caso, el valor se copia y, por lo tanto, el gancho de desconexión no necesita acceder a la clase Main.

Moral: No mezclar hilos e inicialización de clase. El libro de Java Puzzlers de Gafter y Bloch tiene más sobre esto.

+0

Dado que la variable estática es definitiva, parece absurdo que necesite acceder a la clase. Gracias por la referencia Main.a. –

+0

@Anderw Si fuera una constante en tiempo de compilación, entonces se copiará en el archivo de clase. Tal como está, la referencia al objeto particular necesita ser copiada en tiempo de ejecución. En general, con Java funciona de la manera más simple posible (¿esperarías que el comportamiento cambiara si no fuera final o si viniera después del bloque estático?). (El Modelo de memoria de Java (JMM) sí permite algunas suposiciones sobre los campos de instancia final. No estoy seguro acerca de los campos estáticos finales). –

0

Estoy teniendo un punto muerto similar con la primavera con mi gancho de apagado.

Supongo que podría ser un error en JVM? Puede verificarlo

"main" prio=6 tid=0x00316800 nid=0x52c in Object.wait() [0x0093f000] 
    java.lang.Thread.State: WAITING (on object monitor) 
     at java.lang.Object.wait(Native Method) 
     - waiting on <0x22a19da8> (a Main2$1) 
     at java.lang.Thread.join(Thread.java:1143) 
     - locked <0x22a19da8> (a Main2$1) 
     at java.lang.Thread.join(Thread.java:1196) 
     at java.lang.ApplicationShutdownHooks.runHooks(ApplicationShutdownHooks.java:79) 

El hilo del enlace de desconexión esperando un bloqueo del objeto que está bloqueado por sí mismo.

Gracias por su información que la eliminación de referencia de objeto externo puede ayudar a resolver el bloqueo.

1

Ejecuté el mismo programa con IBM JVM (arroja más información sobre 'kill -QUIT'). Los fotogramas en el volcado de subprocesos explican el comentario de Tom Hawkin:

Cambié el nombre de Main a StaticBlockShutdownHook. Como puede ver a continuación, un bloqueo de inicialización interno (monitor) ha sido retenido por el hilo principal durante la ejecución del bloque estático() y el hilo de Shutdown Hook está esperando ser notificado en este monitor.

3XMTHREADINFO  "main" J9VMThread:0x0000000110F5FE00, j9thread_t:0x000000011014E500, java/lang/Thread:0x0700000000002330, state:CW, prio=5 
3XMTHREADINFO1   (native thread ID:0x5A10083, native priority:0x5, native policy:UNKNOWN) 
3XMTHREADINFO3   Java callstack: 
4XESTACKTRACE    at java/lang/Object.wait(Native Method) 
4XESTACKTRACE    at java/lang/Object.wait(Object.java:196) 
4XESTACKTRACE    at java/lang/Thread.join(Thread.java:616) 
4XESTACKTRACE    at java/lang/ApplicationShutdownHooks.run(ApplicationShutdownHooks.java:91) 
4XESTACKTRACE    at java/lang/Shutdown.runHooks(Shutdown.java:101) 
4XESTACKTRACE    at java/lang/Shutdown.sequence(Shutdown.java:145) 
4XESTACKTRACE    at java/lang/Shutdown.exit(Shutdown.java:190) 
4XESTACKTRACE    at java/lang/Runtime.exit(Runtime.java:101) 
4XESTACKTRACE    at java/lang/System.exit(System.java:279) 
4XESTACKTRACE    at StaticBlockShutdownHook.<clinit>(StaticBlockShutdownHook.java:11) 
4XESTACKTRACE    at java/lang/J9VMInternals.initializeImpl(Native Method) 
4XESTACKTRACE    at java/lang/J9VMInternals.initialize(J9VMInternals.java:200(Compiled Code)) 


3XMTHREADINFO  "Thread-5" J9VMThread:0x0000000112C5AF00, j9thread_t:0x0000000112BE01C0, java/lang/Thread:0x07000000000D0380, state:CW, prio=5 
3XMTHREADINFO1   (native thread ID:0x23B00BD, native priority:0x5, native policy:UNKNOWN) 
3XMTHREADINFO3   Java callstack: 
4XESTACKTRACE    at java/lang/Object.wait(Native Method) 
4XESTACKTRACE    at java/lang/Object.wait(Object.java:167(Compiled Code)) 
4XESTACKTRACE    at java/lang/J9VMInternals.initialize(J9VMInternals.java:130(Compiled Code)) 
4XESTACKTRACE    at StaticBlockShutdownHook$1.run(StaticBlockShutdownHook.java:7) 

La sección "monitores" muestra que los bloqueos son de la clase Principal. El J9VMINternals.initializeImpl() parece ser un método nativo que toma el bloqueo del objeto Class.

1LKMONPOOLDUMP Monitor Pool Dump (flat & inflated object-monitors): 
2LKMONINUSE  sys_mon_t:0x0000000110FF38D0 infl_mon_t: 0x0000000110FF3910: 
3LKMONOBJECT  [email protected]/0x07000000000DB7E0: <unowned> 
3LKNOTIFYQ   Waiting to be notified: 
3LKWAITNOTIFY   "Thread-5" (0x0000000112C5AF00) 
2LKMONINUSE  sys_mon_t:0x0000000112687AA0 infl_mon_t: 0x0000000112687AE0: 
3LKMONOBJECT  [email protected]/0x07000000000D0398: <unowned> 
3LKNOTIFYQ   Waiting to be notified: 
3LKWAITNOTIFY   "main" (0x0000000110F5FE00)