CH01 사람을 사랑한 기술

챕터 소개

저자는 어떠한 기술이든(스프링을 두고 이야기 하고 있지만) 그것이 나온 배경이 있다고 강조하고 있다. 여기서 말하는 배경이란 그 기술 이전에 무엇이 있었고, 그것이 어떤 것이 불편해서 이 기술이 나왔는가를 의미한다. 결국 어떤 기술을 익힐때는 이 기술이 '무엇을 해결하고자' 하는가를 알고 익혀야한다는 의미이다.

이러한 맥락에서 저자는 바로 스프링에 대해서 다루기 전에 자바가 나온 배경에 대해서 한 번 짚고 넘어가고자 한다. 이게 이번 챕터의 핵심 내용이다. 굉장히 기본적인 것들로 알고 있었지만 내가 안다고 생각했었는데 잘못 알고 있었던 것들이 많았다는 것을 알게 되었다.

기계어란 무엇인가

내가 상식적으로 알고 있었던 기계어는 '2진법 체계로 CPU 를 조작할때 기계 레벨에서 사용되는 언어'였다. 물론 이것이 틀린 이야기는 아닌데 정의를 제대로 짚고 넘어갈 필요가 있다.

위키 정의

For code that is completely internal to some CPUs and normally inaccessible to programmers, see Microcode.

In computer programming, machine code is computer code consisting of machine language instructions, which are used to control a computer's central processing unit (CPU).

CPU를 제어하기 위해서 사용된다는 명확한 문구가 있다.

스탠포드 에듀 정의

"Software" is the general category of code which runs on the hardware. If the hardware is a player piano, then the software is the music. The common case is a "program" like Firefox -- software you run on your computer to solve a particular problem. A computer can run multiple programs at the same time, keeping their use of memory, drawing in windows etc. separated so they hopefully do not interfere with each other.

A CPU understands a low level "machine code" language (also known as "native code"). The language of the machine code is hardwired into the design of the CPU hardware; it is not something that can be changed at will. Each family of compatible CPUs (e.g. the very popular Intel x86 family) has its own, idiosyncratic machine code which is not compatible with the machine code of other CPU families.

당연히 같은 내용이다. 그런데 눈여겨 봐야할 부분은 'hardwired into the design of the CPU hardware' 이다. 이걸 이번에 나는 처음 알았다. 생각해보면 그래서 M1 이 처음 나왔을때 IDE 를 M1 용으로 다시 설치해야 했다던가 하는 호환성 문제가 있었던 것인데 그때 좀 호기심을 가지고 찾아봤었다면 좋았을 것 같다.

아무튼 누가 기계어가 뭐냐고 물어봤을때 '머신 레벨에서 CPU를 제어하기 위해서 다루는 언어' 라고만 대답해도 괜찮을 것 같다는 생각인데, 좀 더 나아가서 CPU 설계마다 기계어가 다르다는 것도 말할 수 있어야 하는 것 같다. 나는 다 똑같은 줄 알았다.

세상 모든 기계어는 하나의 동일한 체계를 따르는가. 다르다면 무엇을 기준으로 기계어가 다르며 왜 달라져야 하는가.

이미 위에서 정리했지만 기계어는 CPU의 설계마다 다르다. 왜 기계어가 CPU 설계마다 다른걸까? CPU 설계가 다르다는 것은 CPU 의 내부 구조와 동작 방식이 다르다는 것인데 이 말인즉 CPU 에 명령을 내리는 명령어가 설계마다 달라져야함을 의미한다.(이건 너무 로우레벨의 이야기라서 검증 서칭 비용이 너무 많이 들 것 같아서 바드의 이야기를 맞다고 일단 단 믿기로 한다)

아래는 CPU 의 아키텍처가 다르다고 해서 왜 기계어 체계가 달라야하냐는 나의 질문에 대한 바드의 대답이다.

  • CPU의 내부 구조와 동작 방식이 다르기 때문입니다. CPU의 아키텍처가 다르면, CPU 내부의 구조와 동작 방식도 다릅니다. 따라서, CPU가 이해하고 실행할 수 있는 명령어도 다릅니다.

  • CPU의 명령어 세트가 다르기 때문입니다. CPU의 명령어 세트는 CPU가 이해하고 실행할 수 있는 명령어의 집합입니다. CPU의 아키텍처가 다르면, CPU가 지원하는 명령어 세트도 다릅니다.

  • CPU의 성능을 최적화하기 위해서입니다. CPU마다 다른 아키텍처를 가지고 있기 때문에, CPU의 성능을 최적화하기 위해서는 CPU의 아키텍처에 맞는 기계어를 사용해야 합니다.

자바의 기계어 변환 흐름

역시 뭐든 눈으로 직접 확인을 해야하니까 아래와 같이 확인해보았다.

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello world!");
    }
}
java -Djava.class.path=. -cp . -verbose:class Main | od -t x1 | head -n 10

M1 Mac 에서 확인한 바이너리 코드

윈도우 노트북에서 확인한 바이너리 코드

왜 똑같은 것인가!? 다르길 기대하고 확인해봤는데 똑같다.

확실하게 딱 정리를 하자.

기계어는 CPU 종류마다 다른 체계를 가진다.

자바는 어떤 환경이든 동일한 바이트코드를 만들어내고 이를 JIT 컴파일러가 바이너리 코드로 만드는 단계에서 현재 CPU 설계에 맞는 바이너리 코드로 바꾼다.

