자바 프로그래밍은 크로스 플랫폼을 지원하기 위해 자바 가상 머신(JVM), 자바 실행 환경(JRE), 자바 개발 도구(JDK) 세 가지 중요한 구성 요소를 갖추고 있습니다.
크로스 플랫폼
다양한 운영체제나 하드웨어 환경에서 동작할 수 있는 소프트웨어나 기술을 의미합니다. 이는 하나의 소프트웨어가 여러 플랫폼에서 동일한 기능과 동작을 제공할 수 있음을 의미합니다.
- 전통적으로 각 운영체제는 자체적인 시스템 구조와 API를 가지고 있기 때문에, 한 운영체제에서 작성된 소프트웨어가 다른 운영체제에서 직접 실행되지 않는 경우가 많았습니다. 이러한 특성 때문에 개발자들은 여러 플랫폼을 지원하기 위해 각각의 운영체제에 맞게 별도의 코드를 작성해야 했습니다.
- 크로스 플랫폼 접근 방식은 이러한 문제를 해결하기 위해 등장했습니다. 크로스 플랫폼 소프트웨어나 기술은 공통된 코드베이스를 사용하여 여러 운영체제에서 동작할 수 있도록 설계됩니다. 이를 통해 개발자는 한 번의 코드 작성으로 다양한 플랫폼을 지원할 수 있으며, 소프트웨어의 개발 및 유지보수 비용을 줄일 수 있습니다.
자바는 대표적인 크로스 플랫폼 언어로, 자바 애플리케이션은 자바 가상 머신을 통해 다양한 운영체제에서 실행될 수 있습니다.
웹 기술인 HTML, CSS, JavaScript 역시 크로스 플랫폼 접근 방식을 따릅니다. 최근에는 모바일 앱 개발에서도 크로스 플랫폼 프레임워크들이 많이 사용되며, iOS와 Android 등 다양한 플랫폼에서 동일한 앱을 구동할 수 있도록 지원하고 있습니다.
WORA (Write Once Run Anywhere)
WORA는 자바 프로그래밍 언어를 사용하여 개발한 애플리케이션을 다양한 플랫폼에서 재사용 가능한 형태로 배포할 수 있는 개념을 나타냅니다.
자바 언어로 작성된 소스 코드는 JVM에서 실행되기 때문에 어떤 운영체제나 하드웨어에서라도 동일한 결과를 내놓습니다. 이는 "한 번 작성하면 어디서든 실행된다(Write Once, Run Anywhere)"는 슬로건으로 잘 알려져 있습니다.
JVM (Java Virtual Machine)
자바 가상 머신은 자바 프로그램을 실행하기 위한 가상 컴퓨터입니다. JVM은 운영체제와 자바 프로그램 사이에서 중개자 역할을 수행하여 자바 코드를 해당 플랫폼에 맞는 기계어로 변환하고 실행합니다. 이렇게 함으로써, 자바 프로그램은 특정 운영체제에 종속되지 않고 어디서든 동작할 수 있습니다. 각 운영체제에 맞게 JVM이 구현되어 있으며, 자바 애플리케이션이 JVM을 통해 실행되므로 개발자는 플랫폼 별로 다른 코드를 작성할 필요가 없습니다.
JVM의 작동 방식
- 자바 컴파일러 (Java Compiler) : 자바 소스 코드(.java 파일)는 자바 컴파일러(javac)를 사용하여 바이트 코드(.class 파일)로 변환됩니다. 컴파일 단계에서 소스 코드는 JVM이 이해할 수 있는 중간 언어인 바이트 코드로 컴파일되는데, 이는 플랫폼에 독립적입니다. (윈도우에서 컴파일 할려면 윈도우 JVM, 리눅스에서 컴파일 할려면 리눅스 JVM)
- 클래스 로더 (Class Loader) : JVM은 컴파일된 바이트 코드를 실행하기 위해 클래스 로더를 사용합니다. 클래스 로더는 필요한 클래스 파일들을 로드하고, 링크하여 런타임 환경에서 사용할 수 있는 자바 클래스로 변환합니다. 클래스 로더는 동적으로 클래스 로딩하고 언로딩하여 애플리케이션의 메모리 사용량을 최적화합니다.
- 바이트 코드 검증 (Verification) : 로드된 바이트 코드는 JVM 내의 바이트 코드 검증기에의 의해 검증됩니다. 이 검증 과정을 통해 잘못된 형식의 코드나 보안상 위험한 코드를 실행하지 않도록 보장합니다.
- 실행 엔진 (Execution Engine) : 검증된 바이트 코드는 JVM 내의 실행 엔진에 의해 실제로 실행됩니다. 실행 엔진은 바이트 코드를 명령어 단위로 해석하고, 해당 플랫폼에 맞는 기계어로 번역하면서 프로그램을 실행합니다. 이러한 실행 과정은 자바 프로그램이 효율적으로 동작할 수 있도록 합니다.
- 런타임 라이브러리(Runtime Library) : JVM은 자바 프로그램이 실행되는 동안 필요한 라이브러리들을 제공합니다. 이 라이브러리들은 자바 프로그램이 표준 기능들을 사용하거나 특정 작업을 수행할 수 있도록 도와줍니다. 예를 들어, 문자열 처리, 입출력, 네트워킹 등과 같은 기능을 제공하는 라이브러리가 포함됩니다.
클래스 로더 (Class Loader) Java 8
- 부트스트랩 클래스 로더(Bootstrap Class Loader)
- 부트스트랩 클래스 로더는 JVM 시작 시에 가장 먼저 동작하는 로더로서, JVM의 핵심 라이브러리들을 로드합니다. 이 로더는 자바의 표준 라이브러리들인 rt.jar, charsets.jar 등과 같은 라이브러리를 로드하는데 사용됩니다. 부트스트랩 클래스 로더는 네이티브 코드로 구현되어 JVM 자체의 일부로 존재합니다.
- 확장 클래스 로더(Extension Class Loader)
- 확장 클래스 로더는 부트스트랩 클래스 로더의 하위로서, 확장 디렉토리에서 클래스를 로드하는 역할을 합니다. 일반적으로 자바의 확장 라이브러리들인 ext 디렉토리에 위치한 클래스들을 로드합니다.
- 애플리케이션 클래스 로더(Application Class Loader)
- 애플리케이션 클래스 로더는 사용자가 작성한 애플리케이션 코드들을 로드하는 로더입니다. 애플리케이션 클래스 로더는 사용자 클래스패스(classpath)에서 클래스를 로드합니다. 클래스패스는 사용자가 지정한 디렉토리나 JAR 파일의 위치를 나타냅니다.
Delegation Principle (위임 원칙)
클래스 로더에서의 Delegation Principle(위임 원칙)은 클래스를 로드하는 과정에서 특정 클래스 로더가 해당 클래스 로딩을 직접 수행하지 않고, 상위 클래스 로더에게 로딩을 위임하는 원칙을 의미합니다.
main() 메서드가 포함된 ClassLoaderRunner 클래스에서 개발자가 직접 작성한 Internal 클래스를 로딩하는 과정을 그림으로 표현하면 다음과 같다.
- 클래스 로딩 요청이 들어오면 현재 클래스 로더가 해당 클래스를 먼저 찾습니다.
- 현재 클래스 로더가 해당 클래스를 찾지 못하면, 상위 클래스 로더에게 로딩을 위임합니다.
- 상위 클래스 로더도 해당 클래스를 찾지 못하면, 다시 상위 클래스 로더로 로딩을 위임합니다.
- 이 과정은 최상위 클래스 로더까지 반복됩니다. 최상위 클래스 로더인 부트스트랩 클래스 로더까지 클래스를 찾지 못하면, ClassNotFoundException이 발생합니다.
Visibility Principle (가시성 원칙)
- Visibility Principle은 클래스 로더 간의 계층 구조에서 특정 클래스 로더가 하위 클래스 로더에서 로드한 클래스를 볼 수 있지만, 상위 클래스 로더에서 로드한 클래스를 볼 수 없는 원칙을 의미합니다. 이 원칙은 클래스 로더 간의 가시성 범위를 정의하여 중복 로딩을 방지하고, 클래스 간의 의존성을 확실하게 유지하는데 도움을 줍니다.
- 클래스 로더 간의 가시성은 위임 원칙과 밀접한 관련이 있습니다. 클래스 로더가 클래스를 로드할 때는 먼저 자신의 클래스 패스(classpath)에서 해당 클래스를 찾습니다. 만약 찾지 못한다면 상위 클래스 로더에게 로딩을 위임합니다. 이때, 상위 클래스 로더는 이미 로드한 클래스들을 하위 클래스 로더에서 볼 수 없도록 가시성 범위를 설정합니다. 이렇게 하면 클래스 로더 간에 클래스가 중복 로딩되는 것을 방지하고, 서로 다른 클래스 로더가 같은 이름의 클래스를 로드하더라도 각각 독립적으로 사용할 수 있게 됩니다.
Uniqueness Principle (고유성 원칙)
Uniqueness Principle은 클래스 로더 간에 동일한 이름의 클래스를 중복해서 로드하지 않도록 하는 원칙을 의미합니다. 이 원칙은 클래스 로더의 중복 로딩을 방지하여 클래스의 유일성과 일관성을 보장합니다.
클래스 로더는 클래스를 로드할 때, 먼저 자신의 클래스 패스에서 해당 클래스를 찾고 로드합니다. 그러나 이미 상위 클래스 로더에서 해당 클래스를 로드한 경우, 하위 클래스 로더는 다시 로딩하지 않으며 이미 로드된 클래스를 재사용합니다. 이렇게 함으로써 중복 클래스 로딩을 방지하고, 클래스 간의 의존성을 일관되게 유지합니다.
실행 엔진(execution engine)
JVM의 실행 엔진(Execution Engine)은 자바 바이트 코드(Bytecode)를 실제로 실행하는 컴포넌트입니다. 실행 엔진은 JVM의 런타임 환경에서 바이트 코드를 명령어 단위로 해석하고, 해당 플랫폼에 맞는 기계어로 번역하여 자바 애플리케이션을 실행합니다.
실행 엔진은 클래스 파일을 실행시키는 방법에 따라 크게 2가지로 분류됩니다.
인터프리터(Interpreter)
인터프리터 방식은 바이트 코드를 한 줄씩 읽어들이고, 해당 명령어를 해석하고 실행하는 방식입니다. 즉, 바이트 코드를 중간 언어로 해석하면서 실시간으로 실행하는 방식입니다. 이 방식은 단순하고 빠르게 시작할 수 있지만, 실행 시에 바이트 코드를 반복적으로 해석하고 실행해야 하므로 실행 속도가 비교적 느릴 수 있습니다.
JIT 컴파일러(Just-In-Time Compiler) 방식
JIT 컴파일러 방식은 바이트 코드를 한 번에 전체 또는 큰 블록으로 컴파일하여 기계어로 변환하는 방식입니다. 이렇게 생성된 기계어 코드는 플랫폼에 특화된 기계어 명령어로 직접 실행되므로, 인터프리터 방식보다 더 빠른 실행 속도를 제공합니다. JIT 컴파일러는 실행 중에 빈번하게 실행되는 코드를 최적화하여 높은 성능을 얻는데 도움을 줍니다.
- JVM은 보통 인터프리터 방식과 JIT 컴파일러 방식을 조합하여 사용합니다. 초기에는 인터프리터 방식으로 바이트 코드를 실행하여 빠르게 시작하고, 이후에 빈번하게 실행되는 코드는 JIT 컴파일러를 통해 기계어로 컴파일하여 최적화합니다. 이렇게 하면 애플리케이션의 시작 시간을 단축하면서, 실행 중에 더 빠른 성능을 얻을 수 있습니다.
- JIT 컴파일러는 플랫폼에 맞는 기계어 코드를 생성하므로, 특정 플랫폼에서 최적화된 성능을 발휘할 수 있습니다. 또한, JVM은 실행 중인 프로그램의 동작을 분석하고 프로그램에 대한 정보를 수집하여 더 높은 최적화를 수행하는 고급 JIT 컴파일러 기술도 사용합니다. 이러한 실행 엔진의 동작 원리는 자바의 크로스 플랫폼 특성과 최적화된 성능을 가능케 합니다.
가비지 컬렉터(Garbage Collector)
가비지 컬렉터(Garbage Collector)는 가비지 컬렉션을 수행하는 역할을 하는 프로그램의 일부분입니다. 가비지 컬렉터는 자바와 같은 관리형 언어에서 사용되며, 메모리 관리를 자동으로 처리하여 사용되지 않는 메모리 블록(가비지)들을 해제하는 역할을 수행합니다.
가비지 컬렉터의 주요 역할
- 도달 가능한 객체 식별: 가비지 컬렉터는 특정 시점에서 프로그램의 모든 객체들 중에서 "루트(root)" 객체들로부터 직간접적으로 도달 가능한 객체들을 식별합니다. 이러한 객체들은 여전히 프로그램에 의해 참조되고 사용되는 객체들입니다.
- 도달 불가능한 객체 식별: 도달 가능한 객체들 이외의 객체들은 더 이상 사용되지 않으며, 가비지 컬렉션의 대상이 됩니다. 이들은 도달 가능한 객체로부터 어떠한 경로로도 도달할 수 없는 객체들로서, 프로그램에서 더 이상 접근이 불가능한 상태입니다.
- 가비지 컬렉션 실행: 가비지 컬렉터는 도달 불가능한 객체들을 식별하여 자동으로 해제하여 메모리 누수를 방지하고 사용하지 않는 메모리 블록을 회수합니다. 이러한 과정은 프로그램의 실행 중간에 비동기적으로 백그라운드에서 수행되며, 프로그래머가 직접 객체를 해제할 필요가 없습니다.
가비지 컬렉터의 장점
- 가비지 컬렉터는 자바 가상 머신(JVM) 내에 내장되어 있으며, JVM은 프로그램이 실행되는 동안 가비지 컬렉터를 주기적으로 호출하여 메모리 관리를 수행합니다.
- 가비지 컬렉터는 메모리 관리를 자동화하여 개발자가 명시적으로 메모리를 해제하는 번거로움을 피하고, 메모리 누수와 같은 문제를 사전에 방지할 수 있도록 도와줍니다.
- 자바와 같은 관리형 언어에서는 이러한 가비지 컬렉터 기능을 제공하여 프로그래머가 더 안정적이고 효율적인 애플리케이션을 개발할 수 있도록 지원합니다.
가비지 컬렉터의 단점
- 개발자가 메모리가 언제 해제되는지 정확하게 알 수 없다.
- 가비지 컬렉션(GC)이 동작하는 동안에는 다른 동작을 멈추기 때문에 오버헤드가 발생한다.
JRE (Java Runtime Environment)
JRE는 자바 프로그램을 실행하기 위한 최소한의 환경을 제공하는 패키지입니다. JRE에는 JVM, 자바 클래스 라이브러리, 자바 실행 시스템의 필수 구성 요소 등이 포함되어 있습니다. JRE는 개발자가 아닌 사용자가 자바 애플리케이션을 실행할 수 있는 환경을 제공합니다. 따라서, 자바 애플리케이션을 실행하기만 하려면 JRE만 설치하면 됩니다.
JRE는 JDK를 사용하여 작성된 Java 코드를 JVM에서 이를 실행하는 데 필요한 필수 라이브러리와 결합한 후 결과 프로그램을 실행하는 JVM의 인스턴스를 작성합니다. JVM은 다수의 운영체제에 사용 가능하며, JRE를 사용하여 작성된 프로그램이 이 모두에서 실행됩니다. 이러한 방식으로, JRE(Java Runtime Environment)는 수정 없이도 어떤 운영체제에서든 Java 프로그램을 실행할 수 있도록 합니다.
JDK (Java Development Kit)
JDK는 자바 애플리케이션을 개발하기 위해 필요한 모든 도구와 라이브러리를 포함하는 패키지입니다. JDK에는 JRE도 포함되어 있기 때문에 개발자는 JRE를 따로 설치할 필요가 없습니다. JDK에는 다음과 같은 주요 구성 요소들이 포함되어 있습니다:
- 컴파일러 (javac): 자바 소스 코드를 바이트 코드로 변환하는 역할을 합니다.
- 디버거 (jdb): 자바 애플리케이션의 디버깅을 위한 도구입니다.
- 빌드 도구 (Ant, Maven, Gradle 등): 자바 프로젝트를 빌드하고 관리하는데 사용됩니다.
- 자바 런처 (java): 자바 애플리케이션을 실행하는데 사용됩니다.
- 자바 클래스 라이브러리: 개발자들이 자주 사용하는 다양한 클래스와 API들을 제공합니다.
'JAVA 공부' 카테고리의 다른 글
메모리 사용 영역에 대해 (0) | 2023.08.08 |
---|---|
Java 8, 11, 17 (0) | 2023.08.03 |
스트림이란? (1) (1) | 2023.07.11 |
람다식이란? (0) | 2023.07.09 |
자바의 컬렉션 프레임워크(Map) (1) | 2023.01.10 |