우리네 장

[ JVM ] byte code 실행 시 사용되는 변수와 메소드는 어떻게 참조 되는가? 본문

JAVA/JVM

[ JVM ] byte code 실행 시 사용되는 변수와 메소드는 어떻게 참조 되는가?

qpmi1zm29 2022. 8. 4. 16:59

"Inside the Java Vitual Machine" 이라는 Contents를 참고하여 작성한 글입니다. 

 


class의 byte code가 실행되면서 코드적으로 참조하는 특정 클래스의 method에 대한 정보를 

어디서 가져오는 지에 대한 의문에서 시작 하였습니다.

 

단순히 생각하면 Method Area 어디쯤에 있다고 판단이 들었으나,

확실히 짚고 넘어가고자 하여 위 컨텐츠를 참조하여 정리하였고, 오역으로 인해 잘못된 정보가 있을 수 있습니다.

 


instance variable vs class variable

- class variable : static 변수를 의미한다. 이때의 static 변수는 non-final을 의미한다.

                           final static 변수는 변수보다는 상수에 가까우며 둘은 다르게 처리된다.

( class / interface 모두에 정의 가능 )

 

- instance variable : class에 정의된 static이 아닌 멤버 변수를 의미한다. 

( class에 정의 가능 )

 

instance method vs class method

- class method : static 메소드를 의미한다.

 

- instance method : static이 아닌 메소드를 의미한다.

 

※ 위 두가지가 구분되어 책에서 사용되었으므로 정리해 보았다.  

 

 

non-final static variable vs final static variable

static은 임의의 class의 인스턴스들이 공유하여 사용할 때 붙이는 keyword 이다.

심지어 인스턴스가 존재하지 않을 때도 class를 이용해서 접근할 수 있으므로 인스턴스 보다는 

클래스에 속해 있다고 보는 것이 논리적으로 적합하여 class variable, class method라고 부른다.

 

두 값 모두 method area에 저장 된다.

non-final static variable은 클래스가 로드 될 때 method area에 메모리를 할당 받고 1) default value

초기화 된다.

추후 initialize 단계에서 class loader에 의해 2 ) 사용자가 정의한 값으로 값을 할당 받는다.

★☆★☆★☆

load 단계에서 메모리 할당 및 초기화가 한 번에 되는 것이 아니다. 

엄밀히 말하면 메모리 할당과 초기화 단계는 구분되어 있고, 실행 주체가 class loader로 동일하다.

 

final static variable은 클래스에 속해있다기 보다는 constant pool에 속해있다고 보는 것이 더 적합하다.

 

non-final static variable은 information of type이 method area에 적재될 때

한 번 메모리에 할당 및 초기화가 되는데,

final static variable는 이 상수가 사용되는 클래스의 constant pool 마다 해당 상수가 정의되어 있다.

즉, 같은 상수가 method area의 여러 부분들에 저장되어 있다는 의미가 된다. 

 

But whereas non-final class variables are stored as part of the data for the type that declares them, final class variables are stored as part of the data for any type that uses them

 

각 영역 별 저장되는 값들에 대해서

Method area

  • non-final static variable
  • 전역 변수
  • information of type
  • byte code
  • constant pool 
  •         final static variable
  •          String
  •          method information

Heap area

  • instance 
  • instance variable
  • Class type intance 
  •          class load시 해당 type을 대표해주는 Class type의 instance를 생성하여 heap 영역에 저장한다.
  •          Class type instance는 java application에서  method area의 정보에 접근할 수 있도록 지원 해준다. 

Java Stack

method 내 선언된

  • local variable 
  • 상수
  • parameter 
  • 중간 계산 결과 값

 

constant pool

상수 풀은 사용자가 정의한 특정 언어 파일을 class로 컴파일 시, 파일 내부에 포함되는 영역이다.

symbolic table의 구조를 하고 있는 엔트리들의 집합이라고 정의가 되어 있는데,

#1, #2 등이 key이고 = 뒤에 String, Utf8은 해당 key에 대한 value의 추가적인 정보라고 생각하면 된다.

 

엔트리 들이 list로 나열되어 있고, 엔트리 들은 서로서로를 참조하고 있는 형식으로 구성되어 있다.

 

// 뒤에 나오는 부가적인 설명은 compiler가 이쁘게 표현해 준 부분이다.

 

constant pool이 가지고 있는 정보를 확인해 보면, 

사용자 정의 파일에서 사용자가 작성한 코드들 중 String, 호출되는 메소드 명, 호출되는 메소드를 가지고 있는 클래스 명,

그리고 해당 파일의 생성자 등의 정보로 구성되어 있다.

>> 바이트 코드 실행 시에 필요한 정보들로 구성 

