ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JVM, JRE, JDK, 메모리
    BackEnd/자바 2020. 12. 14. 21:34

     

    일반 프로그램은 운영체제에서 바로 실행되는 반면, 자바 프로그램은 JVM 을 거쳐 실행된다.

     

    JVM 이란 (Java Virtual Machine)

    자바 가상 머신으로 자바 바이트 코드를 인터프리터와 JIT 컴파일러를 이용해 OS 에 특화된 코드로 변환한다.

     

    바이트 코드를 실행하는 표준의 의미 JAVA SE

    바이트 코드를 실행하는 표준을 구현한 구현체도 JVM이라는 용어를 사용한다.

    Oracle, Azul, Amazon-corretto, AdoptOpenJDK 등 벤더에 따라 구현이 다르다.

     

    바이트 코드

    자바 컴파일을 하면 .class  파일이 나오고 .class 안에 있는 코드가 바이트 코드다.

    이 바이트코드는 실제 실행될 때 다시 한번 기계가 읽을 수 있는 형태(native code)로 interpreter 를 통해 해석이 되어야 한다. 이렇게 한번에 기계어로 변환하지 않고 두번의 과정을 밟는 이유는 Java 의 최대 장점 중 하나인 machine-independent 특성때문이다.  

    platform independent 라고도 하는데, 한번 작성하면 어떤 기계에서든 돌릴 수 있다는 특성을 말한다.

     

    인터프리터와 JIT 컴파일러가 이 바이트 코드가 머신이 읽을 수 있는 코드로 변환한다.

     

    인터프리터 

    바이트 코드를 한줄씩 실행 

     

    JIT 컴파일러

    인터프리터의 효율을 높이기 위해 , 인터프리터가 반복되는 코드를 발견하면 JIT 컴파일러로 반복되는 코드를 모두 네이티브 코드로 바꾼다. 그 다음부터는 네이티브 코드로 컴파일된 코드를 바로 사용한다. 


    JRE은 JVM 최소한의 배포단위로 자바 애플리케이션 실행할 수 있도록 구성되어 있다.

    JVM과 library가 함께 있다.

    JRE 만 있어도 실행은 할 수 있지만 컴파일러 javac 는 JVM 에 있다.

    jlink 같은걸 이용해 JRE 를 직접 만들 수도 있다.

     

    JDK는  JRE에 개발에 필요한 툴까지 함께 구성되어있다.

    JDK는 javap - c  컴파일 툴까지 제공 

    자바 11부터 JRE 를 제공하지 않는다.  

     

     

     

    JVM 역할


    JVM 내부 영역별 역할

     

    클래스 로더 (Class Loader) 

    JVM 내로 클래스를 로드하고 링크를 통해 배치하는 작업을 수행하는 모듈로 ,

    런타임시 동적으로 클래스를 로드한다.

    프로그램 실행 시에, 컴파일러에 의해 변환 된 Byte Code(.Class)를 읽어 메모리에 저장하는 역할을 합니다.

     

    실행 엔진 (Excution Engine) 

    Class Loader에 의해 JVM 메모리 공간에 저장 된 Byte Code를 하나의 명령어 단위로 읽어 들여 실행하도록 합니다.

     

    Garbage Collector (GC)

    사용하지 않는 객체들을 메모리에서 해제하는 역할을 합니다

     

    RunTime Data Area

    JVM이 프로그램을 수행하기 위 해 운영체제로부터 할당받은 메모리 공간입니다.

     

    • 작성된 소스코드(.java)는 컴파일러(javac.exe)를 통해 JVM이 읽을 수 있도록 Byte Code(.Class)파일로 변환됩니다. 이렇게 변환 된 Byte Code파일을 JVM 내부의 Class Loader가 읽어들여 Runtime Data Area(Memory Area)에 저장하게 됩니다. 저장된 코드들을 Excution Engine이 하나의 명령어 단위로 읽어들여 프로그램을 실행하게됩니다.  그리고 사용이 끝나 더 이상 사용하지 않는 코드들을 Gabage Collector가 모아서 메모리에서 해제합니다.

    JVM  내부 메모리 구조


    클래스 로더 내부

     

    로딩 : 클래스를 읽어오는 과정

     - 클래스 로더가 .class 를 읽고 그 내용에 따라 적절한 바이너리데이터를 만들고 메서드 영역 에 저장.

     - FQCN (Fully Qualified Class Name), 클래스, 인터페이스, 이늄,  메서드, 변수 로딩이 끝나면

       해당 클래스 타입의  Class 객체를 생성해 힙 영역 에 저장

     

    > 부트 스트랩 클래스 로더 : JAVA_HOME\lib 에 있는 코어 자바 API 제공 . 최상위 우선순위.

    > 플랫폼 클래스 로더(익스텐션 클래스 로더) : JAVA_HOME\lib\ext 폴더나 java.ext.dirs 시스템변수에 해당하는 위치에 있는 클래스

    > 애플리케이션 클래스 로더 : 애플리케이션 클래스 패스 (java.class.path 환경변수 값이나 애플리케이션 실행할 때 주는 classpath옵션)에서 클래스를 읽는다.

     

    public class App {
    
    	public static void main(String[] args) {
    		ClassLoader classLoader = App.class.getClassLoader();
    		System.out.println(classLoader);
    		System.out.println(classLoader.getParent());
    		System.out.println(classLoader.getParent().getParent());
    	}
    
    }
    
    jdk.internal.loader.ClassLoaders$AppClassLoader@6aaa5eb0
    jdk.internal.loader.ClassLoaders$PlatformClassLoader@2c9f9fb0
    null //네이티브 코드로 구현되어 있어 부트스트랩 클래스 로더는 읽을 수 없다.
    

     

    링크 : 레퍼런스를 연결하는 과정

    > 검증 : .class 파일이 유효한지 체크

    > preparation : 클래스변수(static)와 기본값에 필요한 메모리

    > resolve (optional) :  심볼릭 레퍼런스를 메서드 영역에 있는 실제 레퍼런스로 교체

     

    초기화 : static 값들 초기화 및 변수에 할당 

     


    RunTime Data Area 내부 영역별 역할

     

    JVM 구조 

     

    힙 영역( Heap Area) : 다른 모든 영역에서 접근 가능

    new 키워드, 인스턴스, 배열 등 생성된 객체에 대한 메모리를 동적으로 할당.

    애플리케이션 영역에 접근할 수 있는 메모리의 메인영역이다.

    Thread 에서 공유하는 영역이다.

    참조하는 변수나 필드가 없다면 GC의 대상의 된다.

    *객체를 할당할 때 이용할 수 있는 메모리가 충분하지 않으면 JVM은 카비지 컬렉션을 이용해 힙에서 메모리를 재사용하려고 한다. 그래도 충분한 메모리 영역을 확보할 수 없다면 OutOfMemory 에러가 발생한다.

     

     

    메서드 영역 (Method Area) -- 다른 모든 영역에서 접근 가능

    클래스 정보를 처음 메모리로 올릴 때 초기화 되는 대상을 저장하기 위한 메모리 공간

    클래스와 인터페이스에 대한 런타임 상수 풀, 멤버변수,클래스변수,메서드,클래스 수중의 정보 풀 패키지 정보 등 저장. 

     

    *상수 풀 : 클래스와 같은 heap 의 permanent area에 생성되어 java 프로세스 종료까지 그 생을 함께 한다.

    String 을 new 로 생성하지 않고 " " 리터럴을 사용하여 생성할 경우, 내부적으로 new String 을 생성한 뒤에 String.intern()이라는 메서드가 호출되어 고유의 인스턴스를 공유하도록 interned 된다. 이것은 생성한 String 을 constant pool 에 등록하는 작업을 한다. (만약 이전에 같은 char sequence 의 문자열이 이미 상수풀에 있다면 문자열을 heap 에서 해제하고 그 상수풀의 레퍼런스를 반환)

    즉. 같은 패키지 내의 같은 클래스 내에서는 정수 스트링들은 동일한 String 객체를 참조한다.

     

    System.out.println("String address test");
    
    String newTest1 = new String("a");
    String test1 = "a";
    
    System.out.println(newTest1==test1); //false
    
    String test2 = "a";
    
    System.out.println(test1==test2); //true
            
    newTest1 = test1;
    System.out.println(newTest1==test1); //true

     

     

    스택 영역 (Stack Area): 스레드 영역에서 공유 되는 자원 

    지역변수, 매개변수(메서드 내에서 유효 / 로컬변수) , 객체의 참조, 메서드를 저장한다.

    메서드 실행이 끝나면 소멸된다. 재귀 메서드를 호출하면 StackOverFlow 에서가 발생할 수 있다.

    각 Thread 마다 하나씩 존재한다.

    *스레드마다  runtime 스택이란걸 만들고 그 안에 stack frame 을 쌓는다. stack frame  = method call 

    쌓인 스택에서 현재 어느 위치를 실행하고 있는지 PC Register 가 기록한다.

     

    스택 : 객체의 참조

     

    PC Register : 스레드 영역에서 공유 되는 자원

    Thread가 시작될 때 생성되며 Thread 마다 존재한다.

    Thread 가 어떤 부분을 어떤 명령어로 실행해야 할 지에 대한 기록을 한다.

    PC Register는 현재 실행되고 있는 부분의 주소(Adress)를 가지고 있다.

    현재 실행되고 있는 명령이 종료되면 카운트 값을 증가시켜 다음 명령을 실행하게 한다.

     

    Native Method Area : 스레드 영역에서 공유 되는 자원 

    자바 외 언어(C, C++ 등)을 수행하기 위한 Stack 영역.

    프로그램 실행 도중 호출 된 메서드가 Native 방식을 사용하는 메소드 일 경우, 이 영역에 저장되어 처리된다.

     

     

     

     

    반응형
Designed by Tistory.