본문 바로가기

개인적으로 공부한 것을 정리해 놓은 블로그입니다 틀린 것이 있으면 댓글 부탁 드립니다!


JAVA

JAVA 스터디 1-JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가.

반응형

목표

자바 소스 파일(.java)을 JVM으로 실행하는 과정 이해하기.

학습할 것

  • JVM이란 무엇인가 v
  • 컴파일 하는 방법 v
  • 실행하는 방법 v
  • 바이트코드란 무엇인가 v
  • JIT 컴파일러란 무엇이며 어떻게 동작하는지v
  • JVM 구성 요소 v
  • JDK와 JRE의 차이 

1. JVM이란 무엇인가?

 

자바의 가상머신으로 자바 소스코드를 운영체제가 이해할 수 있는 기계어(바이트코드)로 변환하여 

CPU나 운영체제에 상관없이 자바 파일을 실행해주는 역할을 한다. 

자바의 클래스 파일 실행 순서는 아래의 그림과 같다.

JRE에 포함되 잇으며 , 클래스로더 , excution engine , runtime data area , garbage collecotor로 이루어져 있다.

 

 

2. 자바 파일 생성 및 클래스파일로 컴파일 하는 방법

: 메모장으로 자바파일 생성

메모장으로 메인 메서드를 포함하는 클래스를 만들고 메인 메서드 안에서

"Hello"라는 문자열을 출력하도록 하였다.

해당 메모를 저장하면서 확장자를 모든파일로 지정하고 Program.java라는 이름으로 저장한다. 

 

메모장에 적은 문자열이 자바 파일로 저장되었다.

 

: 자바파일(소스코드)을 컴파일러를 통해  클래스파일로 컴파일하는 방법

 

명령 프롬프트로 해당파일이 위치한 경로를 찾아가고 

javac 자바파일명

javac는 JDK에 들어있다 . 자바 파일을 컴파일 해준다.

 

java , javac 같은 명령어에 

java + <-옵션명> + 클래스 파일명    옵션을 줘서 명령을 실행 시킬 수 있다.

 

-cp : 컴파일하기 위해서 참조할 클래스 파일들을 찾기위해 컴파일시 클래스 파일 경로를 지정해 주는 옵션 

       ex) javac -cp C:\Users\USER\자바 Program

 

-encoding : 소스 파일에 사용된 문자열 인코딩을 설정한다. 

      ex) javac -encoding UTF-8 Program.java

 

-d: 클래스 파일을 생성할 디렉토리를 지정한다. -d 옵션이 없다면 default로 소스파일이 위치한 디렉토리에 클래스 파일을 생성한다. 

 

이외에 등의 옵션들이 있다 아직 이해하기에는 모르는 개념들이 많아서 공부를 더 하고 하나씩 찾아봐야겠다.

-g

-nowarn

-verbose

-deprecation

-sourcepath

-target

-bootclasspath

 -extdirs

 

* javac 명령어 옵션은
sjava.net/2008/02/javac-%EB%AA%85%EB%A0%B9%EC%96%B4%EC%9D%98-%EC%98%B5%EC%85%98-%EC%A0%95%EB%A6%AC/ 에서 발췌

 

 

2. 자바 파일 실행 방법

java명령어를 통해 컴파일 된 클래스 파일을 실행 시킨다.

 

*위에 있는 것들을 해보고 class 파일을 지우고  java 파일을 실행했는데 문제없이 java파일이 실행 됬다 

왜 되는걸까 ...

 

찾아 보았더니 cmd에서  java를 실행시키는 java 라는 명령어에 몇가지 옵션이 있었다 . 

위의 Program.java 파일을 예로 옵션들을 설명하면

 

1.확장자 없이 파일명만 적어주는경우

ex) java  Program 

- 이 경우에는  컴파일 된 클래스 파일을  JVM으로 실행한다. class 파일이 없을 경우 위 명령어는 실행되지 않는다.

 

2. 프로그램명과 확장자명을 같이 적어주는 경우

ex) java Program.java

- 자바 파일을 컴파일하여 JVM으로 실행시킨다 단 , class 파일은 생성되지 않는다.

 

3.클래스파일 위치에서 클래스파일의 이름의 클래스를 JVM으로 실행 

ex) java -cp C:\Users\USER\자바 Program

- 정확한 위치의 파일을 실행시키고 싶을때 사용한다. 

 

javac의 옵션들 처럼 java도 여러가지 옵션이 있다.

아직 이해 안되는 개념들이 많기 때문에 더 공부후에 다시 찾아봐야겠다.

 

JAVA 명령어의 옵션정리 참조

sjava.net/2008/02/java-%EB%AA%85%EB%A0%B9%EC%96%B4%EC%9D%98-%EC%98%B5%EC%85%98-%EC%A0%95%EB%A6%AC/

 

