본문 바로가기

소스·컴파일러·JVM

성공한 Hello 프로그램을 기준으로 .java, javac, .class, java, JVM의 역할을 구분합니다.

앞 문서에서 한 명령은 파일을 만들고 다른 명령은 그 결과를 실행했습니다.

이제 이미 성공한 Hello 프로그램을 기준으로 각 도구가 무엇을 입력받고 무엇을 만드는지 정리합니다.


.java에서 화면 출력까지

전체 흐름은 네 단계입니다.

  1. 사람이 src/Hello.java를 작성합니다.
  2. javac가 문법과 이름을 검사하고 out/Hello.class를 만듭니다.
  3. java 실행기가 out에서 Hello 클래스를 찾습니다.
  4. JVM이 main부터 명령을 실행해 문자열을 출력합니다.

javac와 java는 이름이 비슷하지만 역할이 다릅니다.

명령입력결과
javac -d out src/Hello.javaJava 소스 파일Hello.class 생성 또는 컴파일 오류
java -cp out Hello클래스 이름과 탐색 폴더JVM 실행 또는 실행 오류

소스를 저장했다고 .class가 자동으로 갱신되지는 않습니다.

javac가 성공해야 새 실행 파일이 만들어집니다.


JDK와 JVM

JDK는 프로그램을 개발하는 도구 묶음입니다.

컴파일러 javac, 실행기 java, 표준 라이브러리와 여러 진단 도구가 들어 있습니다.

JVM(Java Virtual Machine)은 .class의 명령을 현재 운영체제와 CPU에서 실행하는 환경입니다.

java 명령이 JVM을 시작하고 실행할 클래스를 전달합니다.

JDK
├─ javac : 소스 컴파일
├─ java  : JVM 시작
└─ 표준 라이브러리
      └─ System.out.println 같은 기능

JDK 배포판이 달라도 같은 Java 규격과 기능 버전을 구현하면 기본 언어 문법과 표준 API의 계약은 같습니다.

배포판별 지원 정책과 진단 옵션의 차이는 운영 단계에서 살펴봅니다.


클래스 경로의 가장 작은 의미

다음 명령을 다시 봅니다.

java -cp out Hello

-cp는 --class-path의 짧은 형태입니다.

여기서는 클래스 파일을 찾기 시작할 폴더라고 이해하면 됩니다.

JVM은 out 아래에서 Hello.class를 찾습니다.

다음 명령은 서로 다릅니다.

java -cp out Hello          # 올바름
java Hello                  # 현재 폴더에서 찾아 실패할 수 있음
java -cp out Hello.class    # 클래스 이름에 확장자를 붙여 잘못됨

패키지와 여러 라이브러리가 생기면 클래스 경로가 더 중요해집니다.

지금은 src에는 소스, out에는 컴파일 결과라는 한 가지 구조만 사용합니다.

소스에서 main 실행까지, JDK 도구의 경계

javac가 class를 만들고 launcher/JVM이 main·라이브러리를 호출하는 실제 실행 과정입니다. 전체 도구 경계를 유지한 채 현재 입력·산출물만 순차 강조할 수 있습니다.

소스, class 산출물, JDK 도구, JVM 실행과 화면 출력의 경계

ARCHITECTURE · TOOLCHAIN BOUNDARY

소스에서 main 실행까지, JDK 도구의 경계

보존하는 소스와 다시 만드는 class를 구분하고, javac·java launcher·JVM·표준 라이브러리가 다음 단계에 무엇을 넘기는지 추적합니다.

소스에서 main 실행까지, JDK 도구의 경계 사람이 보존하는 src의 Java 소스를 javac가 검사해 out의 class로 만들고, java launcher가 classpath root와 binary class name을 해석해 JVM을 시작하며, JVM이 main과 표준 라이브러리를 통해 화면 출력을 만드는 경계입니다. 작업 폴더 JDK 도구와 실행 환경 소스 읽기 javac -d out -cp out Hello START CALL PRINT 소스 · 보존 src/Hello.java 사람이 편집 · 저장 class · 재생성 가능 out/Hello.class JVM bytecode 화면 출력 Hello, Java! main 실행 결과 javac compiler 문법 · 이름 검사 저장만으로 갱신 안 됨 java launcher -cp out Hello out에서 클래스 탐색 JVM Hello.main bytecode 실행 표준 라이브러리 println 호출 출력 기능 제공 javac가 class를 만들고, java와 JVM이 그 class를 실행합니다.
소스에서 main 실행까지, JDK 도구의 경계 작업 폴더 source/class 2개와 JDK 도구4개, 화면출력1개; source→compiler→class→launcher→JVM→library→output. 상세 문구와 명령은 바로 아래 원래 설명에서 확인합니다. 작업 폴더 · 보존 / 산출물 JDK 도구 실행 환경 소스 읽기 생성 -cp out JVM 시작 호출 출력 소스 · 보존 src/ Hello.java 편집·저장 산출물 out/ Hello.class 재생성 가능 javac compiler 문법과 이름 검사 저장만으로 갱신되지 않음 java launcher -cp out Hello out에서 클래스 탐색 JVM Hello.main class의 bytecode 실행 표준 라이브러리 System.out.println 표준 출력 기능 제공 화면 출력 Hello, Java! main 실행 결과 관찰

