ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JVM 구조
    프로그래밍/Java 2022. 4. 30. 22:28

     

    출처 - 더 자바, 코드를 조작하는 다양한 방법

    JVM 구조는 크게 클래스 로더 시스템, 메모리, 실행 엔진 으로 구성되어 있다. 

     

    클래스 로더 시스템 

    • 컴파일된 .class 파일(byte code)  를 읽고 메모리에 적절하게 배치하는게 클래스 로더가 하는 일이다. 
    • 클래스 로더가 하는일은 크게 세 가지로 나누어 볼 수 있다. 로딩, 링크, 초기화 
    • 로딩: 클래스 파일에서 byte code 를 읽어오는 과정
    • 링크: 레퍼런스를 연결하는 과정
    • 초기화: static 값들 초기화 및 변수에 할당 

     

    메모리 

    • 크게 다섯가지 영역으로 나뉘어져 있다. 모두 중요하긴 하지만 하나를 꼽으라면, 메소드 영역을 더 유심히 볼 필요가 있다. 
    • 메소드 영역에는 클래스 수준의 정보 (클래스 이름, 부모 클래스 이름, 메소드, 변수) 를 저장한다. 그리고 공유자원이다. 즉 다른 영역에서도 참조할 수 있는 영역이다. 
    • 힙 영역에는 객체(인스턴스) 를 저장한다. 그리고 공유 자원이다. 
    • 스택, PC, 네이티브 메소드 스택 영역들은 쓰레드에 국한된다. 어떤 쓰레드 인지에 따라 해당 쓰레드에서만 자원을 공유한다. 
      (힙이나 메서드처럼 모든 영역에 자원을 공유하진 않는다.)
    • 스택영역은 쓰레드마다 런타임 스택을 만들고, 그 안에 메소드 호출을 스택 프레임(=메소드 콜) 이라 부르는 블럭으로 쌓는다. 쓰레드를 종료하면 런타임 스택도 사라진다. 
    • 스택은 쓰레드마다 하나씩 만들어 진다. 이렇게 만들어진 스택에 메서드를 막 쌓았는데 현재 어느 위치를 실행하고있는 지를 가르키는 pc레지스터라는 게 생긴다. 이것도 해당 쓰레드에 국한된다. 
    • PC (Program Counter) 레지스터: 쓰레드 마다 쓰레드 내 현재 실행할 스택 프레임을 가리키는 포인터가 생성된다.  
    • 네이티브 메소드 스택도 쓰레드 마다 생기며, 네이티브 메소드를 호출할때 생기는 별도의 스택이다. 
    • 네이티브 메소드란 네이티브(native) 라는 키워드가 붙어있고, 그 구현을 자바가 아닌 c, c++ 로 한 것을 얘기한다.
      예를들어, Thread.cuurentThread( ) 라는 메서드가 있는데 해당 메서드는 C 로 만들어져 있고 네이티브 키워드가 붙어있다. 
    • 프로그램에서 네이티브 메소드를 사용하는 코드가 있다면 네이티브 메소드 스택이 생길 것이고 그 안에 네이티브 메소드 인터페이스를 호출하는 스택 프레임이 하나 쌓일것이다. 

     

    여기서 쌓은 배경지식은 자바 앱에서 프로파일링할때 주로 사용한다.

     

    실행엔진 

    • .class 파일의 byte code 를 인터프리터가 한 줄씩 실행하여 네이티브 코드로 변경.
    • 이때 똑같은 코드가 여러번 나오게 되면 매번 네이티브 코드로 변경하는게 비효율적이여서 이때 JIT 컴파일러에게 보낸다.
      (JIT 컴파일러 는 간단하게 말하자면 바이트 코드를 네이티브 코드로 컴파일시켜주는 역할을 한다.)
    • JIT (Just in time) 컴파일러 : 인터프리터 효율을 높이기 위해, 인터프리터가 반복되는 코드를 발견하면 JIT 컴파일러로 반복되는 코드를 모두 네이티브 코드로 바꿔둔다. 그 다음 부터는 인터프리터는 네이티브 코드로 컴파일된 코드를 바로 사용한다. 실행 속도가 향상된다. 
    • GC (Garbage collector) : 더 이상 참조되지 않는 객체를 모아서 정리한다.
      이해도 해야하지만 경우에 따라 옵션을 조정해야할 때도 있다. 프로 파일링을 할때도 우리가 사용하는 GC 가 어떤 GC 인지, 어플리케이션 실행전에 사용할 GC 를 선택하는 경우도 있다. 가장 크게 GC 를 둘로 나누면 쓰로우 풋 위주의 GC 가 있고,  stop the world 를 줄이는 GC 가 있다. 그 중에 적절한 것을 사용한다. "우리는 서버 운영중에 많은 객체를 생성하고, response time 이 굉장히 중요하다" 라 하면 stop the world  즉, 멈춤 현상을 최소화 할 수 있는 GC 를 사용하는 것이 좋다.이부분은 성능 관련한 부분이므로 나중에 더 찾아보자. 

     

     

    정리 

    클래스로더가 읽으면 메모리에 배치를 함 (힙, 메소드 ). 실행을 할때 스레드가 만들어지면 스택, PC, 네이티브 메소드 스택이 쓰레드에 맞게 만들어진다. 실행엔진이 byte code 를 한줄 씩 실행을 하면서 어떤 코드는 스택에 넣는것도 있고 빼내서 더하는 것도 있을것이고 이렇게 실행하면서 스택을 사용한다. 그런데 한줄씩 하는것이 비효율적이다 보니 JIT컴파일러도 쓰고, 메모리도 최적화 해주어야하니까 남는 레퍼런스 (인스턴스) 도 찾아서 정리해도 해준다. 메모리나 실행엔진이 네이티브 라이브러리를 사용한다하면 JNI (네이티브 메소드 인터페이스)를 통해 사용하도록 되어 있는 구조이다. 

     

     

     

    참고

    https://www.geeksforgeeks.org/jvm-works-jvm-architecture/   

    https://dzone.com/articles/jvm-architecture-explained  
    http://blog.jamesdbloom.com/JVMInternals.html

    백기선 - 더 자바, 코드를 조작하는 다양한 방법 (인프런)

    '프로그래밍 > Java' 카테고리의 다른 글

    다이나믹 프록시  (0) 2022.05.03
    클래스 로더 (class loader)  (0) 2022.05.01
    JDK, JRE, JVM 이해하기  (0) 2022.04.29
    Java 8 Funtional 스터디  (0) 2021.09.23
    Inner class 정리  (1) 2021.09.10
Designed by Tistory.