Java, JVM, JRE, JDK

기계치 컴맹·2023년 9월 17일
0

java

목록 보기
1/2
post-thumbnail

Java의 본질에 대해 정리하고 복습할 겸 기록해보겠다. 어디까지나 학습 기록과 중간 중간 생긴 내 호기심에 대한 나의 하찮은 견해이다😥

Java


우선 Java는 잘 알려져 있다시피 썬 마이크로시스템즈 (Sun Microsystems) 라는 회사에서 제임스 고슬링(James Gosling)이라는 사람을 주축으로 만들어졌다.

https://commons.wikimedia.org/wiki/File:Sun-Logo.svg

썬에 대해 잠깐 말해보자면, Stanford University Network의 약자다. 스탠포드 대학 동문들끼리 설립한 회사다. 지금은 오라클에 인수합병된지 햇수로만 14년이고, 썬이 주력하던 일들 중 오라클에 남아있는건 Java 말고는 거의 안 남아있는 것 같다. MySQL도 있지만 이도 썬이 인수했었던 것이니...썬의 주력사업은 주로 소프트웨어가 아닌 하드웨어 쪽이었다. SPARC라는 CPU에 본인들의 Solaris OS를 탑재한 서버를 파는게 주였다고 한다.

https://www.quora.com/Who-is-the-founder-java

그런 썬으로 입사해 Java를 개발했다고 잘 알려진 이가 사진의 제임스 고슬링이다. 스트레스 많이 받았을 법 한데 굉장히 인자해 보이신다. 일의 고됨은 젊은 시절부터 확인할 수 있어보이는 것 같다.

http://www.marcgeller.com/people/gosling.html

Java의 정확한 개발 연도는 다양하게 알려져있다. 1990년, 91년이라는 정보도 있고, 95년 즈음이라는 정보도 있다. 제임스 고슬링은 가전제품에 들어갈 소프트웨어를 개발하는 엔지니어였다. 당시 가전제품들은 다양한 종류의 CPU와 OS를 이용했다. 하드웨어 + 소프트웨어를 일컬어 플랫폼이라고 하는데, 그 플랫폼이 다양했다는 말이다. 원래 제임스 고슬링은 C++를 확장해 사용하려고 했으나, 플랫폼마다 다른 컴파일러가 필요한 C++이기에 아예 플랫폼과 언어를 새로 개발한 것이다. 그것이 Green이라는 플랫폼과 Oak라는 언어이다. 오크나무를 보고 오크라 지었다 카더라. 그렇게 개발하기 시작한게 90년, 91년 즈음이고 Oak가 나온 것은 92년이다.

https://en.wikipedia.org/wiki/Oak_(programming_language)#cite_note-1

JDK가 깔려있는 bin 폴더에 들어가면 java.exe가 있는데, 그 파일의 아이콘인 듀크다. 원래 Oak를 출시하며 같이 나온 스마트 에이전트이다. Oak를 사용하면 사용자를 도와주는 역할을 한다. 시리나 빅스비가 되지 못하고 커피한테 밀린 친구다.

그렇게 Oak를 개발했지만 Oak Technology라는 반도체 회사의 존재를 확인했고, 그렇게 Oak는 Java로 이름이 바뀌었다. 그리고 Java가 정식으로 JDK 1.0을 출시한게 1995년이다. 그렇게 때문에 Java의 역사의 시작을 91년, 92년 혹은 95년으로 보는 것은 그냥 편한대로 생각하자.

https://www.flickr.com/photos/xmodulo/14485179234

Java의 로고는 커피다. 이는 Java를 개발했던 개발자들이 인도네시아 자바 섬에서 재배되는 원두로 만든 커피를 즐겨 마신다는 썰이 있다. 그래서 Java의 이름도 Java다. Java를 개발했던 제임스 고슬링과 아서 반 호프, 백톨샤임의 이니셜을 따서 Java라고 지었다는 말도 있는데, 커피가 더 그럴싸하지 않은가? 😏