program file - java - JDK- bin 폴더에 가면 실행시킬 수 있는 exe들이 많이있다 java javac도 여기에 들어있다. 

한번 쫙 찾아보고 어떤 거구나 정도는 익혀놔야겠다. 

 

 

3. 바이트코드란 무엇인가

 

특정 하드웨어가 아닌 가상 컴퓨터에서 돌아가는 실행 프로그램을 위한 이진 표현법, 하드웨어가 아닌 소프트웨어에 의해 처리되기 때문에 기계어(native 코드)보다 더 추상적이다.

* 컴퓨터 과학에서 추상화(abstraction)는 복잡한 자료, 모듈, 시스템 등으로부터 핵심적인 개념 또는 기능을 간추려 내는 것을 말한다.

바이트 코드는 명령 집합이 0개 이상인 매개변수를 갖는 1바이트 크기의 명령코드(opcode)이기 때문에 Bytecode라 불리게 되었다. 특정 하드웨어에 대한 의존성을 줄이고 , 인터프리팅도 쉬운 결과물을 생성하고자 출력코드의 형태로 사용된다. 컴파일되어 만들어진 바이트코드는 특정 하드웨어의 기계 코드를 만드는 컴파일러의 입력으로 사용되거나 ,가상 컴퓨터에서 실행된다.

   소스 코드와 비교한다면 , 바이트코드는 덜 추상적이지만 더 간결하고 , 더 컴퓨터 중심적이다. 예를 들어 변수의 접근 범위(지역변수 또는 전역변수인지 여부) 등과 같은 의미 분석 단계의 결과를 부호화한다. 그래서 일반적으로 소스 코드를 직접 분석. 실행하는 것보다 더 좋은 성능을 보여준다.

 

*위키피디아 '바이트코드'에서 자료 참조 

 

 

4. JIT 컴파일러란 무엇이며 어떻게 동작하는가

JIT는 just in time의 약자이며 말그대로 실시간으로 런타임 시점에 바이트코드를 기계어로 컴파일하는 컴파일러다. 반복되는 코드를 캐싱하여 캐싱된 코드를 사용하기 때문에 인터프리터의 속도를 개선시킨다. JVM에 Excution Engine에 속해 있으며  다섯 단계로 바이트코드를 네이티브 코드로 생성한다. 

 

JIT Complier의 성능최적화

1. 인라인( inlining)

 작은 메서드의 트리를 병합하거나 '인라인'하여 그들의 호출 트리를 만들어서 자주 호출되는 메서드의 호출 속도를 향상시킨다.

2.지역 최적화(Local optimizations)

 한 번에 코드의 작은 부분을 분석하고 성능을 향상시킨다. 주로 코드의 중복된 연산제거 , 예측가능한 값의 대치를 처리한다.

3. 제어 흐름 최적화 (Control flow optimizations)

 메서드나 그 내부의 흐름을 분석하고 코드 경롤를 재정렬하여 효율성을 향상 시킨다.

4.전역 최적화 (Globla optimizations)

 전체 탐색영역에서 가장 좋은 해를 찾는 것을 목표로 하여 컴파일 시간은 더 요구하지만 성능을 크게 향상시킬 수 있다.

5.네이티브 코드 생성

 

 

일단 클래스로더가 가져온 바이트 코드를 인터프리터는 메서드 단위로 읽어들이다, 적절한 시점에 바이트코드 전체를 컴파일 하여 네이티브 코드로 변경하고, 이후에는 더이상 인터프리팅 하지 않고 네이티브 코드로 직접 실행한다.
네이티브 코드는 캐시에 보관하기 때문에, 한번 컴파일된 코드는 빠르게 수행한다.컴파일 과정은 인터프리팅하는 것보다 훨씬 오래 걸리므로, 한번만 실행된다면 인터프리팅하는 것이 유리하다. JVM은 내부적으로 해당 메서드가 얼마나 자주 수행되는지 체크하고, 일정 정도를 넘을 때만 컴파일을 수행한다.

  JIT 컴파일러는 결과적으로 성능을 향상시키지만 프로세서와 메모리를 사용하기 때문에 JVM이 처음 시작할때 많은 메서드들이 call 되고 그 메서드들을 컴파일하게 되어 startup time에 영향을 줄 수밖에 없다. 이 때문에 현업에서는 warm-up이라하여 미리 애플리케이션을 미리 몇 번 구동시키는 방식을 사용한고 들었다.

 

 