JDK 제공 경계 javac · java launcher · JVM · 표준 라이브러리

  1. 소스 보존

    src/Hello.java는 사람이 편집하고 저장하는 원본입니다.

  2. javac로 class 갱신

    javac -d out이 문법과 이름을 검사해 out/Hello.class JVM bytecode를 만듭니다. 저장만으로는 class가 바뀌지 않습니다.

  3. 산출물 경계

    out/*.class는 소스에서 다시 만들 수 있는 시점별 산출물입니다.

  4. launcher가 실행 대상을 해석

    java -cp out Hello에서 out은 classpath root, Hello는 확장자 없는 binary class name입니다.

  5. JVM 시작

    java launcher가 JVM을 시작하고 발견한 class의 Hello.main을 호출합니다.

  6. 표준 라이브러리 호출

    JVM이 System.out.println을 실행해 표준 출력 기능을 사용합니다.

  7. 출력 관찰

    화면의 Hello, Java!는 main이 실행된 뒤 관찰되는 결과입니다.

  • 입력 전달과 도구 호출
  • class 생성 경로와 JVM 실행 초점

소스는 보존하고 class는 다시 만듭니다. javac의 출력은 launcher의 입력이 되고, JVM의 실행은 표준 라이브러리를 거쳐 화면 출력으로 관찰됩니다.

이 예의 조건

  • main 출력은 Hello, Java!인 원 예제이다.
관계도의 보충 설명
원 도표의 설명과 주석
구분내용
전체 읽기 정보소스에서 main 실행까지, JDK 도구의 경계 소스, class 산출물, JDK 도구, JVM 실행과 화면 출력의 경계 ARCHITECTURE · TOOLCHAIN BOUNDARY 소스에서 main 실행까지, JDK 도구의 경계 보존하는 소스와 다시 만드는 class를 구분하고, javac·java launcher·JVM·표준 라이브러리가 다음 단계에 무엇을 넘기는지 추적합니다. JDK 제공 경계 javac · java launcher · JVM · 표준 라이브러리 소스 보존 src/Hello.java 는 사람이 편집하고 저장하는 원본입니다. javac로 class 갱신 javac -d out 이 문법과 이름을 검사해 out/Hello.class JVM bytecode를 만듭니다. 저장만으로는 class가 바뀌지 않습니다. 산출물 경계 out/*.class 는 소스에서 다시 만들 수 있는 시점별 산출물입니다. launcher가 실행 대상을 해석 java -cp out Hello 에서 out 은 classpath root, Hello 는 확장자 없는 binary class name입니다. JVM 시작 java launcher가 JVM을 시작하고 발견한 class의 Hello.main 을 호출합니다. 표준 라이브러리 호출 JVM이 System.out.println 을 실행해 표준 출력 기능을 사용합니다. 출력 관찰 화면의 Hello, Java! 는 main이 실행된 뒤 관찰되는 결과입니다. 입력 전달과 도구 호출 class 생성 경로와 JVM 실행 초점 소스는 보존하고 class는 다시 만듭니다. javac의 출력은 launcher의 입력이 되고, JVM의 실행은 표준 라이브러리를 거쳐 화면 출력으로 관찰됩니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
컴파일 입력

javac가 src/Hello.java를 검사합니다.

  • 소스: src/Hello.java
  • 명령: javac -d out
  • 보존 소스를 읽어 실행 산출물을 만든다.
실행 준비

launcher가 out에서 Hello를 찾고 JVM을 시작합니다.

  • 산출물: out/Hello.class
  • 실행 명령: java -cp out Hello
  • classpath root와 binary class name이 실행 대상을 정한다.
main과 출력

JVM의 main이 표준 라이브러리 println을 호출합니다.

  • 현재 호출: Hello.main → println
  • 출력: Hello, Java!
  • class의 bytecode 실행이 라이브러리 출력으로 이어진다.

소스에서 main 실행까지, JDK 도구의 경계

javac가 class를 만들고 launcher/JVM이 main·라이브러리를 호출하는 실제 실행 과정입니다. 전체 도구 경계를 유지한 채 현재 입력·산출물만 순차 강조할 수 있습니다.

소스, class 산출물, JDK 도구, JVM 실행과 화면 출력의 경계

ARCHITECTURE · TOOLCHAIN BOUNDARY

소스에서 main 실행까지, JDK 도구의 경계

보존하는 소스와 다시 만드는 class를 구분하고, javac·java launcher·JVM·표준 라이브러리가 다음 단계에 무엇을 넘기는지 추적합니다.

소스에서 main 실행까지, JDK 도구의 경계 사람이 보존하는 src의 Java 소스를 javac가 검사해 out의 class로 만들고, java launcher가 classpath root와 binary class name을 해석해 JVM을 시작하며, JVM이 main과 표준 라이브러리를 통해 화면 출력을 만드는 경계입니다. 작업 폴더 JDK 도구와 실행 환경 소스 읽기 javac -d out -cp out Hello START CALL PRINT 소스 · 보존 src/Hello.java 사람이 편집 · 저장 class · 재생성 가능 out/Hello.class JVM bytecode 화면 출력 Hello, Java! main 실행 결과 javac compiler 문법 · 이름 검사 저장만으로 갱신 안 됨 java launcher -cp out Hello out에서 클래스 탐색 JVM Hello.main bytecode 실행 표준 라이브러리 println 호출 출력 기능 제공 javac가 class를 만들고, java와 JVM이 그 class를 실행합니다.
소스에서 main 실행까지, JDK 도구의 경계 작업 폴더 source/class 2개와 JDK 도구4개, 화면출력1개; source→compiler→class→launcher→JVM→library→output. 상세 문구와 명령은 바로 아래 원래 설명에서 확인합니다. 작업 폴더 · 보존 / 산출물 JDK 도구 실행 환경 소스 읽기 생성 -cp out JVM 시작 호출 출력 소스 · 보존 src/ Hello.java 편집·저장 산출물 out/ Hello.class 재생성 가능 javac compiler 문법과 이름 검사 저장만으로 갱신되지 않음 java launcher -cp out Hello out에서 클래스 탐색 JVM Hello.main class의 bytecode 실행 표준 라이브러리 System.out.println 표준 출력 기능 제공 화면 출력 Hello, Java! main 실행 결과 관찰

JDK 제공 경계 javac · java launcher · JVM · 표준 라이브러리

  1. 소스 보존

    src/Hello.java는 사람이 편집하고 저장하는 원본입니다.

  2. javac로 class 갱신

    javac -d out이 문법과 이름을 검사해 out/Hello.class JVM bytecode를 만듭니다. 저장만으로는 class가 바뀌지 않습니다.

  3. 산출물 경계

    out/*.class는 소스에서 다시 만들 수 있는 시점별 산출물입니다.

  4. launcher가 실행 대상을 해석

    java -cp out Hello에서 out은 classpath root, Hello는 확장자 없는 binary class name입니다.

  5. JVM 시작

    java launcher가 JVM을 시작하고 발견한 class의 Hello.main을 호출합니다.

  6. 표준 라이브러리 호출

    JVM이 System.out.println을 실행해 표준 출력 기능을 사용합니다.

  7. 출력 관찰

    화면의 Hello, Java!는 main이 실행된 뒤 관찰되는 결과입니다.

  • 입력 전달과 도구 호출
  • class 생성 경로와 JVM 실행 초점

소스는 보존하고 class는 다시 만듭니다. javac의 출력은 launcher의 입력이 되고, JVM의 실행은 표준 라이브러리를 거쳐 화면 출력으로 관찰됩니다.

이 예의 조건

  • main 출력은 Hello, Java!인 원 예제이다.

관계도의 보충 설명

원 도표의 설명과 주석
구분내용
전체 읽기 정보소스에서 main 실행까지, JDK 도구의 경계 소스, class 산출물, JDK 도구, JVM 실행과 화면 출력의 경계 ARCHITECTURE · TOOLCHAIN BOUNDARY 소스에서 main 실행까지, JDK 도구의 경계 보존하는 소스와 다시 만드는 class를 구분하고, javac·java launcher·JVM·표준 라이브러리가 다음 단계에 무엇을 넘기는지 추적합니다. JDK 제공 경계 javac · java launcher · JVM · 표준 라이브러리 소스 보존 src/Hello.java 는 사람이 편집하고 저장하는 원본입니다. javac로 class 갱신 javac -d out 이 문법과 이름을 검사해 out/Hello.class JVM bytecode를 만듭니다. 저장만으로는 class가 바뀌지 않습니다. 산출물 경계 out/*.class 는 소스에서 다시 만들 수 있는 시점별 산출물입니다. launcher가 실행 대상을 해석 java -cp out Hello 에서 out 은 classpath root, Hello 는 확장자 없는 binary class name입니다. JVM 시작 java launcher가 JVM을 시작하고 발견한 class의 Hello.main 을 호출합니다. 표준 라이브러리 호출 JVM이 System.out.println 을 실행해 표준 출력 기능을 사용합니다. 출력 관찰 화면의 Hello, Java! 는 main이 실행된 뒤 관찰되는 결과입니다. 입력 전달과 도구 호출 class 생성 경로와 JVM 실행 초점 소스는 보존하고 class는 다시 만듭니다. javac의 출력은 launcher의 입력이 되고, JVM의 실행은 표준 라이브러리를 거쳐 화면 출력으로 관찰됩니다.
예의 값과 변화
관찰·상황값·보충 정보변화·이유
컴파일 입력

javac가 src/Hello.java를 검사합니다.

  • 소스: src/Hello.java
  • 명령: javac -d out
  • 보존 소스를 읽어 실행 산출물을 만든다.
실행 준비

launcher가 out에서 Hello를 찾고 JVM을 시작합니다.

  • 산출물: out/Hello.class
  • 실행 명령: java -cp out Hello
  • classpath root와 binary class name이 실행 대상을 정한다.
main과 출력

JVM의 main이 표준 라이브러리 println을 호출합니다.

  • 현재 호출: Hello.main → println
  • 출력: Hello, Java!
  • class의 bytecode 실행이 라이브러리 출력으로 이어진다.

한 번에 다시 만들기

결과 폴더는 소스에서 다시 만들 수 있어야 합니다.

컴파일 결과가 오래됐다고 의심되면 out을 비우고 다시 컴파일합니다.

Remove-Item -Recurse -Force out -ErrorAction SilentlyContinue
New-Item -ItemType Directory out | Out-Null
javac -d out src/Hello.java
Get-ChildItem out
Hello.class

앞에서 만든 Hello.java를 컴파일하면 out 폴더에 Hello.class가 생성됩니다.

소스 파일이 늘어나면 빌드 도구가 이 목록과 라이브러리 경로를 관리합니다.

빌드 도구는 뒤에서 다루고, 지금은 명령의 입력과 출력을 직접 확인합니다.


Java의 이식성이 뜻하는 것

같은 .class를 Windows용 JVM과 macOS·Linux용 JVM이 각 환경에서 실행할 수 있습니다.

이것이 Java 이식성의 핵심입니다.

다만 실행할 JVM이 그 .class의 버전을 지원해야 합니다. 예를 들어 JDK 25에서 별도 옵션 없이 컴파일한 결과를 Java 21 JVM에서 실행할 수는 없습니다. 이 장에서 컴파일러와 실행기를 모두 25로 맞춘 이유이기도 합니다. 운영체제가 달라도 버전 조건은 그대로입니다.

이 버전 조건은 JVM 명세의 class 파일 버전 규칙에 정의되어 있습니다. 이전 버전을 대상으로 만드는 javac --release 옵션은 뒤에서 호환성을 다룰 때 배우며, 지금은 두 버전을 맞춘 상태로 실습합니다.

운영체제 차이가 모두 사라진다는 뜻은 아닙니다.

  • 파일 경로는 운영체제마다 표현이 다를 수 있습니다.
  • 텍스트 파일은 문자 인코딩을 정해야 합니다.
  • 네트워크와 파일 권한은 실행 환경의 영향을 받습니다.

이 문제를 지금 코드로 해결하려 하면 아직 배우지 않은 파일·문자 API가 한꺼번에 등장합니다.

문자와 바이트는 ch23, 파일 경로는 ch24, JVM 진단 도구는 ch38에서 각각 충분히 다룹니다.


연습 문제

다음 항목을 소스 작성, 컴파일, 실행 중 하나로 분류하세요.

  1. Hello.java의 문자열을 고친다.
  2. 세미콜론이 빠졌다는 오류를 발견한다.
  3. Hello.class를 out에 만든다.
  4. JVM이 main을 시작한다.
  5. 화면에 Hello, Java!가 나타난다.

정답은 1이 소스 작성, 2와 3이 컴파일, 4와 5가 실행 단계입니다.