Java의 특징


Java의 개발자들이 주로 목표했던 점들이 몇가지 있다.

  • Simple, object-oriented, and familiar
  • Robust and secure
  • Architecture - neutral and portable
  • High performance
  • Interpreted, threaded, and dynamic

심플함과 친숙함, 객체지향, 견고함과 안전함, 플랫폼 독립성, 고성능, 인터프리터, 쓰레드, 그리고 동적 로딩 등이 핵심적으로 목표됐던 Java의 특징이다. 이를 전부 커버하기엔 너무 너무 길어질 것 같으니, 아예 이 특징들에 대해 말을 안 꺼내겠다🥱

귀찮아서 그런건 아니고, 백문이불여일견이라고 Java의 동작 방식에 대해 공부하다보면 이 특징들은 자연스럽게 이해할 것이라고 생각했다. 그래서 이 포스트도 Java의 동작 방식에 대한 내 학습 기록이다.

제임스 고슬링은 어떤게 마음에 안 들었길래 C++가 아닌 새로운 언어를 개발했던 것일까? JVM, JRE, JDK가 Java를 동작시키는데, JVM부터 간략하게 기록하겠다.

JVM (Java Virtual Machine)


JVM은 Java가 실행될 수 있는 가상 머신이고, Java가 동작하는데 핵심적인 역할을 한다. JVM은 Java로 쓰여진 코드들을 기계어(Machine Code)로 변환해 OS에게 전달하는 과정 중 하나이다.

기계어란 데이터나 명령을 컴퓨터에게 전달할 때 컴퓨터가 알아들을 수 있도록 바이너리(0, 1)로 된 코드이다. 컴퓨터는 신호가 on(1)인지 off(0)인지로 전달받은 데이터나 명령을 해석하는데, 이 머신코드가 CPU의 설계마다 해석하는 수행이 다르고 운영체제마다 해석이 다르다. 플랫폼(CPU + OS)마다 알아듣는 머신코드가 다르다는 말이다. 같은 mov라는 명령이더라도 이걸 구성하는 머신코드가 플랫폼마다 다르다.

C++ 코드로 실행 파일을 만들면 컴파일러마다 실행 파일이 다르게 생성된다.

https://medium.com/@neil.wilston123/why-java-is-platform-independent-1d82c2249a69

Windows OS에서 컴파일되어 생성된 파일은 Windows OS에서는 돌아가겠지만 다른 플랫폼에서는 돌아가지 않는다. 컴파일러가 해당 플랫폼에 맞는 머신코드로 번역해서 파일을 만들기 때문이다. 그래서 플랫폼별로 다른 컴파일러를 사용해 실행 파일을 만들어야 해당 플랫폼에 돌아간다. 그렇기 때문에 C++은 Platform Dependent, 즉 플랫폼에 의존하는 플랫폼 종속적인 언어이다.

Java는 다르다.

https://net-informations.com/java/intro/jvm.htm
Linux쪽 화살표는 착한 사람 눈에만 보입니다. 전 보여요

Java의 경우 Java 코드를 우선 컴파일해서 .class 파일을 생성한다. Java 컴파일러로 생성한 .class 파일은 바이너리가 아닌 바이트 코드로 이루어져있다. 바이너리로 변환하기 전에, JVM이 이해할 수 있는 바이트 코드로 우선 변환을 한다. 그러면 JVM은 플랫폼에 맞춰 바이트 코드를 바이너리 코드로 변환해준다. 그래서 원시적인 Java 코드.java와 Javac로 컴파일 된 파일.class은 어떤 OS던 맞는 JVM만 있다면 독립적으로 존재할 수 있기에 Java를 Platform Independent, 플랫폼 독립적이라고 한다.

