JVM(Java Virtual Machine)

Ga0·2023년 4월 28일
1

java

목록 보기
4/6
post-thumbnail

JVM(Java Virtual Machine)

  • 자바 가상 머신(JVM)은 자바 프로그래밍 실행환경을 만들어주는 소프트웨어이며, OS에 종속받지 않고 CPU가 JAVA를 인식/실행할 수 있게 만드는 가상 컴퓨터라 볼 수 있다.

  • 위의 그림을 보면 일반 프로그램의 경우 OS를 통해 Hardware에 접근하므로 OS에 종속적이다.

  • JAVA 프로그램의 경우 JVM을 통해 OS에 접근하기 떄문에 OS에 종속적이지 않다.

  • 하지만, JVM을 사용하는 경우 OS에 종속적이지 않다는 특징때문에 속도가 느리다. (종속적인 언어 C에 비해)

  • 또한, JVM을 사용하기 위해선 JDK(JVM : 가상 머신, JRE : 라이브러리 집합소)을 별도로 설치를 해야하는 번거로움이 있는데, 종속적인 언어를 사용할 경우는 운영체제별로 개발해야하는데 이정도 설치쯤이야 별거 아니다!

  • JVM은 이렇게 OS사이에서 중계자 역할을 하며, JAVA 코드의 재사용성을 높여준다.

JAVA 컴파일 과정

  • Java 소스코드(*.java)는 CPU가 인식을 하지 못하므로 기계어로 컴파일을 해줘야한다.

  • 하지만, Java는 컴파일 후 바로 OS에 가는 것이 아니므로, OS와 Compiler 사이에 있는 JVM이 이해하느 언어(*.class)로 변환해야한다.

  • Java compiler(JDK를 설치하면 bin에 존재하는 javac,exe ; JDK에 java compiler가 포함되어있음!!!)은 .java 파일을 .class 라는 java bytecode를 변환해주는 역할을 한다.

  • .class로 변환된 코드는 기계어가 아니기 떄문에 OS가 이해하는 기계어로 변환해주어야 하는데, 이때 JVM이 OS가 이해할 수 있도록 java bytecode를 해석해준다.
    -> 이러한 특징으로 JVM은 OS에 종속적이지 않으며 Java 파일 하나만 만들면 어느 디바이스든 JVM위에서 실행할 수 있다는 것!

Class Loader(클래스 로더)

  • Class Loader가 클래스 로딩을 통해 Class 파일들을 JVM에 적재해 클래스 파일을 읽고 메모리를 할당해준다.
  • JVM 내로 클래스파일(.class)를 로드하고, 링크를 통해 배치하는 작업을 수행하는 모듈이다.
  • Java는 동적으로 클래스를 읽어오기 때문에 프로그램이 실행중인 런타임에 JVM과 연결된다.
  • 한 번에 메모리에 모든 클래스를 로드하는 것이 아닌, 필요한 순간에 해당 클래스(.class) 파일을 찾아 메모리에 로딩하는 역할을 수행한다.

Execution Engine(실행 엔진/Execution)

  • 로드된 클래스의 바이트코드를 실행하는 런타임 모듈이다.
  • Class Loader을 통해 JVM내의 Runtime Data Areas에 배치된 바이트 코드는 실행 엔진에 의해 실행되며, 실행 엔진은 자바 바이트 코드를 명령어 단위로 읽어서 실행한다.(Interpreter 방식과 JIT compiler 방식을 사용하게 된다.)

Interpreter(인터프리터)

  • 프로그래밍 언어의 소스 코드를 바로 실행하는 프로그램
  • 원시 코드를 기계어로 번역하는 컴파일러와 대비된다.
  • JAVA는 인터프리터 방식을 사용하여 JAVA Bytecode를 명령어 단위로 읽어 실행한다.
    -> 단, 한줄씩 수행하기 때문에 수행 속도가 느리다는 단점이 있다

JIT Compiler (Just In Time Compiler)

  • 인터프리터 방식의 단점을 보완하기 위해 JIT 컴파일러가 도입되었다.
  • JIT 컴파일러는 바이트코드를 컴파일하여 native code(컴퓨터 기계어로 동작하하며 OS에 의해 직접적으로 컴파일 되는 코드)로 변환하여 사용한다.
  • 한 번 컴파일된 코드는 빠르게 수행하게 되어 수행 속도 Interpreter보다 빠르다.(바뀐 부분만 컴파일 하고 나머지는 전에 컴파일 하면서 해당 코드를 캐싱된 코드를 사용)
    -> 단, 컴파일하는 과정에서 비용이 들기 때문에 한 번 수행하고 말 코드는 Interpreter을 사용하는게 좋다.
  • JVM은 인터프리터 방식을 사용하다가 일정 기준이 넘어가면 JIT 컴파일러를 사용하는 혼합 방식을 사용한다.