( 코드에 생성자가 존재하지 않으면 컴파일러가 기본 생성자를 넣어준다. )

 

- literals ( String, Integer, floating point constants ) 

+ String도 primitive 타입의 객체는 아니지만 상수풀에서 관리 

- symbolic references to types, filelds. methods ( 다른 클래스나 인터페이스의 것을 호출하는 부분이 있을 때 )

+ static final class variable

 

13번의 invokevirtual ( + invokespecial )이 method를 실행하라는 부분

constant pool을 사용하는 이유이다.

사용자가 작성한 java 코드를 byte code로 compile 하였을 때 위와 같이 표현이 되는데

instructions 중 #n 으로 무언가 참조를 의미하는 instruction이 존재하고 그때 constant pool 이 참조 되어진다.

 

constant pool은 특정 type에 대한 information의 일부분으로, 

type이 method area에 메모리를 할당 받고 load될 때 같이 올라간다. 

 

★☆★☆★☆

실행 엔진에 의해 코드가 실행 되면서 constant pool을 참조하게 되면,

실행 속도를 높이기 위해 JVM이 " constant pool resolution" 을 수행한다.

☞ ☞  Full qualified name ( = class loader에게 key역할 ) 을 사용해 symbolic reference로 되어있던 상수 풀의 엔트리를 

     해당 type의 인스턴스를 heap area에 할당하고 그 주소의 pointer를 사용해 direct reference로 변경한다.

             :  constant pool resolution이 발생하게 되는 시점 ( , 사용되는 엔트리에 대하여 )

 

class loader


class loader의 종류는 아래와 같다.

  • Bootstrap classLoader
  • user-defined classLoader ( = system classLoader )

ClassLoader는 java의 java.lib 와 깊은 관련이 있다. 

classloader가 지원하는 메소드를 통해 java application 이 →  jvm 영역에 접근할 수 있다. 

 

예시로,

  1. defineClass () : classLoader가 method area에 특정 클래스에 대한 type data ( = binary data ) 를 적재할 수 있도록 도와준다.  return 값으로 해당 클래스 타입의 Class instance 를 반환 해준다.
  2. resolveClass ()  : defineClass () 의 return 값으로 받은 Class type 의 instance와 메소드 영역의 type data를 linking 해주는 역할을 한다.
  3. findSystemClass () : 매개변수로 특정 클래스의 풀 패키지 경로를 받는다. 매개변수로 넘어온 클래스에 대한 인스턴스를 반환해 주는데 1.0 버전에서는 부트스트랩 클래스 로더가 수행하다가 그 이후 버전에서는 시스템 클래스 로더가 수행한다. 

와 같은 메소드 들이 존재한다. 

 

클래스 로더가 하는 역할은 특정 클래스의 type information과 byte code를  메소드 영역에 적재하는 것 외에 다양한 역할을 한다. 

  • class file의 적합성 확인
  • static 변수의 메모리 할당 및 초기화
  • 임의의 클래스에 대한 type information과 byte code를 method area에 적재
  • 적재된 type에 대한 Class type의 인스턴스를 heap area에 생성
  • resolution symbolic linking > direct linking 

 

클래스 로더가 여러 종류로 나뉘는 이유는 

각 로더마다 스캔하는 패키지의 범위가 다르기 때문이다.

 

예전에는 부트 스트랩 로더가 CLASSPATH라고 선언된 경로의 모든 패키지를 순회하면서 클래스를 로드하였는데,

JVM의 버전이 올라가면서 해당 영역이 쪼개지며 클래스 로더가 분리 되었다. 

 

scan 범위

  • Bootstrap classLoader : 자바 API ( system class 들 ) 가 설치된 디렉토리에 한해서 경로를 탐색한다. 
  • user-defined classLoader : CLASSPATH라고 하는 경로의 디렉토리를 탐색해 클래스 파일을 찾는다. 

 

 

위 코드에서 메소드 내의 i와 10, " Hello from Hello.main !" 그리고 new Hello()로 만들어진 instance는 

   ☞ ☞ 메소드 내의 지역 변수 및 상수 를 의미.

   ☞ ☞ bipush

thread  별로 java stack에 저장되어 중간 값으로 코드들에 의해 참조되어 사용된 후 pop 되어 사라진다. 

 

byte code로 본다면 위 네 개의 instrunction 들이 변수 i을 0으로 초기화 한 값과 10을 stack에 넣은 뒤 비교하면서 for문을  돌도록 하는 부분이 되겠다. 

 

끗-.

 

 

 

'JAVA > JVM' 카테고리의 다른 글

[ Sevlet ] Servlet Container 와 JVM 관계 정리  (0) 2022.05.19
server.xml과 context.xml 차이 ?  (0) 2020.12.28