JVM은 단순히 바이트 코드를 플랫폼에 맞는 바이너리 코드로 변환시켜주는 용도만 있는게 아니다. Java를 특별하게 만들어주었던 메모리 관리와 가비지 콜렉터이 존재하고, 멀티스레드 등을 가능하게 만드는 것이 JVM이다. 이러한 장점들 덕에 Java Virtual Machine이라는 이름에도 불구하고 해석가능한 바이트 코드로 변환이 가능한 다른 언어로도 사용도 가능하고... JVM을 더 깊게 공부해보고 따로 기록을 남겨 볼 생각이다.

이런 플랫폼 독립성이 원래 가전제품용 소프트웨어 엔지니어였던 제임스 고슬링이 C++의 확장이 아닌 다른 언어를 개발하려던 이유이지 않았을까. 가전제품은 Processor 혹은 Control Unit이 더 다양하니까 말이다. 여기서 단순한 의문이 들었다.

플랫폼별 컴파일러가 존재하는 것과 OS별 JVM이 존재하는 것은 뭐가 다른건가?
플랫폼 독립성을 가진 것은 Java이지 JVM은 아니지 않나?

JVM과 플랫폼 독립성에 대한 학습과 내 하찮은 의견은 길어질 것 같으니 나중에 따로 포스팅 해야겠다. (귀찮아서 그런거 아님) 😕

JRE (Java Runtime Environment)


Java를 쓰면 java.lang, java.util 등 기본적으로 제공되는 라이브러리들을 쓰기 마련이다. 이러한 라이브러리들은 JRE에 포함되어 있다. 즉, JRE는 Java로 개발을 하기 위한 라이브러리들과 UI 툴킷 등이 모여 있는 실행 환경이다.

내가 이해한 JRE와 JVM

Class Loader


Class Loader는 우선 소스 코드가 컴파일 된 .class 파일을 포함해 참조한 다양한 클래스들을 동적으로 불러온다. 사용하겠다 선언한 클래스들만 불러온다는 말이다. 이는 JRE에 기본적으로 제공되는 라이브러리에 있을 수도 있고, 웹 상에 존재할 수도 있다!

인터넷은 정보가 정말 많다. 근데 너무 많다. JVM과 JRE에 대한 공부를 할 때 누군가는 Class Loader를 JVM 안에 포함시키고, 또 누군가는 JVM 밖에 그리고 JRE 안에 포함시킨다. 그래서 나는 JRE가 Class Loading을 하고 JVM한테 넘겨주는건가 싶었다. verbose로 간단한 파일을 실행시켜보니, JRE라는 이름처럼, JRE는 그저 런타임 환경이다. JVM처럼 실행하는 것이 아닌 환경인 것이다.

헷갈릴 땐 명칭을 보고

"왜 이런 명칭으로 지었을까?"

에 대해서 생각해보면 도움이 되는 것 같다.

아무튼 클래스 로더는 내 생각엔 JVM에 포함되는 것이 맞다. JRE는 그냥 라이브러리와 부수적인 파일들이 들어있는 환경이다.

Bytecode Verifier


이렇게 불러온 클래스들을 Bytecode Verifer가 일반적인 Java 규정에 맞는 바이트 코드인지 확인해 보안성을 높여주는 역할을 한다.

이후 Interpreter에 넘겨져 해석을 진행하는데, 쓰다보니 JRE가 아닌 JVM에 대한 설명이 된 것 같다. Class Loader, Bytecode Verifier, Execute Engine, Runtime Data Area 등 JVM의 구조는 나중에 더 자세하게 기술하고싶다😎

JDK (Java Development Kit)


여러 클래스들을 참조한 .class 파일을 동작시키기 위해서는 JRE만 있으면 실행이 가능하다. JRE에 있는 라이브러리들을 사용하는게 아니라면, JVM만으로도 가능하다. 하지만 Java로 코드를 짜서 .java 확장자로 저장한다면, JVM은 이게 바이트 코드가 아니기에 해석하지 못한다. 그래서 .java의 소스 코드를 바이트 코드로 컴파일 시켜주어야 하는데 그를 위한 툴들까지 포함된 것이 JDK이다.