* 참조 네이버 O2 d2.naver.com/helloworld/1230    

         Abloullaite Med  aboullaite.me/understanding-jit-compiler-just-in-time-compiler/ 

         j4bez님 blog j4bez.tistory.com/6?category=811686

 

  5.JVM 구성요소

 

  - 클래스로더 ( class loader)

 

   소스 코드 ( .java)가  컴파일러를 거치면 바이트코드로 변환되어 클래스 파일이 생성된다(.class)  이때 클래스파일은 바이트코드로 포맷되어있고 , 참조하는 라이브러리를 포함하지 않고 단순히 symbolic references(특정 메모리 주소를 참조 관계로 구성한 것이 아닌 참조대상 이름만 지칭한 상태)를 가진다. 

  각각의 클래스 로더들은 부모와 자식 형태인 계층적 모델을 취하고 있고 delegate(위임) 방식으로 작업을 진행한다.

Bootstrap Class loader - Extension ClassLoader - Application ClassLoader 순으로 클래스를 로딩한다 .

  각 클래스 로더는 로드된 클래스들을 보관하는 네임스페이스를 갖는다. 클래스를 로드할 떄 이미 로드된 클래스인지 확인하기 위해서 네임스페이스에 보관된 FQCN(Fully Qualified Class Name)을 기준으로 클래스를 찾는다 비록 FQCN이같더라도 네임스페이스가 다르면 , 즉 다른 클래스 로더가 로드한 클래스이면 다른 클래스로 간주된다. 

 

클래스 로딩은 세부적으로 로딩 , 링크 , 초기화라는 세 단계 과정을 거친다. 

 

로딩Loading

- 클래스 파일을 바이트 코드로 읽어 메모리로 가져오는 과정

 

Bootstrap ClassLoader : jre 의 lib 폴더에 rt.jar 파일을 뒤져 기본 자바 API 라이브러리를 로드하며 , 가장 최우선으로 로드 된다. VM의 일부로 구현되(c언어로 만들어짐)기 때문에 다른 class loader와는 달리 자바 코드로 인스턴스화 할 수 없다

 

Extension ClassLoader: jre의 lib 폴더에 있는 ext 폴더에 모든 확장 코어 클래스파일들을 로드한다(Platform Classloader

라고도 부른다).  Bootstrap ClassLoader의 child 이고 ,Extension 클래스 로더는 JAVA_HOME/lib/ext의 jar 파일 혹은 java.ext.dirs 에 저장된 경로에서 로드된다.

 

Application ClassLoader - Extension ClassLoader의 child이며 시스템  클래스로더(System ClassLoader)라고도 불린다. 어플리케이션 레벨에 있는 클래스들(사용자가 직접 정의한 클래스들)을 로드하고 Classpath 환경변수에 있는 클래스 파일이나 -classpath 또는 -cp 명령어 옵션이 있는 파일들을 로드한다.

 

링크Linking

- 가장 복잡한 과정으로 , 읽어본 바이트 코드가 자바 규칙을 따르는지 검증하고 , 클래스에 정의된 필드 ,메소드, 인터페이스들을 나타내는 데이터 구조를 준비하며, 그 클래스가 참조하는 다른 클래스를 로딩한다.

 

검증(verifiy): 바이트코드 검증기가 생성된 자바 바이트코드가 적절한지 아닌지에 대해서 검증하며 검증에 실패할 경우 검증오류를 내보낸다.

준비(prepare): 모든 정적변수의 메모리가 할당되며 기본 default 값으로 할당된다(초기화는 되지 않은 상태)

해석(resolve): 모든 심볼릭한 메모리 참조를 메소드 영역에 있는 타입으로 직접 참조한다.

 

초기화(initialize)

- 클래스 로딩의 마지막 단계로 모든 정적 변수가 자바 코드에 명시된 값으로 초기화되며 정적블록이 실행된다. 

 

  - Execution Engine

 

   클래스 로딩을 끝낸 Class 파일은 Rutime Date Area의 Method 영역에 배치 되고 JVM은 로딩된 바이트코드들을 Execution Engine으로 제공하여 기계어(native code)로 변환하고 명령어 단위로 실행 한다.  인터프리터(interpreter)방식과 JIT(just-in-time)컴파일러(밑에서 자세히 설명) 두가지 방식으로 코드를 실행한다.

 

1. Interpreter 방식

    -바이트코드를 한 줄씩 해석, 실행하는 방식이다. 초기의 방식으로 , 코드를 읽는 속도는 빠르지만 실행 속도가 느리다

 