Garbage Collector(가비지 컬렉터)

  • 유효하지 않은 메모리(사용하지 않는 메모리)인 가비지(Garbage)를 정리해주는 역할을 한다.
  • 힙 영역에서 참조되지 않은 데이터를 삭제하는 역할을 해서, 개발자가 직접 참조하지 않는 데이터를 삭제하지 않아도 된다. (개발자의 불편을 많이 덜어줌)
  • 자세한 설명은 Garbage Collection POST에 설명해 두었다.

Runtime Data Area

PC Register

  • CPU내의 기억장치인 레지스터와 다르게 작동하며, 각 쓰레드별로 하나씩 존재한다.
  • 현재 수행 중인 JVM Instruction(=bytecode)의 주소를 가지게 된다.
  • 즉, 스레드가 어떠한 명령을 실행할지 기록하는 부분이다.

JVM Stack

  • 메소드(Method)가 호출될 때 메소드와 메소드의 정보는 JVM Stack에 쌓인다.
  • 메소드의 매개변수(Parameter), 지역변수(local variable), return 주소, 임시 변수 등의 정보를 기록하는 stack이다.
  • 각 스레드 별로 생성되기 때문에 다른 스레드는 접근할 수 없으며, 메소드 호출이 종료되면 스택에서 정보들이 제거 된다.
  • 예시
/*예를 들어 난수로 만들어 암호화한 비밀번호를 반환하는 코드이다.*/
 @Transactional
    // 비밀번호 난수로 제공
    public String findPw(MemberDto memberDto){ 
    //result <= stack 영역에 들어간다(지역변수)
        boolean result = memberEntityRepository.existsByMemailAndMphone(memberDto.getMemail(), memberDto.getMphone());
        
        //charStr <= stack 영역에 들어간다(지역변수)
        String charStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^"; 
        
		//newPwd <= stack 영역에 들어간다(지역변수)
        String newPwd = ""; 

        if(result){
            Optional<MemberEntity> memberEntityOptional = memberEntityRepository.findByMemail(memberDto.getMemail());
          

            if(memberEntityOptional.isPresent()){
                MemberEntity entity = memberEntityOptional.get();

                for(int i = 0; i < 8; i++){
                    Random random = new Random();
                    //ranStr 문자열에서 0인덱스 ~ 마지막 인덱스의 난수 인덱스 만들기
                    int index = random.nextInt(charStr.length()); //0번 인덱스부터 마지막 인덱스[인덱스를 난수로 가져옴]
                    newPwd += charStr.charAt(index);

                }
              entity.setMpassword(passwordEncoder.encode(newPwd));

                return newPwd; //return 주소(호출한 주소/곳) stack 영역이다.
            }
        }
        return newPwd; //return 주소(호출한 주소/곳) stack 영역이다.
    }
  • 위의 코드가 어떤 코드인지는 이해하지 않아도 된다. (stack 부분에 들어가는 것이 무엇인지만 기억하면됨!)

Native Method Stack

  • java외의 언어로 작성된 Native 코드를 위한 stack이다.
  • java Native interface를 통해 호출되는 C/C++등의 코드를 수행한다.

Method Area

  • 모든 스레드가 공유하는 메모리 영역으로 클래스, 인터페이스, 메소드, 필드, Static 변수 등의 바이트 코드를 보관한다.
  • Runtime Constant Pool(상수풀 : 문자상수, 타입, 필드, 객체참조가 저장됨)이라는 별도의 관리 영역이 존재한다.
  • static으로 선언된 변수는 항상 이미 그 정보가 JVM 내에서 다른 변수들보다 우선 저장된다.
public class AppStart {
	//main <= static 메소드로 Method Area에 속한다.
    public static void main(String[] args) {
        SpringApplication.run(AppStart.class);
    }
}

Heap Area

  • new 키워드로 생성된 객체와 배열이 생성되는 여역이다.
  • 주기적으로 GC가 제거하는 영역이다.
  • 런타임시 할당된다.
  • 효율적인 GC를 위해 메모리 영역이 분리되어 있다.
  • Method Area에 로드된 클래스만 생성이 가능하다.
    Heap Area는 new 키워드로 생성된 객체와 배열이 생성되는 영역이다.
  1. Young Generation 영역은 자바 객체가 생성되자마자 저장되고, 생긴지 얼마 안되는 객체가 저장되는 공간이다.

  2. Heap 영역에 객체가 생성되면 최초로 Eden 영역에 할당된다.

  3. Eden영역에 데이터가 어느정도 쌓이게 되면 참조정도에따라 Servivor의 빈 공간으로 이동되거나 회수된다.

  4. Young Generation(Eden+Servivor) 영역이 차게 되면 또 참조정도에따라 Old 영역으로 이동이 되거나 회수가 된다.

  5. Young Generation과 Tenured Generaction에서의 GC를 Minor GC라고 한다.

  6. Old 영역에 할당된 메모리가 허용치를 넘기게 되면 Old 영역에 있는 모든 객체들을 검사하여 참조되지 않는 객체들을 한꺼번에 삭제하는 GC가 실행된다.

  7. 이때, Stop-the-world이 발생하며, Old 영역의 메모리를 회수하는 GC를 Major GC라고 한다.

0개의 댓글