https://techvidvan.com/tutorials/java-virtual-machine/

궁극적으로 JDK의 컴파일러가 .java 파일을 바이트 코드로 이루어진 .class 파일로 컴파일하고, 그걸 JRE에서 JVM이 컴퓨터와 직접적으로 소통할 수 있는 머신 코드로 변환시켜 실행한다.
이게 내가 이해한 Java의 실행 방식이다.

뜯어보기


IDE 없이 간단한 Hello World를 구현해봤다.

C:\Users\USER\Desktop\공부\Java>vi HelloWorld.java

public class HelloWorld {
	public static void main(String[] args) {
		System.out.println(“Hello World!);
	}
}

C:\Users\USER\Desktop\공부\Java>ls
 C 드라이브의 볼륨에는 이름이 없습니다.
 볼륨 일련 번호: D8A9-5F09

 C:\Users\USER\Desktop\공부\Java 디렉터리

2023-09-16  오전 03:06    <DIR>          .
2023-09-16  오전 03:06    <DIR>          ..
2023-09-16  오전 03:05               523 .HelloWorld.java.un~
2023-09-16  오전 03:05               116 HelloWorld.java
2023-09-16  오전 03:05               116 HelloWorld.java~
               3개 파일                 755 바이트
               2개 디렉터리  110,899,326,976 바이트 남음
내용을 입력하세요.

대충 바탕화면에 디렉토리를 만들어 헬로월드를 만들었다. 디렉토리에 이렇게 .java 파일만 있을 때 java.exe를 실행시켜보았다.

C:\Users\USER\Desktop\공부\Java>java HelloWorld
Error: Could not find or load main class HelloWorld
Caused by: java.lang.ClassNotFoundException: HelloWorld
내용을 입력하세요.

Class를 찾지 못했다고 떴다. 이는 확장자가 .class인 HelloWorld라는 이름을 가진 파일이 없기 때문에 뜬 에러다. java.exe는 JVM을 생성하고 JVM 구조대로 동작한다. JVM이 알아먹기 위해선 .class라는 바이트코드로 된 파일이 있어야 하는데 없으니 에러가 뜬 것이다. 올라온 에러는 java.lang.ClassNotFoundException이라는 에러로, JRE에 포함된 java.lang에 있는 예외 중 하나이다. 그러니까, 만약 .class가 있다면 JDK가 아닌 JRE만 있어도 실행시키는데는 문제 없을 것이다.

아무튼, 이번엔 확장자를 포함해 HelloWorld.javajava.exe로 실행시켜보았다.

C:\Users\USER\Desktop\공부\Java>java HelloWorld.java
Hello World!
내용을 입력하세요.
이번엔 헬로 월드가 정상적으로 출력되었다...


C:\Users\USER\Desktop\공부\Java>ls
 C 드라이브의 볼륨에는 이름이 없습니다.
 볼륨 일련 번호: D8A9-5F09

 C:\Users\USER\Desktop\공부\Java 디렉터리

2023-09-16  오전 03:06    <DIR>          .
2023-09-16  오전 03:06    <DIR>          ..
2023-09-16  오전 03:05               523 .HelloWorld.java.un~
2023-09-16  오전 03:05               116 HelloWorld.java
2023-09-16  오전 03:05               116 HelloWorld.java~
               3개 파일                 755 바이트
               2개 디렉터리  110,899,830,784 바이트 남음
내용을 입력하세요.

.class 파일은 여전히 없는데 말이다. 왜 그럴까…하는 마음으로 어떤 java.exe를 실행하는지 확인했다.

C:\Users\USER\Desktop\공부\Java>where java
C:\Program Files\Common Files\Oracle\Java\javapath\java.exe
C:\Program Files (x86)\Common Files\Oracle\Java\javapath\java.exe
C:\Program Files\Java\jdk-17.0.5\bin\java.exe
C:\jdk-11\bin\java.exe
내용을 입력하세요.