2. JIT컴파일

    -Interpreter를 보완 하기위해 나온 방식이다.  JVM은 내부적으로 해당 메서드가 얼마나 자주 호출되고 실행되는지 체크하고 , 일정 기준을 넘었을 때에만 JIT 컴파일러를 통해 컴파일하여 네이티브 코드로 변경하고 캐시에 보관한다. 한번 컴파일된 코드는 다음번 사용될때 캐시에서 바로 꺼내어 실행하기 떄문에 빠르게 수행된다. JIT 컴파일러를 통한 컴파일 과정은 바이트 코드를 바로 네이티브 코드로 만드는 것이 아니라 안에서 IR(intermediate Representation)로 변환하여 최적화를 수행하고 그 다음에 네이티브 코드로 변환하는 과정을 거친다.

  오라클 핫스팟 VM은 핫스팟 컴파일러라고 불리는 JIT 컴파일러를 사용하는데 내부적으로  프로파일링을 통해 가장 컴파일이 필요한 부분,  즉 '핫스팟'을 찾아내서 컴파일한다. 한번 컴파일된 바이트코드라도 해당 메서드가 더이상 자주 불리지 않는다면 , 캐시에서 네이티브 코드를 덜어내고 다시 인터프리터 모드로 동작한다.

 

- Garbage Collecotor(GC)(나중에 자세히)

  Heap 메모리 영역에 생성된 객체들 중에 참조하지 않는 객체들을 탐색후 제거한다. 

 

- Runtime Data Area 

  JVM이 운영체제로부터 할당 받은 메모리 영역이며 애플리케이션을 실행할 때 사용되는 데이터들을 적재하는 영역.

각각의 용도에 따라 Method area, Heap area ,stack area , PC register , Native Method Stack 다섯 영역으로 나눠지며 

Method Area와 Heap은 모든 Thread에 공유된다.

 

1.Method area

 - 모든 쓰레드가 공유하는 영역이며 JVM이 시작될 때 생성된다. 클래스와 인터페이스의 메소드에 대한 바이트코드 , 전역변수 (클래스 변수) , 런타임 상수 풀이 위치한다. 오라클의 Hot Spot VM 에서는 Permanent Area, Permanet Generation(PermGen)이라 부른다

  *실행 상수 풀(Runtime Constant Pool)

 - 클래스 파일 포맷에서 constant_pool 테이블에 해당하는 영역이다. 메서드 영역에 포함되는 영역이긴 하지만, JVM 동작에서 가장 핵심적인 역할을 수행하는 곳이기 때문에 JVM 명세에서도 따로 중요하게 기술한다. 각 클래스와 인터페이스의 상수뿐만 아니라, 메서드와 필드에 대한 모든 레퍼런스까지 담고 있는 테이블이다. 즉, 어떤 메서드나 필드를 참조할 때 JVM은 런타임 상수 풀을 통해 해당 메서드나 필드의 실제 메모리상 주소를 찾아서 참조한다.

 

2.Heap 

- new 키워드를 통해 생성된 인스턴스와 배열이 저장되는 공간으로 가비지 컬렉션 대상이다. JVM 성능 등의 이슈에서 가장 많이 언급되는 공간이다. 힙 구성 방식이나 가비지 컬렉션 방법 등은 JVM 벤더의 재량이다.

 

3.Stack (JVM Stack)(임시메모리)

 - 메인 메서드가 실행될때 마다 할당되는 스택 프레임(Stack Frame) 이라는 각 쓰레드 마다 하나씩 존재하고 메소드가 실행될 구조체를 저장한다.

-JVM은 오직 JVM 스택에 메소드에 대한 스택 프레임을 추가(push)하고 제거(pop)하는 동작만 실행한다.

-자바에서 예외 발생시 printStackTrace() 등의 메서드로 보여주는 Stack Trace의 각 라인은 하나의 스택 프레임을 표현한다.

-실행되는 메소드의 스택 프레임을 가지는데 각 스스택 프레임에는 지역변수 , 메소드의 인자 , 메소드의 리턴값 , 리턴 번지 등이 저장되고 스택 프레임은 메소드가 끝나면 사라진다.

 

4.PC 레지스터 (Program Counter Register)

 - 쓰레드가 시작될 때 생성되며 쓰레드 마다 하나씩 존재하여 실행 할 JVM의 명령어의 주소를 가리킨다.

 

 

6.네이티브 메소드 스택(Native Method Stack) 

-자바이외의 언어로 작성된 네이티브 코드를 위한 스택 , JNI(Java Native Interface)를 통해 호출하는 c/c++ 등의 코드를 수행하기 위한 스택이다.

 

 

6.JDK와 JRE의 차이 

  - JDK(Java Development Kit) 

  자바 프로그래밍 개발 환경 키트 , JRE(자바 실행 환경) 컴파일러(javac) ,java, javadoc,debug 등 개발에 필요한 도구들을 포함하고 있다

  - JRE(Java Runtime Environment)

  컴파일된 클래스 파일을 실행하는 실행환경이며 실행에 필요한 Class Library와 JVM(Java virtual Machine)을포함 하고 있다 .

출처 https://www.oracle.com

 

반응형