Monday

Java 생성자 분석 (바이트 코드) 본문

언어/Java 기타

Java 생성자 분석 (바이트 코드)

뉴비2 2021. 3. 23. 23:51

1. 자바 생성자 내부 동작 원리

다음과 같이 Animal 클래스가 있을 때 main 함수에서 new를 호출하면 어떻게 동작하는지 살펴보겠습니다.

// Animal.java

public class Animal {
    String name;

    public Animal() {
        this.name = "동물";
    }

    public Animal(String name) {
        this.name = name;
    }
}

Main 함수

// Main.java

public class Main {

    public static void main(String[] args) {
        Animal tiger = new Animal("Strong Tiger");
        System.out.println(tiger.name);
    }

}

Main.class 바이트 코드

// class version 52.0 (52)
// access flags 0x21
public class Main {

  // compiled from: Main.java

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 1 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this LMain; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

---------------------------- 여기서부터 5줄 정도만 보시면 됩니다 -------------------------------
  // access flags 0x9
  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 4 L0			//이 부분부터 살펴보시면
    NEW Animal				// new Animal을 호출하면 객체를 위한 Heap 공간을 할당한 후에 해당 변수를 Stack에 쌓아둡니다.
    DUP					// 그 후 해당 변수를 Stack에서 Duplicate, 복사 한 후
    LDC "Strong Tiger"			// 매개변수 "Strong Tiger"를 Stack 푸시하고
    INVOKESPECIAL Animal.<init> (Ljava/lang/String;)V	// Animal("Strong Tiger")를 호출합니다. 
    ASTORE 1											
   L1
    LINENUMBER 5 L1
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 1
    GETFIELD Animal.name : Ljava/lang/String;
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L2
    LINENUMBER 6 L2
    RETURN
   L3
    LOCALVARIABLE args [Ljava/lang/String; L0 L3 0
    LOCALVARIABLE tiger LAnimal; L1 L3 1
    MAXSTACK = 3
    MAXLOCALS = 2
}

위 코드는 Main.class 파일의 바이트 코드를 살펴본 결과입니다. Main 함수에서 new 호출 시 Heap 영역에 Animal 객체를 위한 공간을 할당하고 그 주소를 가르키는 변수(P)를 Stack에 저장합니다. 그 후, dup명령어(스택 맨 위 변수 복제)를 사용하여 주소를 가르키는 변수(Tiger)를 복사하여 동일한 주소를 가르키는 새로운 변수(P2)를 스택에 생성합니다. 마지막으로, 매개변수 "String Tiger"를 Contant Pool에 저장 후 해당 위치를 가르키는 변수(S)를 스택에 푸시합니다. 마지막으로, P2, S를 스택에서 제거하면서 생성자를 호출합니다.

 

 

2. 생성자에 숨어있는 super()

다음과 같이 Animal 을 상속받는 Cat Class가 추가되었다고 가정하겠습니다. 

public class Cat extends Animal {
    int age;

    public Cat(){
    //  super() 생략
        age = 0;
    }

    public Cat(int age){
    //  super() 생략
        this.age = age;
    }
}

 

Cat 클래스 생성자 내부에는 사실 super()가 생략되어있습니다. 


Cat.class 바이트 코드

// class version 52.0 (52)
// access flags 0x21
public class Cat extends Animal {

  // compiled from: Cat.java

  // access flags 0x0
  I age

---------------- 여기서부터 보시면 됩니다(주석 확인) ------------
  // access flags 0x1
  public <init>()V		// public Cat()
   L0
    LINENUMBER 4 L0
    ALOAD 0
    INVOKESPECIAL Animal.<init> ()V	//super(). 즉, Animal()이 생략되어있습니다.
   L1
    LINENUMBER 5 L1
    ALOAD 0
    ICONST_3
    PUTFIELD Cat.age : I
   L2
    LINENUMBER 6 L2
    RETURN
   L3
    LOCALVARIABLE this LCat; L0 L3 0
    MAXSTACK = 2
    MAXLOCALS = 1

  // access flags 0x1
  public <init>(I)V		//public Cat(int age)
   L0
    LINENUMBER 8 L0
    ALOAD 0
    INVOKESPECIAL Animal.<init> ()V //super(). 즉, Animal()이 생략되어있습니다.
   L1
    LINENUMBER 9 L1
    ALOAD 0
    ILOAD 1
    PUTFIELD Cat.age : I
   L2
    LINENUMBER 10 L2
    RETURN
   L3
    LOCALVARIABLE this LCat; L0 L3 0
    LOCALVARIABLE age I L0 L3 1
    MAXSTACK = 2
    MAXLOCALS = 2
}
Comments