허허..😅
정확한 이해를 위해 잠시 Java 11만 남기고 환경변수에서 전부 뺐다.

C:\Users\USER\Desktop\공부\Java>where java
C:\jdk-11\bin\java.exe
내용을 입력하세요.

이제 다시 .java 확장자를 포함해 HelloWorld를 java.exe로 실행시켜보았다.

C:\Users\USER\Desktop\공부\Java>java HelloWorld
Error: Could not find or load main class HelloWorld
Caused by: java.lang.ClassNotFoundException: HelloWorld

C:\Users\USER\Desktop\공부\Java>java HelloWorld.java
Hello World!

C:\Users\USER\Desktop\공부\Java>ls
 C 드라이브의 볼륨에는 이름이 없습니다.
 볼륨 일련 번호: D8A9-5F09

 C:\Users\USER\Desktop\공부\Java 디렉터리

2023-09-16  오전 03:06    <DIR>          .
2023-09-16  오전 03:06    <DIR>          ..
2023-09-16  오전 03:05               523 .HelloWorld.java.un~
2023-09-16  오전 03:05               116 HelloWorld.java
2023-09-16  오전 03:05               116 HelloWorld.java~
               3개 파일                 755 바이트
               2개 디렉터리  111,189,626,880 바이트 남음
내용을 입력하세요.

결과는 똑같았다. 왜 그럴까?

java.exe는 JVM을 생성하고 실행시키는데 이 때 인풋으로 .class 파일의 바이트 코드들이 들어간다. 이 때 java.exe는 인풋에 java 확장자가 있으면 내부적으로 바이트 코드로 변환하고 실행시키는 것이 아닐까 하는 생각이 든다. 정확한 답을 위해 스택오버플로우 형님들한테 물어봤다.

https://stackoverflow.com/questions/77114812/how-do-java-exe-execute-a-java-source-code?noredirect=1#comment135946543_77114812

물어본지 얼마 되지도 않았는데 바로 답변이 달렸다. 친절한 형님들…
JEP 330에 따르면, java.exe로 java 확장자가 붙어있는 파일을 오픈하면 자체적으로 메모리에 컴파일을 해서 바이트 코드를 생성하고 그걸 JVM에 넣는 것이다.

호기심에 못 이겨 직접 눈으로 보고싶었다. javac.exe를 실행시켜 .java 파일을 .class로 컴파일도 시킬 겸 verbose로 javac 과정을 봤다.

C:\Users\USER\Desktop\공부\Java>javac -verbose HelloWorld.java
[parsing started SimpleFileObject[C:\Users\USER\Desktop\공부\Java\HelloWorld.java]]
[parsing completed 31ms]
[loading /modules/jdk.scripting.nashorn.shell/module-info.class]
[loading /modules/jdk.hotspot.agent/module-info.class]
[loading /modules/jdk.zipfs/module-info.class][loading /modules/jdk.compiler/module-info.class]
[loading /modules/java.prefs/module-info.class]
[loading /modules/jdk.httpserver/module-info.class]
[loading /modules/jdk.attach/module-info.class]
[loading /modules/jdk.javadoc/module-info.class]
[loading /modules/jdk.management.jfr/module-info.class]
[search path for source files: .]
[search path for class files: C:\jdk-11\lib\modules,.]
[loading /modules/java.base/java/lang/Object.class]
[loading /modules/java.base/java/lang/String.class]
[loading /modules/java.base/java/lang/Deprecated.class][loading /modules/java.base/java/lang/Comparable.class]
[loading /modules/java.base/java/lang/CharSequence.class]
[wrote HelloWorld.class]
[total 374ms]
내용을 입력하세요.

