CH02 자바와 절차적/구조적 프로그래밍
Last updated
Last updated
무엇인가가 진행되고 그것이 순차적이라는 멘탈 모델은 프로그래밍이 아니어도 모든 일에 적용되듯이, 객체 지향이라고 분류가 되어도 그 속에는 기존의 패러다임이 가지고 있던 특성이 남아 있는 것이 어찌보면 당연하다고 생각한다.
이 챕터 제목의 의도는 이러한 맥락에서 자바 메소드가 절차적으로 진행이 되는 과정에서 어떠한 일들이 발생하는지 살펴본다는 것 같다. 더 정확히는 메모리 측면에서 각 코드마다 어떤 일들이 발생하는지를 살펴보는 챕터였다.
사실 내용에 더 가까운 제목으로는 '메소드 호출 과정에서 발생하는 메모리 적재 순서' 정도가 되는 것 같다. 복습시 이러한 관점에서 내용을 상기하자.
이건 백기선님 강의에서도 다뤘었는데 책에서도 다시 언급하고 있어서 다시 정리한다. 과거 정리노트를 그대로 가져온다.
뭔가 외우거나 굳이 자세히 기억하려하지 않아도 상식적으로 너무 당연한 포함관계이다. 내가 게임 개발자라고 한다면 개발 하는 과정에 당연히 게임도 실행 할 것이다. 실행이 가능하다는 의미(JDK 만 있어도 모든게 가능하다는 뜻)이다. 반면에 내가 일반 유저고 유니티 등이 깔려있지도 않은데 컴파일 등이 가능할까. 오로지 실행만 가능 할 것이다.
뭔가 여러번 봐도 누가 딱 물었을때 선뜻 자세히 대답이 안나오는 것 같다. 그만큼 제대로 숙지를 안해서 같다. 이번에 확실하게 머리에 다 담고자 한다.
처음 이걸 접했을 때는 'JVM 이 이렇게 생겼구나' 정도만 알면 되는 줄 알았는데 이직시 면접 질문이나 책에서 다루는 내용을 보면 결국 세부 컴포넌트까지 다 알아야 하는 것 같다. 안보고 그릴 수 있도록 하자.
Main.java
위 자바 파일이 있다고 하자. 이걸 컴파일러가 실행하고 종료할때 까지 JVM 의 무엇이 어떤 역할을 하는지의 관점에서 순서대로 정리한다.
Main.java 파일을 JDK의 컴파일러가 컴파일하여 Main.class 파일로 만든다. Main.class 는 바이트코드로 구성된 파일이다.(자바 -> 바이트 코드)
클래스 로더에 의해서 method area(=static area) 에 적재된다. method area 에는 import 된 모든 클래스 정보들이 적재된다.(= 클래스 로딩이 발생한다는 것) 이 때 적재되는 것들은 모두 '바이트 코드' 이다.
메모리에 적재된 '바이트 코드' 들을 JIT 컴파일러(정확히는 JIT 컴파일러 내부의 인터프리터)가 읽어 들인다. 이 때 메소드의 실행 빈도, 복잡성, 성능 등을 고려해서 JIT 컴파일러가 이를 기계어로 변환해 코드 캐시 영역에 저장할지(키값은 메소드 시그니처 및 바이트 코드 위치를 이용) 인터프리터가 읽은 바이트 코드를 그대로 CPU 가 실행할 수 있는 명령어로 바꿔서 실행시킬지 결정한다.
메소드: 클래스 수준의 정보(클래스 이름, 부모 클래스 이름, 메소드, 변수) 를 저장. 모든 스레드 공유.
힙: 객체를 저장. GC 동작 대상. 모든 스레드 공유.
스택: 스레드마다 런타임 스택을 만들고, 그 안에 메소드 호출을 스택 프레임이라고 부르는 블럭으로 쌓는다. 스레드 종료시 런타임 스택도 사라진다.
PC(Program Counter) 레지스터: 스레드 내 현재 실행할 스택 프레임을 가리키는 포인터가 저장된다. CPU 스케줄링 매커니즘의 맥락에서 다음번 리소스 할당때 '전에 여기까지 했다' 라는 것을 표시하는 용도이다.
네이티브 메모리: 네이티브 메서드 호출에 사용되는 스택. 네이티브 코드 실행 시 필요한 메모리를 할당하는 영역. 네이티브 라이브러리, 네이티브 스택, Code Cache 등이 여기에 포함.
스레드 마다 고유 스택 프레임을 갖게 된다. 스레드가 생김과 동시에 각 스레드가 메모리 내 스택 영역에 각각의 스택을 가지게 된다. 물론 멀티 스레드일때의 이야기이다.
이미 위에 정리를 했지만 책에서도 강조하고 있기도 해서 한 번 더 정리한다.
메소드(스태틱) : 클래스 멤버 변수가 존재한다.
스택 : 지역 변수(메소드 내 선언되는 변수들). 메소드 호출시 생성되고 메소드 종료시 소멸된다.
힙: 참조 변수. 프로그래머가 직접 생성한다.