
이글은, 오라일리 사의 Optimizing Java 책을 참고하여, 정리한 내용이다.
//.java
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, World!");
}
}
//.class
CAFEBABE 00000034 00110003 00000002 00000001
00000000 07000005 07000006 07000007 07000008
00000009 0000000A 00000000 00000001 00000000
00000003 0000000D 00000002 0000000E 0000000F
00000010 00000011 00000012 00000000 00000001
00000003 00000013 00000003 00000014 00000015
00000016 00000017 00000018 00000019 0000001A
0000001B 00000000 00000001 00000000 00000000
00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000

속성(컴포넌트)별 어떤 역할인지에대해서는 다루지 않습니다.
package test;
public class mainTest {
public static void main(String[] args) {
for(int i=0; i<10; i++){
System.out.println("Hello World");
}
}
}
//명령어 실행
javac mainTest.java
javap -v mainTest.class
public test.mainTest();
descriptor: ()V
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: iconst_0
1: istore_1
2: iload_1
3: bipush 10
5: if_icmpge 22
8: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
11: ldc #13 // String Hello World
13: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
16: iinc 1, 1
19: goto 2
22: return
LineNumberTable:
line 5: 0
line 6: 8
line 5: 16
line 8: 22
StackMapTable: number_of_entries = 2
frame_type = 252 /* append */
offset_delta = 2
locals = [ int ]
frame_type = 250 /* chop */
offset_delta = 19
}
먼저 mainTest class에는 메서드가 main 하나지만, javac가 클래스 파일에 디폴트 생성자를 자동 추가하므로, 2개가 생성 됩니다.
위의 javap 역어셈블러를 통해 변환된 코드를 간단하게 해석해보자면 이런 순서로 해석할 수 있습니다.
aload_0 명령을 실행invokespecial 명령을 호출. 슈퍼 생성자들을 호출하고 객체를 생성하는 등 특정 작업을 담당하는 인스턴스 메서드를 실행.main() 메서드 실행. iconst_0으로 정수형 상수 0을 평가 스택에 푸쉬, istore_1로 상수값을 오프셋 1에 위치한 지역 변수(루프의 i)에 저장.iload_1)한 뒤, 상수 10을 푸쉬(bipush10)한 다음 if_icmpage로 둘을 비교 합니다. (정수값이 10보다 같거나 큰가?)getstatic #2)하고 상수 풀에서 "Hello World"라는 스트링(문자열)을 로드(ldc #3) 합니다.invokevirtual 명령어로 이 클래스에 속한 인스턴스 메서드를 실행.iinc를 만나, 정수값은 하나 증가, goto를 만나서 다시 2번 명령어로 되돌아갑니다.그렇다면, 이전에 알아본 클래스로더와 컴파일을 연결시키면 어떤 과정이 될까?
아마 이러한 과정이 될 것이다. (코드는 위와 동일)
.java 코드를 컴파일 진행. -> .class 파일 생성.java 명령어로, 애플리케이션 실행클래스 로더에 로딩 요청을 보냄.클래스 로더가 Main 클래스의 .class 파일을 찾아서 메모리에 로드함.클래스 로더가 .class 파일을 읽어 바이트코드를 해석하고 클래스 정의 및 초기화를 수행함.
역시, IDE의 딸깍 실행 버튼과 커맨드는 편하다. 이래서 IDE 쓰는 것 같다...
참고 문서
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.1