먼저 JDK에서 컴파일을 하기위해 모듈들을 로딩하고 동적으로 로딩할 모듈들 또한 바이트 코드에 포함시키는 것 같다.

.class도 컴파일 됐나 확인하면

C:\Users\USER\Desktop\공부\Java>ls
 C 드라이브의 볼륨에는 이름이 없습니다.
 볼륨 일련 번호: D8A9-5F09

 C:\Users\USER\Desktop\공부\Java 디렉터리

2023-09-16  오후 08:09    <DIR>          .
2023-09-16  오후 08:09    <DIR>          ..
2023-09-16  오전 03:05               523 .HelloWorld.java.un~
2023-09-16  오후 08:09               426 HelloWorld.class
2023-09-16  오전 03:05               116 HelloWorld.java
2023-09-16  오전 03:05               116 HelloWorld.java~
               4개 파일               1,181 바이트
               2개 디렉터리  110,783,922,176 바이트 남음
내용을 입력하세요.

HelloWorld.class가 예상대로 생성됐다.

갑자기 또 궁금해졌었다. 여기서 HelloWorld.javajava.exe로 오픈하면 어떻게 될까?

C:\Users\USER\Desktop\공부\Java>java -verbose HelloWorld.java
[0.009s][info][class,load] opened: C:\jdk-11\lib\modules
[0.022s][info][class,load] java.lang.Object source: jrt:/java.base
[0.022s][info][class,load] java.io.Serializable source: jrt:/java.base
[0.023s][info][class,load] java.lang.Comparable source: jrt:/java.base
…
[0.773s][info][class,load] sun.nio.ch.IOStatus source: jrt:/java.base
[0.775s][info][class,load] javax.tools.SimpleJavaFileObject source: jrt:/java.compiler
[0.775s][info][class,load] com.sun.tools.javac.launcher.Main$1 source: jrt:/jdk.compiler
[0.776s][info][class,load] java.lang.UnsupportedOperationException source: jrt:/java.base
[0.777s][info][class,load] java.io.CharArrayReader source: jrt:/java.base
…
[1.074s][info][class,load] javax.tools.ForwardingJavaFileManager source: jrt:/java.compiler
[1.074s][info][class,load] com.sun.tools.javac.launcher.Main$MemoryFileManager source: jrt:/jdk.compiler
[1.076s][info][class,load] com.sun.tools.javac.api.ClientCodeWrapper source: jrt:/jdk.compiler
[1.078s][info][class,load] com.sun.source.util.TaskListener source: jrt:/jdk.compiler
…
[2.549s][info][class,load] com.sun.tools.javac.launcher.Main$MemoryFileManager$1 source: jrt:/jdk.compiler
[2.549s][info][class,load] com.sun.tools.javac.launcher.Main$MemoryFileManager$1$1 source: jrt:/jdk.compiler
[2.550s][info][class,load] com.sun.tools.javac.jvm.ClassWriter$1 source: jrt:/jdk.compiler
[2.551s][info][class,load] com.sun.tools.javac.jvm.ClassFile$NameAndType source: jrt:/jdk.compiler
…
[2.411s][info][class,load] jdk.internal.misc.JavaIOFilePermissionAccess source: jrt:/java.base
[2.413s][info][class,load] java.io.FilePermission$1 source: jrt:/java.base
[2.414s][info][class,load] java.io.FilePermissionCollection source: jrt:/java.base
[2.415s][info][class,load] java.io.FilePermissionCollection$1 source: jrt:/java.base
[2.416s][info][class,load] HelloWorld source: file:/C:/Users/USER/Desktop/%ea%b3%b5%eb%b6%80/Java/
[2.567s][info][class,load] com.sun.tools.javac.resources.LauncherProperties$Errors source: jrt:/jdk.compiler
[2.569s][info][class,load] com.sun.tools.javac.resources.launcher source: jrt:/jdk.compiler
…
[2.579s][info][class,load] java.text.MessageFormat$Field source: jrt:/java.base
error: class found on application class path: HelloWorld
[2.581s][info][class,load] java.lang.Shutdown source: jrt:/java.base
[2.582s][info][class,load] java.lang.Shutdown$Lock source: jrt:/java.base
내용을 입력하세요.

