자바 코드는 대체 누가 실행하는가
Article
자바를 처음 배울 때 대부분 이렇게 시작한다. 파일을 하나 만들고 Hello.java 라고 이름 붙인 다음, 코드를 적는다.
public class Hello {
public static void main(String[] args) {
System.out.println("안녕");
}
}
그리고 터미널에 이렇게 친다.
java Hello
- 오류: 기본 클래스 Hello을(를) 찾거나 로드할 수 없습니다.
- 원인: java.lang.ClassNotFoundException: Hello
파일은 분명히 거기 있다. 이름도 틀리지 않았다. 그런데 못 찾겠다고 한다.
이 에러는 자바가 코드를 어떻게 실행하는지 모르면 계속 만난다. 그리고 그 구조를 한 번 이해하고 나면, 앞으로 만날 수많은 "왜 안 되지?"가 미리 풀린다.
이번 강의는 문법을 하나도 다루지 않는다. 대신 Hello.java 라는 텍스트 파일이 화면에 "안녕"을 찍기까지, 그 사이에 무슨 일이 벌어지는지 본다.
자바 코드는 스스로 실행되지 않는다
.java 파일은 그냥 글자가 적힌 텍스트 파일이다. 메모장으로 열어도 읽힌다. 확장자를 .txt 로 바꿔도 내용은 그대로다.
컴퓨터는 이 글자들을 그 자체로는 이해하지 못한다. public 이 무슨 뜻인지, System.out.println 이 뭘 하라는 건지 모른다.
그러니 누군가 이 텍스트를 읽고, 무슨 뜻인지 해석하고, 실제 작업으로 옮겨줘야 한다. 자바는 그 일을 두 사람에게 나눠 맡긴다. 이게 자바의 가장 큰 특징이고, 아까 그 에러의 원인이기도 하다.
사람의 글자를 기계의 글자로
첫 번째 담당자는 컴파일러다. 이름은 javac 다. java compiler 의 준말이다.
javac 는 .java 파일을 읽어서 .class 파일을 만들어낸다.
javac Hello.java
이 명령을 치면 화면에는 아무것도 안 뜬다. 대신 같은 폴더에 Hello.class 라는 파일이 새로 생긴다. 아무 말이 없다는 건 잘 됐다는 뜻이다. 자바 컴파일러는 칭찬에 인색하다.
- Hello.java사람이 읽는 텍스트
- javac컴파일러가 번역한다
- Hello.class바이트코드
- JVM읽고 실행한다
- 화면에 "안녕"
두 번째 담당자가 마지막 칸의 JVM 이다. Java Virtual Machine, 자바 가상 머신이다. java 명령을 치면 JVM 이 뜬다.
.class 안에는 무엇이 들어 있나
Hello.class 를 메모장으로 열면 깨진 글자만 잔뜩 나온다. 사람이 읽으라고 만든 파일이 아니기 때문이다.
그래도 안을 들여다보는 도구가 있다. javap 다.
javap -c Hello.class
- Compiled from "Hello.java"
- public class Hello {
- public Hello();
- Code:
- 0: aload_0
- 1: invokespecial #1
- 4: return
- public static void main(java.lang.String[]);
- Code:
- 0: getstatic #7
- 3: ldc #13
- 5: invokevirtual #15
- 8: return
- }
getstatic, ldc, invokevirtual, return. 우리가 쓴 적 없는 단어들이다.
public Hello(); 라는 덩어리도 눈에 걸린다. 우리는 그런 걸 쓴 적이 없다. javac 가 몰래 하나 만들어 넣었다. 이름이 클래스 이름과 똑같은 이 덩어리를 생성자라고 부르는데, 11강에서 만난다. 지금은 "안 쓴 것도 생길 수 있다"는 것만 봐두자.
실제로 돌려보면 각 줄 뒤에 // Field java/lang/System.out:... 같은 주석이 더 붙는다. 여기서는 지웠다.
이건 자바가 아니다. 사람이 읽으라고 만든 글자가 아니라, JVM 이 읽으라고 만든 글자다. 이걸 바이트코드라고 부른다.
우리가 System.out.println("안녕") 이라고 쓴 한 줄이, JVM 이 알아듣는 네 개의 명령으로 잘게 쪼개진 것이다.
그래서 아까 왜 안 됐나
이제 맨 처음의 에러를 다시 보자.
java Hello
java 는 JVM 을 띄우는 명령이다. JVM 은 바이트코드를 읽는다. 그러니 Hello.class 를 찾는다.
그런데 우리는 javac 를 부른 적이 없다. Hello.class 가 아직 세상에 없다. JVM 은 Hello.java 를 거들떠보지도 않는다. 자기가 읽을 줄 아는 글자가 아니기 때문이다.
- 01
코드를 쓴다
파일 이름은 Hello.java. 클래스 이름과 같아야 한다.
public class Hello { public static void main(String[] args) { System.out.println("안녕"); } } - 02
컴파일한다
Hello.class 가 생긴다. 화면에는 아무것도 안 뜬다.
javac Hello.java - 03
실행한다
확장자를 빼고 클래스 이름만 적는다. Hello.class 라고 쓰지 않는다.
java Hello
3단계에서 java Hello.class 라고 쓰면 또 에러가 난다. java 명령에게 넘기는 것은 파일 이름이 아니라 클래스 이름이기 때문이다. 21강에서 패키지를 배우고 나면 이 이름이 shop.Main 처럼 길어진다.
JVM 은 자바로 만들어지지 않았다
여기서 헷갈리기 쉬운 지점이 하나 있다.
JVM 은 자바 코드를 실행해주는 프로그램이다. 그렇다면 JVM 자신은 무엇으로 만들어졌을까? 자바로 만들었다면, 그 JVM 은 또 누가 실행하나?
가장 널리 쓰이는 JVM 인 HotSpot 은 주로 C++ 로 작성됐다. 운영체제가 직접 실행할 수 있는 보통의 프로그램이다.
자바를 실행해주는 프로그램은 자바가 아니다. 당연해 보이지만, 이걸 짚고 넘어가면 다음 이야기가 쉬워진다.
"한 번 쓰면 어디서든 돈다"
자바를 소개할 때 늘 따라붙는 문구다. Write Once, Run Anywhere. 이게 무슨 뜻인지 이제 설명할 수 있다.
Hello.class 는 어느 운영체제에서 만들었든 내용이 똑같다. 윈도우에서 컴파일한 .class 를 그대로 맥에 복사해도 돌아간다.
운영체제마다 다른 것은 .class 가 아니라 JVM 이다.
윈도우
Hello.class + 윈도우용 JVM
.class파일은 완전히 동일하다- 윈도우용 JVM 이 바이트코드를 읽는다
- 화면 출력은 윈도우 방식으로 처리한다
- JVM 자체는
.exe다
맥 · 리눅스
Hello.class + 그쪽 JVM
.class파일은 완전히 동일하다- 맥용 JVM 이 같은 바이트코드를 읽는다
- 화면 출력은 맥 방식으로 처리한다
- JVM 자체는
.exe가 아니다
운영체제 차이를 JVM 이 혼자 뒤집어쓴다. 그 덕분에 우리가 쓴 코드는 어디서든 똑같이 동작한다.
정확히 말하면 "한 번 쓰면 어디서든 돈다"가 아니라 "한 번 쓰면 JVM 이 있는 곳이면 어디서든 돈다" 이다.
JDK, JRE, JVM
자바를 설치하려고 검색하면 이 세 단어가 한꺼번에 쏟아진다. 관계만 잡아두자.
- JDK
- 개발 도구
- javac
- javap
- JRE
- JVM
- 표준 라이브러리
- 개발 도구
만든 프로그램을 실행만 하려면 JRE 로 충분하다. 코드를 작성하려면 javac 가 필요하니 JDK 를 깔아야 한다.
우리는 코드를 쓸 거니까 JDK 를 설치한다. 그러면 JRE 도 JVM 도 그 안에 다 들어 있다.
정리하면
한 줄로 줄이면 이렇다.
자바 코드는 javac 가 바이트코드로 번역하고, JVM 이 그 바이트코드를 읽어 실행한다.
- 01
.java는 텍스트다그 자체로는 아무 일도 하지 않는다. 사람이 읽으라고 만든 글자다.
- 02
javac가.class로 번역한다결과물이 바이트코드다. JVM 이 읽으라고 만든 글자다.
javac Hello.java - 03
JVM 이 바이트코드를 실행한다
파일 이름이 아니라 클래스 이름을 넘긴다.
java Hello - 04
JVM 은 운영체제마다 다르다
바이트코드는 어디서나 같다. 그래서 코드가 옮겨 다닐 수 있다.
- 05
JDK 를 설치한다
javac 와 JVM 이 함께 들어 있다.
이 시리즈에서 앞으로 배울 문법은 대부분 javac 가 검사하는 영역이다. 타입을 틀리게 쓰면 실행되기 전에 컴파일러가 먼저 잡아낸다. 자바를 배우는 일의 절반은 컴파일러와 대화하는 법을 익히는 것이다.
다음 강의에서는 아까 그냥 지나친 한 줄을 뜯어본다.
public static void main(String[] args)
단어가 다섯 개나 붙어 있다. JVM 은 왜 하필 이 이름을 찾는지, 그리고 저 단어들이 각각 무슨 뜻인지 본다. 솔직히 말하면 2강에서는 전부 설명하지 않는다. public 과 static 의 진짜 의미는 한참 뒤에 가서야 답할 수 있다.