Крюк выключения статического блока Java с System.exit - PullRequest
14 голосов
/ 16 февраля 2010

Этот код заблокирует:

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) {}
}

Этот код выйдет нормально:

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) {}
}

Что происходит?

Ответы [ 4 ]

13 голосов
/ 16 февраля 2010

Важно, чтобы к классам не обращались одновременно во время инициализации, поэтому блокировка удерживается.

Я думаю, что в первом случае происходит:

  • Основной поток содержит блокировку инициализации для Main.
  • Удерживая блокировку, System.exit блокирует, поскольку она не возвращается.
  • Отключение завершает работу.
  • Завершение работы пытается получить доступ к классу Main для чтения поля, но блокируется при инициализации класса.

Отсюда тупик. Немного понятнее, если вы напишите if (a == null); как if (Main.a == null);.

Во втором случае значение копируется, и поэтому перехватчику отключения не требуется доступ к классу Main.

Мораль: не смешивайте потоки и инициализацию класса. В книге Гафтера и Блоха «Java Puzzlers» об этом больше.

1 голос
/ 12 июня 2012

Я запустил ту же программу с IBM JVM (она выдает больше информации об 'kill -QUIT'). Кадры в дампе потока объясняют комментарий Тома Хоукина:

Я переименовал Main в StaticBlockShutdownHook. Как вы можете видеть ниже, внутренняя блокировка инициализации (монитор) удерживалась основным потоком во время выполнения статического блока (), и поток Завершение работы завершает работу, ожидая уведомления на этом мониторе.

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)

В разделе «Мониторы» показано, что блокировки относятся к классу Main. J9VMINternals.initializeImpl () кажется родным методом, который берет блокировку объекта Class.

1LKMONPOOLDUMP Monitor Pool Dump (flat & inflated object-monitors):
2LKMONINUSE      sys_mon_t:0x0000000110FF38D0 infl_mon_t: 0x0000000110FF3910:
3LKMONOBJECT       StaticBlockThread@0x07000000000DB7C8/0x07000000000DB7E0: <unowned>
3LKNOTIFYQ            Waiting to be notified:
3LKWAITNOTIFY            "Thread-5" (0x0000000112C5AF00)
2LKMONINUSE      sys_mon_t:0x0000000112687AA0 infl_mon_t: 0x0000000112687AE0:
3LKMONOBJECT       StaticBlockThread$1@0x07000000000D0380/0x07000000000D0398: <unowned>
3LKNOTIFYQ            Waiting to be notified:
3LKWAITNOTIFY            "main" (0x0000000110F5FE00)
1 голос
/ 16 февраля 2010

Вот байт-код для примера взаимоблокировки:

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

}

А вот байт-код для случая, который обычно заканчивается:

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

}

Байт-код явно отличается. Я либо придумаю ответ, либо кто-то другой, кто понимает внутреннее устройство JVM, поможет.

0 голосов
/ 17 января 2011

У меня похожий тупик с пружиной с моим крюком отключения.

Я полагаю, это может быть ошибка в JVM? Можете ли вы проверить это

"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)

Поток крюка отключения, ожидающий блокировки объекта, который сам блокируется.

Спасибо за информацию о том, что удаление ссылки на внешний объект может помочь снять блокировку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...