class found on application class path: HelloWorld. 헬로월드라는 클래스가 이미 존재해서 에러가 뜬다. 흥미로웠던 것은 .java 확장자 파일이 java.exe로 오픈되면 .class 파일이 있건 말건 클래스들 부터 일단 로딩하고 javac를 런칭해서 MemoryFileManager와 함께 JVM에서 클래스를 작성한다. 그러고나서 소스를 뒤져보니 .class 파일이 있으면 에러를 레이즈 한다는 것이다.

그러면 class 파일을 지우고 다시 HelloWorld.javajava.exe로 오픈하면 시간이 비슷하게 걸리지 않을까?

C:\Users\USER\Desktop\공부\Java>del HelloWorld.class

C:\Users\USER\Desktop\공부\Java>ls
 C 드라이브의 볼륨에는 이름이 없습니다.
 볼륨 일련 번호: D8A9-5F09

 C:\Users\USER\Desktop\공부\Java 디렉터리

2023-09-16  오후 09:39    <DIR>          .
2023-09-16  오후 09:39    <DIR>          ..
2023-09-16  오전 03:05               523 .HelloWorld.java.un~
2023-09-16  오전 03:05               116 HelloWorld.java
2023-09-16  오전 03:05               116 HelloWorld.java~
               3개 파일                 755 바이트
               2개 디렉터리  110,586,535,936 바이트 남음
내용을 입력하세요.

HelloWorld.class 지웠다.

C:\Users\USER\Desktop\공부\Java>java -verbose HelloWorld.java
[0.008s][info][class,load] opened: C:\jdk-11\lib\modules
[0.022s][info][class,load] java.lang.Object source: jrt:/java.base
[0.022s][info][class,load] java.io.Serializable source: jrt:/java.base
…
[1.102s][info][class,load] com.sun.tools.javac.launcher.Main$MemoryFileManager source: jrt:/jdk.compiler
[1.104s][info][class,load] com.sun.tools.javac.api.ClientCodeWrapper source: jrt:/jdk.compiler
[1.104s][info][class,load] com.sun.source.util.TaskListener source: jrt:/jdk.compiler
…
[2.439s][info][class,load] com.sun.tools.javac.launcher.Main$MemoryFileManager$1 source: jrt:/jdk.compiler
[2.439s][info][class,load] com.sun.tools.javac.launcher.Main$MemoryFileManager$1$1 source: jrt:/jdk.compiler
[2.440s][info][class,load] com.sun.tools.javac.jvm.ClassWriter$1 source: jrt:/jdk.compiler
[2.441s][info][class,load] com.sun.tools.javac.jvm.ClassFile$NameAndType source: jrt:/jdk.compiler
…
[2.445s][info][class,load] HelloWorld source: __JVM_DefineClass__
…
[2.449s][info][class,load] java.nio.LongBuffer source: jrt:/java.base
[2.449s][info][class,load] java.nio.DirectLongBufferU source: jrt:/java.base
Hello World!
[2.451s][info][class,load] java.util.IdentityHashMap$IdentityHashMapIterator source: jrt:/java.base
[2.451s][info][class,load] java.util.IdentityHashMap$KeyIterator source: jrt:/java.base
[2.455s][info][class,load] java.lang.Shutdown source: jrt:/java.base
[2.455s][info][class,load] java.lang.Shutdown$Lock source: jrt:/java.base
내용을 입력하세요.

