Java 컴파일부터 실행까지

들어가기

C를 배울때는 C 언어자체가 하드위어와 가까워서 코드가 전처리 -> 컴파일러 -> 어셈블리어 -> 링킹과정을 거쳐서 실행되는 것을 꼼꼼하게 살펴보고 생각하며 코딩했지만 Java를 코드만 작성하고 넘어오면서 ‘알아서 실행되네'의 마음가짐 을 가졌었다
그리고 시중에 Java책을 봤을때도 자세하게 다루지 않고 넘어갔기에 중요하지 않다고 생각했다.

하지만 누가 Java공부를 한다고하면 (if, for, 변수선언 같은) 기초문법 다음으로 꼭 Java의 컴파일 그리고 실행 과정을 꼭 보라고 말할것이다.

그래야, 나중 Spring, Lombok 같은 프레임워크를 공부할때 프레임워크가 해주는 마법같은 일이 마법이 아니라는 걸 알아차릴 수 가 있다.
(‘알아서 해주던데'라는 마음가짐은 나뻐요!!)

큰틀에서 Java 플랫폼 구조와 JDK, JRE, JVM을 볼 것이다.
긴글이 될거 같은데 마음 단단히 먹고 긴호흡으로 읽어주셨으면 좋겠다.

사전 지식

C를 조금 해봤고 Java문법을 잘 알고 코딩하는 것 자체에 충분히 익숙하다면 보는데 문제는 없을거 같다.
충분히 익숙하다는 것은 별찍기 정도는 작성할 수 있다고 가정하겠다.

                       *                        
                      * *                       
                     *****                      
                    *     *                     
                   * *   * *                    
                  ***** *****                   
                 *           *                  
                * *         * *                 
               *****       *****                
              *     *     *     *               
             * *   * *   * *   * *              
            ***** ***** ***** *****             
           *                       *            
          * *                     * *           
         *****                   *****          
        *     *                 *     *         
       * *   * *               * *   * *        
      **** *****             ***** *****       
     *           *           *           *      
    * *         * *         * *         * *     
   *****       *****       *****       *****    
  *     *     *     *     *     *     *     *   
 * *   * *   * *   * *   * *   * *   * *   * *  
***** ***** ***** ***** ***** ***** ***** *****

Java 플랫폼 구조

처음에는 Java 플랫폼에 대해서 큰 시각으로 보며 들어는 봤지만 정확하게 뭐라고 말하기 힘들었던 SE, EE, JDK, JVM에 대해서 보며 각각의 관계를 살펴볼 것이다.

Java 플랫폼은 Java Application 실행되는 특정 환경 을 뜻한다.
우리의 소스코드가 하드웨어에 반영되기 위해서는 하드웨어를 추상화 해줄 API가 필요하다. 그런 API의 모음이라고 봐도 무방하다.

Java SE, Java EE, Java ME, Java FX 어디서 들어는 봤을 것이다.
이것들은 전부 Java 플랫폼들의 이름인데 여러가지로 나뉜 이유는 하드웨어 마다 다른 제약사항들 때문에 동일한 API를 제공하기 불가능하기 때문에 각 상황에 맞는 Java를 제공하기위해 나눠졌다.

제사한 정보는 공식문서: Java EE와 Java SE의 차이점에 있지만 굳이 찾아가서 보는 수고스러움은 안해도 된다. (어쩌피 대부분 SE 아님 EE를 쓴다)

각 플렛폼에 대해서 간략하게 요약하면 다음과 같다.

  • Java SE :
    Standard Edition으로 대부분의 API가 여기에 포함된다. 모르겠다 싶으면 이거 쓰면 된다.
  • Java EE :
    Enterprise Edition인데 Java SE를 기반으로 서버측 개발을 위한 플랫폼이다.
  • Java ME :
    Micro Edition, 제안된 자원을 가진 휴데폰, 셋탑박스 (임베디드) 등을 지원하기 위한 플랫폼이다.
  • Java FX :
    Java SE의 표준 GUI 라이브러리인 스윙을 대체하기 위해 고안되었다는데 안써봐서 모르겠다.

각 Java 플랫폼에 대해 간략하게 알아봤다.
그 다음으로 자꾸 헷갈리는 JDK, JRE, JVM에 대해 알아보자

JDK, JRE, JVM

JDK, JRE, JVM는 각각의 뭔지, 차이에 대해서 설명할 수 있다면 해당 챕터는 넘어가도 된다.

다음 이미지는 Java SE Version 8의 아키텍쳐이다. javaSE8

아키텍쳐를 양쪽에 범위를 선택한 것을 보면 대충 어떤식으로 구분하는지 알 수 있을 것이다.

  • JDK(Java Development Kit):
    자바 프로그램 개발 에 필요한 구성 요소로 JRE + 개발에 필요한 툴 로 되어있다.
    툴 몇개를 설명하면

    • javac: ‘.java’ 소스를 ‘.class’ 코드로 변환하는 컴파일러
    • javap: ‘.class'를 ‘.java'로 변환하는 컴파일러
      (외부 라이브러리 디버깅에 주로 쓰인다)
  • JRE(Java Runtime Environment):
    자바 프로그램 실행 에 필요한 구성 요소로 JVM + 라이브러리 로 되어있다. JVM과 핵심 라이브러리 및 자바 런타임 환경에서 사용하는 프로퍼티 세팅, 리소스파일을 포함한다. JAVA Version 11부터는 JRE는 따로 배포하지 않는거 같다

  • JVM(Java Virtual Machine):
    자바 가상 머신으로 자바가 OS 상관없이 잘 작동하도록 도와주는 JAVA의 핵심 철학(Write once, run everywhere)의 구현체라고 할 수 있다.
    역할은 자바 바이트 코드(.Class)를 OS에 특화된 코드로 변환 및 실행을 담당 한다. 바이트 코드를 변환하는 인터프리터JIT 컴파일러가 담당한다.

    • 인터프리터 : 코드를 한 줄씩 읽어 내려가며 실행하는 프로그램으로 당연히 한 줄씩 읽고 실행하기에 최적화나 실행 측면에서 느리다.
      (C가 더 빠른 여러 이유중 하나)

    • JIT 컴파일러 : Just In Time의 약자로 인터프리터의 성능을 향상 시키기 위해 도입 이 되었다. 인터프리터가 반복된 코드를 해석한다면 JIT가 해당 코드를 캐싱한 뒤 인터프리터가 JIT의 캐싱을 재사용 하도록 하여 성능을 향상시킨다.

JDK, JRE, JVM는 전체적인 구역을 뜻하는 것이고 Java SE, EE, ME FX는 제공해주는 라이브러리(API)를 뜻한다.

JDK 유료?!

Java가 JDK 11로 넘어오면서 Oracle JDK가 상용버전에서 유료가 되었다.
하지만 그럼에도 openJDK를 검색하면 상용버전에서도 무료로 사용할 수 있는 JDK를 얻을 수 있다.

Kotlin

위에서 JVM은 .Class 파일을 OS별 네이티브 코드로 변경해준다고 했다.
즉 굳이 Java가 아니라도 .Class파일로 변경해주는 컴파일러만 있으면 다른 언어로 코딩해도 된다.
(Kotlin, Grovvy, ….)

중간 정리

Java 플랫폼, Java 아키텍쳐 그외에 짜잘한 Java에 대한 이슈를 살펴봤다.
다음으로는 나올 내용은 .java File 부터 실행까지의 내용이 나올 것이다.
위에 나오는 용어들이 익숙하지 않는다면 읽기가 힘들 수도 있다.

.java File 부터 실행까지

JVM에 대해서 읽었으면 Java가 두번 컴파일(하나는 사실 인터프리터지만)된다는 것을 알것이다.
그래서 크게 두가지로 나눠서 보려고 한다.

  • Java Compile(.java to .class)
  • JVM (.class to running)

.java File 부터 실행까지를 이쁘게 그려봤다.
java-compile-and-running 댓글에 “ㄹㅇㅋㅋ"만 치세요!

Java Compile(.java to .class)

java-compile
메모장 코딩을 해보신 분이라면 cmd에 javac라는 라고 써봤을 것이다.
javac는 Java Compile로 .java 파일을 .class로 변환 시킨다.

JVM (.class to running)

java-running

Class Loader

다음 3가지로 나뉜다.

  • 로딩 : .class 에서 바이트코드를 읽고 메모리에 저장시킨다.

  • 링크 : 3가지 엑션을 하는데

    • 증명(verification) : 로더로 읽은 .Class 파일이 포맷형식은 지키고 있는지, 유효한 컴파일러에 의해 생성되었는지 체크한다.

    • 준비(preparation) :
      모든 정적 변수에 대해 메모리가 할당되고 기본 값이 지정된다.

    • 해결(resolution) :
      모든 심볼 메모리 참조가 메소드 영역의 참조로 바뀝니다.

  • 초기화 static 변수값이 초기화되고 정적 블록이 실행된다.

더 자세한 내용을 알고 싶다면 HomeEfficio님의 Java 클래스로더 훑어보기를 읽어보는 걸 추천한다.

메모리

메모리 구성을 잘 알아야 나중에 디버깅할때 왜 Null Excption이 발생하는지 CG가 어떤식으로 제거해주는지 알 수 있다. 메모리구조는 외워야 합니다

  • Method 영역:
    클래스 로더의 로딩때 .class 파일을 읽고 그 내용에 따라 적절히 만든 바이너리 데이터가 저장된다. 클래스 수준의 정보(인터페이스, 클래스 이름, 부모 클래스, 메소드, 변수)

  • Heap 영역 :
    객체를 저장하며 공유 자원이다. 주로 new 키워드를 해서 객체를 생성하면 Heap 메모리에 저장된다.
    포인터를 안다면 이해가 될것이다. CG에 의해서 메모리 관리가 된다.

  • PC registers 영역 :
    쓰레드 마다 쓰레드 내 현재 실행할 스택 프레임을 가리키는 포인터를 저장한다. 각 쓰레드마다 별도의 PC registers를 가지고 있다.

  • Stack 영역 :
    쓰레드 별로 스택을 만들고 공유자원은 아니다.

  • Native Memory Stack 영역 : 네이티브 메소드 정보를 저장

실행 엔젠

위에서 이미 설명했음으로 자세한 내용은 Pass..

  • 인터프리터
  • JIT
  • GC

자바 네이티브 인터페이스 (JNI)

Java API 소스를 본적이 있다면 native키워드를 봤을 것이다.
native키워드를 이용해서 C, C++ 어셈블리로 작성된 함수를 제공한다.

네이티브 메소드 라이브러리

C, C++로 작성 된 라이브러리

마치며

글을 다 읽고 다음 질문들에 대해서 답할 수 있으면 충분히 이해했다고 봐도 된다.

  • Java는 한줄한줄 컴파일되고 실행되는걸까요?
  • new 한 객체는 어느 메모리에 저장될까요?
  • 싱글톤이 static 보다 애플리케이션 실행속도가 빠를까요?

참고

comments powered by Disqus