언어/Java

참조 타입과 메모리 사용 영역 (+Java 코드 실행 과정)

뭉지(moonz) 2021. 12. 29. 14:46
반응형
도서 『혼자 공부하는 자바』 를 참고하였습니다.

자바의 타입은 크게 기본 타입(primitive type)과 참조 타입(reference type)이 있다.

 

기본 타입과 참조 타입

기본 타입은 정수 타입(byte, char, short, int, long), 실수 타입(float, double), 논라 타입(boolean)을 저장하는 타입이다.

참조 타입은 객체 object의 번지를 참조하는 타입이며 배열, 열거, 클래스, 인터페이스를 말한다.

 

기본 타입은 실제 값을 변수 안에 바로 저장하지만

참조 타입은 변수 안에 메모리의 번지를 저장한다. 번지를 통해 객체를 참조한다는 뜻에서 참조 타입이다.

 

차이점참조 타입에는 null 을 저장할 수 있다는 것이다. 만약 힙 영역의 객체를 참조하고 있는 변수에 null을 넣음으로써

힙 영역의 객체가  어떠한 변수로부터 참조되지 않는다면 JVM은  Garbage Collector를 구동시켜서 메모리에서 자동 제거한다.

 

스택 영역과 힙 영역의 개념이 등장하는데 스택 영역에는 참조 타입이든 기본 타입이든 변수들이 저장된다. 

해당 스택 영역에 참조 타입 변수는 객체의 번지 값을 가지고 있고, 기본 타입 변수는 직접 값을 저장하고 있다.

 

메모리 영역

먼저 JVM이 사용하는 메모리 영역은 다음과 같다.

JVM은 운영체제에서 할당받은 메모리 영역 Runtime Data Area을 메소드 영역, 힙 영역, JVM 스택으로 세부 영역으로 구분해서 사용한다.

아래 그림에는 표시되지 않았지만, Runtime Data Areas(런타임데이터영역)은 이외에도 PC Register, JNI, Native Method Library를 갖고 있다.

더보기

 

  • PC Register : Thread가 시작될 때 생성되어 현재 수행 중인 JVM 명령의 주소를 갖고있다.
  • JNI (Java Native Interface) : 자바 애플리케이션에서 C, C++, 어셈블리어로 작성된 함수를 사용할 수 있는 방법을 제공하는 인터페이스로, Native 키워드를 사용해서 메서드를 호출한다. 대표적인 메서드 : Thread의 currentThread()
  • Native Method Library : C, C++로 작성된 라이브러리

출처: https://heepie.me/49

메소드 영역

JVM이 시작할 때 생성되고 모든 스레드가 공유하는 영역이다.

메소드 영역에는 클래스 별로 정적 필드(static field), 상수(constant), 메소드 코드, 생성자(constructor) 코드등을 분류해서 저장한다.

코드에서 사용되는 클래스(.class)들을 클래스 로더가 읽어서 클래스마다 위의 분류대로 저장한다.

구체적으로, 클래스 멤버 변수, 메소드 정보, Type 정보, Constant Pool(상수 풀), static, final 변수 등이 생성된다.

Constant Pool은 모든 Symbolic Reference를 포함하고 있다.

 

힙 영역

객체와 배열이 생성되는 영역이다. (배열도 객체로 취급된다.)

힙 영역에 생성된 객체와 배열은 JVM 스택 영역의 변수나 다른 객체의 필드에서 참조한다.

만일 힙 영역의 객체 혹은 배열을 참조하는 변수나 필드가 없다면 의미 없는 객체가 되기 때문에 JVM이 쓰레기로 취급하여 Garbage Collector를 실행시켜 자동으로 제거된다. (GC의 대상 영역)

 

JVM 스택 영역

JVM 스택 영역은 메서드를 호출할 때마다 Frame을 추가(push)하고 메서드가 종료되면 해당 Frame을 제거(pop)하는 동작으로 수행한다.

또 Frame 내부에서는 지역변수 스택이 있어서, 여기에 변수(기본 타입 변수 혹은 참조 타입 변수)가 추가(push)되거나 제거(pop)된다.

변수가 스택 영역에 생성되는 시점은 변수에 값이 저장(초기화)될 때 이다. 변수는 선언된 블록 안에서만 스택에 존재하다가 블록을 벗어나면 스택에서 제거된다. (if문, for문, 메서드 등등..)

 


Java 코드 실행 과정

마지막으로 면접 준비를 하면서 정리한 Java 코드 실행 과정을 적어두며 마무리 하겠다.

1. 개발자가 자바 소스코드(.java)를 작성한다.

2. 자바 컴파일러(Javac)가 자바 소스코드(.java)를 읽어 JVM용 바이트코드(.class)로 변환시킨다. => .class 파일 생성

3. 컴파일된 바이트 코드를 JVM의 클래스 로더(Class Loader)에게 전달한다.

4. 클래스 로더는 동적로딩(Dynamic Loading)을 통해 필요한 클래스들(class 파일들)을 Runtime Data Area에 로드한다.

5. 로딩된 class파일들은 실행 엔진(Execute Engine)을 통해 해석된다.

6. 해석된 바이트 코드는 Runtime Data Area에 배치되어 실질적인 수행이 이루어진다.

더보기

클래스 로더 세부동작

-     로드 : 클래스 파일(컴파일된 바이트코드)을 가져와서 JVM의 메모리에 로드합니다.

-     검증 : 자바 언어 명세(Java Language Specification) 및 JVM 명세에 명시된 대로 구성되어 있는지 검사합니다.

-     준비 : 클래스가 필요로 하는 메모리를 할당합니다. (필드, 메서드, 인터페이스 등등)

-     분석 : 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경합니다.

-     초기화 : 클래스 변수들을 적절한 값으로 초기화합니다. (static 필드)

더보기

실행 엔진은 JVM 메모리에 올라온 바이트 코드를 실행시키는 역할이다. 이때, 실행 엔진은 두가지 방식으로 바이트 코드로 변경할 수 있다.

  • 인터프리터 : 바이트 코드를 한 줄씩 읽어 해석하고 실행합니다. (하나하나의 실행은 빠르나, 전체적인 실행 속도가 느리다는 단점)
  • JIT 컴파일러(Just-In-Time Compiler) : 인터프리터 효율을 높이기 위한 컴파일러로, 인터프리터가 반복되는 코드를 발견하면 JIT 컴파일러가 반복되는 코드를 네이티브 코드로 바꿔줍니다. 그 다음부터 인터프리터는 네이티브 코드로 컴파일된 코드를 바로 사용하여 다시 인터프리팅하는 것이 아닌 컴파일된 바이너리 코드를 실행하는 것이기 때문에 전체적인 실행속도는 인터프리팅 방식보다 빠릅니다.

 

 

반응형