역시 무조건 javac를 켜서 자체적인 메모리에 소스 코드의 클래스를 생성시킨다. 그리고 그 소스가 실제 디렉토리가 아닌 __JVM_DefineClass__이면 그 아웃풋을 내는 것이다. 심지어 소스를 확인하고 JVM에서 런타임하는게 아니라 이미 머신코드로 다 만들어놓고 소스를 확인한 다음 바로 출력하는 것 같다.

  1. Java 소스 코드 --> javac(바이트 코드) --> 머신코드
    javac HelloWorld.java [HelloWorld.java] >> 컴파일 >> [HelloWorld.class]
    java HelloWorld [HelloWorld.class] >> JVM >> [머신코드]

  2. Java 소스 코드 --> 머신코드
    java HelloWorld.java [HelloWorld.java] >>컴파일>>JVM>> [JVM DefineClass] >> [머신코드]

라는 말이다. OpenJDK에 따르면 싱글파일, 즉 Java 라이브러리들이 아닌 다른 클래스를 참조하지 않는 파일만 가능한 것 같다.

Single-file programs -- where the whole program fits in a single source file -- are common in the early stages of learning Java, and when writing small utility programs.

OpenJDK JEP 330

별 의미 없이 입문자들 더 쉽게 하라고 한 것이다. 그러니까…그닥 중요한건 아닌데…호기심에 못 이겨 또 이렇게까지 빠져버렸다…😛😝

아무튼 다시 본론으로 돌아와서 다시 헬로월드 클래스 파일을 생성했다.

C:\Users\USER\Desktop\공부\Java>javac HelloWorld.java

C:\Users\USER\Desktop\공부\Java>ls
 C 드라이브의 볼륨에는 이름이 없습니다.
 볼륨 일련 번호: D8A9-5F09

 C:\Users\USER\Desktop\공부\Java 디렉터리

2023-09-16  오후 09:59    <DIR>          .
2023-09-16  오후 09:59    <DIR>          ..
2023-09-16  오전 03:05               523 .HelloWorld.java.un~
2023-09-16  오후 09:59               426 HelloWorld.class
2023-09-16  오전 03:05               116 HelloWorld.java
2023-09-16  오전 03:05               116 HelloWorld.java~
               4개 파일               1,181 바이트
               2개 디렉터리  110,580,568,064 바이트 남음
내용을 입력하세요.

바이트 코드로 어떻게 컴파일 하는지는 위에서 대충 해봤으니, 바이트 코드 자체를 봐야겠다.

C:\Users\USER\Desktop\공부\Java>type HelloWorld.class
龕봅7
      
<init>()VCodeLineNumberTablemain([Ljava/lang/String;)V
SourceFileHelloWorld.java

Hello World!
내용을 입력하세요.

깨져서 알아보기는 힘들지만 이게 HelloWorld.java의 변환된 바이트 코드이다. 역시 난 JVM이 아니라 못 알아먹나보다🤗

이걸 역어셈블러로 해석해보았다.

C:\Users\USER\Desktop\공부\Java>javap -c HelloWorld.class
Compiled from "HelloWorld.java"
public class HelloWorld {
  public HelloWorld();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String Hello World!
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}
내용을 입력하세요.

바이트 코드는 이러한 JVM이 이러한 순서로 실행하도록 쓰여있는 것이었다! 이 명령문들에 대해서는 추후에 JVM에 대한 포스트를 써보며 뜯어보도록 하겠다. JVM 이 놈도 참 뜯어보고싶고 궁금해지는게 많다.

자꾸 딴길로 새면 안된다는 걸 알면서도 공부하다보면 호기심을 못 이겨 이것 저것 시도해볼 때가 많은 것 같다 ㅎㅎ;; 공부할거 참 많은데 빠져버려서 깊이 들어가버리곤 한다. 내가 호기심을 막을 순 없을 것 같으니 잠을 줄이는 수 밖에…

References
https://net-informations.com/java/intro/jvm.htm
https://ladofa.blogspot.com/2018/07/c-1.html
https://blog.yevgnenll.me/posts/optimizing-java-jvm-overview

profile
개발 애송이

0개의 댓글