추가적으로 궁금해서 알아봤다.

AI 가 워낙 틀린 말도 아는 척을 자주 해서 저걸 100% 믿을 수 없지만 확실한 것은 JIT 컴파일러가 실행시점에 참조를 통해서 CPU 의 종류를 알든 설치되는 시점에 설계 타입을 인식해서 저장을 해놓든 간에 JIT 컴파일러가 CPU 설계 종류를 알고 있고, 이에 맞게 기계어를 만들어 준다는 것을 알 수 있다.

어셈블리어의 역할과 탄생 배경

이제 CPU 종류마다 기계어가 달라야 한다는 걸 알았다. 그런데 기계어 자체가 말 그대로 기계어 이기 때문에 사람이 쉽게 인지하고 구사하기 어렵다.

아래 그림과 같이 1+3 을 명령 하려면 CPU 종류(책에서는 옛날에 나온 최초의 전자 계산기랑 상업용 컴퓨터를 경쟁 진영으로 설명했다)에 따라 다르게 명령해야했고 명령 자체가 사람이 쉽게 인지하고 구사하기 어렵다.

그래서 인간이 쉽게 인지할 수 있는 상징 체계를 통해서 이것이 기계어로 변환되는 것이 필요했고 이러한 배경에서 어셈블리어가 나왔다.

하지만 1 어셈블리어 : 1 기계어 체계라는 문제는 여전히 남아있었다. C 가 나오면서 One Source Multi Object User Anywhere 을 실현 시킬 수 있게 되었다.

가상 머신(Virtual Machine) 의 정의

위키

In computing, a virtual machine (VM) is the virtualization or emulation of a computer system. Virtual machines are based on computer architectures and provide the functionality of a physical computer.

뻔한 말 같지만 여기서 에뮬레이션이라는 단어의 정의에 대해서 짚고 가면 좋을 것 같다. 안드로이드 스튜디오에서도 에뮬레이터를 쓰는데 막연히 쓰다보니 저 용어 자체가 의미를 지니고 있음을 간과했었다.

In computing, an emulator is hardware or software that enables one computer system (called the host) to behave like another computer system (called the guest).

에뮬레이터는 위 정의대로 '컴퓨터 시스템 내부에서 또다른 컴퓨터 시스템으로서 동작하는 하드웨어 또는 소프트웨어' 라고 할 수 있다.

가상머신은 에뮬레이터와 의미가 거의 동일하다고 볼 수 있다. 가상머신은 반드시 based on 된 시스템이 있고 해당 시스템 위에서 마치 또다른 시스템처럼 '가상화'되어 동작하는 또다른 시스템인 것이다.

자바의 Java Virtual Machine(JVM) 에서 '가상 머신' 의 역할

이번 챕터를 보니 저번 멘토링 시간에 멘토님이 주신 질문이 핵심 사항을 물어보신 것이라는 것을 알 수 있었다.

자바가 플랫폼간의 차이를 극복하고 뛰어난 이식성을 가지고 동작할 수 있는 것은 JVM 이 어느 플랫폼에서든 동일하게 수행될 수 있도록 해주는 '가상 머신'으로서의 역할을 잘 수행하기 때문이다.

이 부분에 관해서 좋은 아티클을 찾았다.

왜 가상(virtual) 인가

The Java Virtual Machine, or JVM, is an abstract computer that runs compiled Java programs. The JVM is "virtual" because it is generally implemented in software on top of a "real" hardware platform and operating system. All Java programs are compiled for the JVM. Therefore, the JVM must be implemented on a particular platform before compiled Java programs will run on that platform.

실제 하드웨어 플랫폼과 OS 위에서 가상의 레이어로 동작하기 때문이다. JVM 은 기반하는 특정 플랫폼의 구현체라고 할 수 있고 모든 자바 프로그램은 이 구현체 위에서 동작한다.

즉 어떠한 플랫폼이든 간에 자바 프로그램 자체는 그것에 대한 관심을 갖지 않아도 되고 단지 가상레이어 위에서 동작할 뿐이고, JVM 이 플랫폼에 맞게 가상 레이어로서 필요한 자원을 플랫폼에서 끌어다 사용할 수 있도록 처리해준다.

It provides a layer of abstraction between the compiled Java program and the underlying hardware platform and operating system. The JVM is central to Java's portability because compiled Java programs run on the JVM, independent of whatever may be underneath a particular JVM implementation.

이 문단에서도 명확하게 나오고 있다. JVM 은 '레이어'다. 자바 프로그램과 하드웨어 플랫폼 및 OS 사이에 존재하는 레이어다. 자바 프로그램은 이 레이어 위에서 동작하기만 하면 되므로 동작해야할 플랫폼이 무엇인지에 대해서는 완전하게 독립적이다.

이는 곧 'JVM 만 있으면 어디서든 동작하므로 휴대성이 좋다' 고도 말할 수 있다.

꼭 자바로 된 프로그램이 아니어도 JVM 위에서만 동작하면 실제로 프로세스로서 동작하는 걸까

당연한 이야기지만 일부러 명시적으로 소제목으로 만들었다. 그루비나 코틀린이 그러하다.

멘토님이 설명해주셨듯 이 특정한 사실을 기억하는 것이 중요하다기 보다 'n개의 다름을 수용할 수 있는 마치 어댑터와 같은 1개의 인터페이스의 역할'에 대한 멘탈 모델을 인지하는 것이 중요한 것 같다.

Last updated