2011-12-13 3 views
11

기본적으로 Sun의 JVM은 모두 클래스를 지연로드하고 느리게 초기화 (예 : <clinit> 메소드 호출)합니다. ClinitBomb 클래스를 고려하면 static{} 블록 중에 Exception을 던집니다.Sun의 JVM에서 지연 클래스로드/초기화를 비활성화하려면 어떻게합니까?

public class ClinitBomb { 
    static { 
     explode(); 
    } 
    private static void explode() { 
     throw new RuntimeException("boom!"); 
    }  
} 

지금, 폭탄 트리거하는 방법을 고려해야합니다

public class Main { 
    public static void main(String[] args) { 
     System.out.println("A"); 
     try { 
      Class.forName("ClinitBomb"); 
     } catch (Exception e) { 
      e.printStackTrace(System.out); 
     } 
     System.out.println("B"); 
     ClinitBomb o2 = new ClinitBomb(); 
     System.out.println("C"); 
    } 
} 

을 우린 forName의 설명서 이렇게 말한다 이후 폭발, 점 B 전에 발생 보장; 문제는이

가 나는 방법은로드 JVM을 말하고 싶어 A. 후에 발생하고,이 main()ClinitBomb에 대한 정적 참조를 포함하더라도, Sun의 JVM에서 (Main이로드 될 때.) A 지점 전에 발생 여부 Main을 초기화하자마자 ClinitBomb을 초기화하십시오. (따라서 폭탄은 보다 앞서 점 A를 폭발시킵니다.) 일반적으로 나는 "클래스 X를로드/초기화 할 때마다 참조하는 클래스 Y에 대해서도 그렇게합니다."라고 말하고 싶습니다.

할 방법이 있습니까?

+0

메인의 ClinitBomb를 참조하는 정적 블록? –

+0

@ Thorbjørn Ravn Andersen은 일반적인 문제를 해결하지 못할 것입니다. 하지만 사용자 정의 클래스 로더가로드 된 모든 클래스에 정적 블록을 삽입 할 수 있다고 가정합니다. – emory

+0

'Class.forName()'의 거대한 목록을 추가하지 못하는데 잘 모르겠습니다. 오토, 왜 그렇게 중요한거야? – Bill

답변

8

이렇게 할 방법이 없습니다. JLS는 §12.4.1 When Initialization Occurs (강조 광산)에서 말한다 : 클래스의

초기화는 클래스에 선언 된 정적 필드에 정적 초기화 및 초기화를 실행으로 구성되어 있습니다. [...]

클래스 또는 인터페이스 타입 T는 다음 중 어느 하나에 처음 나타나는 직전 초기화한다 :

  • T 클래스 및 T의 인스턴스가 생성된다.
  • T는 클래스이며 T로 선언 된 정적 메서드가 호출됩니다.
  • T로 선언 된 정적 필드가 할당됩니다.
  • T로 선언 된 정적 필드가 사용되며 필드는 상수 변수가 아닙니다 (4.12.4).
  • T는 최상위 클래스이며 T 내에 어휘 적으로 중첩 된 어설 션문 (§14.10)이 실행됩니다.

클래스 클래스 및 패키지 java.lang.reflect에있는 특정 반영 메소드 호출은 클래스 또는 인터페이스 초기화를 유발합니다. 다른 환경에서는 클래스 또는 인터페이스가 초기화되지 않습니다.

로드 된 즉시 클래스를 초기화하는 Java 구현은 JLS를 위반합니다.

JVM instrumentation API을 사용하면 참조 된 클래스를 명시 적으로 초기화 한 모든 클래스에 정적 블록을 추가 한 ClassFileTransformer을 작성할 수 있습니다 (아마도 Class.forName을 통해). 한 클래스가 초기화되면 곧바로 도달 할 수있는 모든 클래스가 초기화됩니다.그러면 결과가 달라질 수 있습니다. 그것은 꽤 많은 일입니다.

+1

감사합니다. (이 경우에는 디버깅에 매우 편리했을 것입니다.) 대부분의 클래스가 'static final Logger' 필드를 선언하는 코드베이스를 상속 받았는데, 이는 성능이 좋은 로깅 인프라를 사용하여 생성된다는 것을 제외하고는 훌륭합니다. 기본적으로 합리적으로 임의의 실행 시점에서 갑자기 원격 호출이 발생하면 실패로 인해 ExceptionInInitializerError가 발생합니다. 재미 있지는 않습니까? – jon

+0

음 .. 흥미롭게도 ClinitBomb의 클래스는 forName()이로드 될 때까지로드됩니다. (즉, ClinitBomb.class를 제거해도 NoClassDefFoundError를 던지기 전에 주 인쇄물 "A"를 여전히 사용할 수 있습니다.) JLS 12.1.2에 따르면 구현에 따라 다릅니다. Sun JVM에서 기본 동작을 변경하는 방법은 무엇입니까? – jon

+0

나는 하나를 알지 못한다. 'java -XX : + UnlockDiagnosticVMOptions -XX : + PrintFlagsFinal'을 실행하고 어떤 것이 스스로를 제안하는지 봅니다. JRE가있는 것을 볼 수 없었습니다. –

1
Class.forName("...", true /*initialize*/, getClassLoader()); 

당신은 중간에있었습니다.

+0

자신 만의 열망하는 ClassLoader를 사용할 수 있습니다. –

+0

아니요. 그가 작성한 코드는 이미'forName' 호출에서'ClinitBomb'을 초기화 할 것입니다; [forName의 단일 인수 버전] (http://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#forName%28java.lang.String%29) "은 다음과 같습니다. : Class.forName (className, true, currentLoader) "를 참조하십시오. 그의 욕망은 ClinitBomb을 그 이전보다 더 초기 화하도록하는 것입니다. –

+0

@Tom Anderson. 나는 교정했다. –

관련 문제