김 정출 2024. 10. 8. 16:39

JVM

  • JVM(Java Virtual Machine)은 자바 애플리케이션을 실행하기 위한 가상 머신으로, 자바 바이트코드를 실행하는 데 필요한 환경을 제공합니다.
  • JVM은 자바 프로그램을 플랫폼에 독립적으로 실행할 수 있도록 해 주며, 다음과 같은 주요 기능을 가지고 있습니다.

1. 플랫폼 독립성

  • 자바는 "Write Once, Run Anywhere"라는 슬로건 아래 개발되었습니다. 이는 자바 코드가 한 번 작성되면, 어떤 운영 체제에서든 JVM이 설치된 환경에서 실행될 수 있음을 의미합니다. JVM은 각 플랫폼에 맞게 구현되기 때문에, 자바 바이트코드가 플랫폼에 종속되지 않습니다.

2. 바이트코드 실행

  • 자바 컴파일러는 자바 소스 코드를 바이트코드로 변환합니다. JVM은 이 바이트코드를 읽고 실행하여 최종적으로 애플리케이션의 기능을 수행합니다.

3. 메모리 관리

  • JVM은 메모리 관리를 담당하며, 특히 가비지 컬렉션을 통해 사용하지 않는 객체를 자동으로 메모리에서 제거하여 메모리 누수를 방지합니다.

4. JIT(Just-In-Time) 컴파일

  • JVM은 실행 중인 바이트코드를 네이티브 코드로 컴파일하여 성능을 향상시키는 JIT 컴파일러를 제공합니다. 이렇게 하면 반복적으로 호출되는 코드의 성능을 크게 개선할 수 있습니다.

5. 보안

  • JVM은 보안 모델을 가지고 있으며, 자바 애플리케이션이 실행되는 환경을 격리하여 악의적인 코드로부터 보호합니다. 이를 통해 샌드박스(Sandbox) 환경을 제공하여, 불법적인 시스템 접근을 제한합니다.

6. 플러그인 및 확장성

  • JVM은 다양한 언어와 플랫폼의 인터페이스를 지원합니다. 예를 들어, Kotlin, Scala, Groovy와 같은 언어들이 JVM에서 실행될 수 있습니다.

JVM은 자바 생태계에서 매우 중요한 역할을 하며, 자바 프로그램의 효율적이고 안전한 실행을 보장합니다.


JVM의 주요 컴포넌트

  • JVM(Java Virtual Machine)은 자바 프로그램을 실행하기 위해 필요한 다양한 내부 컴포넌트를 가지고 있습니다. 이러한 컴포넌트들은 각각의 역할을 수행하여 자바 애플리케이션의 효율적인 실행, 메모리 관리, 보안 및 성능 최적화를 지원합니다. 주요 내부 컴포넌트는 다음과 같습니다.

1. Class Loader

  • 역할: 자바 클래스 파일을 로드하고, 런타임에 클래스를 동적으로 로드합니다.
  • 기능:
    • 클래스를 메모리에 로드하고, 검증(verification), 준비(initialization), 해석(interpreting), 및 실행(execution) 과정을 처리합니다.
    • 클래스 로딩을 위한 다양한 전략을 지원합니다(예: 시스템 클래스 로더, 사용자 정의 클래스 로더 등).

2. Execution Engine

  • 역할: 로드된 바이트코드를 실행하는 컴포넌트입니다.
  • 구성 요소:
    • Interpreter: 바이트코드를 한 줄씩 읽고 실행하는 역할을 합니다. 초기 실행 속도가 빠르지만, 반복 호출 시 성능이 떨어집니다.
    • JIT Compiler (Just-In-Time Compiler): 자주 호출되는 바이트코드를 네이티브 코드로 변환하여 성능을 향상시키는 역할을 합니다.
    • Garbage Collector (GC): 사용하지 않는 객체를 자동으로 메모리에서 제거하여 메모리 관리를 돕습니다.

3. Java Native Interface (JNI)

  • 역할: 자바 애플리케이션과 C/C++와 같은 네이티브 언어로 작성된 코드 간의 상호작용을 가능하게 합니다.
  • 기능:
    • 자바에서 네이티브 메서드를 호출하거나, 네이티브 라이브러리를 사용할 수 있도록 합니다.
    • 성능이 중요한 부분에 대해 자바 외부의 코드를 호출할 수 있습니다.

4. Java Memory Model

  • 역할: JVM에서 메모리를 관리하는 구조입니다.
  • 구성 요소:
    • Heap: 객체와 배열이 저장되는 메모리 공간으로, 가비지 컬렉션의 대상입니다.
    • Stack: 메서드 호출 시 생성되는 스택 프레임이 저장되는 공간으로, 로컬 변수 및 메서드 호출 정보를 포함합니다.
    • Method Area: 클래스 메타데이터, 필드, 메서드 정보 등이 저장되는 공간입니다. JVM이 시작될 때 초기화되며, 모든 스레드가 공유합니다.
    • Program Counter (PC) Register: 현재 실행 중인 JVM 명령어의 주소를 저장하는 레지스터로, 각 스레드마다 별도의 PC 레지스터를 가집니다.

5. Execution Engine (Execution Manager)

  • 역할: JVM의 주요 실행을 관리하는 컴포넌트입니다.
  • 기능:
    • 쓰레드 관리, CPU 자원 관리 및 명령어 실행을 담당합니다.
    • 병렬 및 동시 실행을 지원하여 성능을 극대화합니다.

6. Garbage Collector

  • 역할: JVM에서 자동으로 메모리를 관리하는 컴포넌트입니다.
  • 기능:
    • 사용되지 않는 객체를 식별하고 메모리에서 제거합니다.
    • 다양한 가비지 수집 전략(예: Serial, Parallel, Concurrent Mark-Sweep 등)을 제공하여 성능을 최적화합니다.

7. Security Manager

  • 역할: 자바 애플리케이션의 보안을 관리하는 컴포넌트입니다.
  • 기능:
    • 클래스와 리소스에 대한 접근을 제어하여 악의적인 코드 실행을 방지합니다.
    • 권한을 관리하고, 필요한 경우 접근 권한을 요청하도록 합니다.

8. Just-In-Time (JIT) Compiler

  • 역할: 바이트코드를 실행 중에 네이티브 코드로 변환하여 성능을 최적화합니다.
    • 네이티브 코드는 특정 플랫폼의 CPU가 직접 실행할 수 있는 기계어 코드
  • 기능:
    • 자주 호출되는 메서드와 루프인 hotspot 을 분석하여 동적으로 최적화된 네이티브 코드를 생성합니다.
    • 프로그램 실행 중 최적화할 수 있는 기회를 제공하여 성능을 극대화합니다.
    • JIT 컴파일이 필요하므로 초기 실행 속도는 느릴 . 수있으며, 네이티브 코드를 메모리에 저장하여 추가적인 메모리 사용이 필요하나, 전체 애플리케이션의 성능을 크게 향상 시킬 수 있습니다.

9. Native Method Stack

  • 역할: JNI를 사용하여 호출된 네이티브 메서드를 위한 스택입니다.
  • 기능:
    • C/C++로 작성된 네이티브 메서드를 호출할 때 필요한 로컬 변수를 저장합니다.

JVM 설치 및 배포

Linux에서 JDK 설치하기

sudo apt update
sudo apt install openjdk-11-jdk # OpenJDK 설치

vi /etc/environment # 환경 변수 설정 
---
JAVA_HOME="/usr/lib/jvm/java-11-openjdk-amd64"

source /etc/environment

java -version # 설치 확인

Java 코드 작성 및 실행

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

Java 프로그램 컴파일 및 실행하기

javac HelloWorld.java

Byte코드 실행하기

java HelloWorld
---
Hello, World!

Docker Container Image 빌드

Dockerfile 작성하기

# Use an official OpenJDK runtime as a parent image
FROM openjdk:11-jre-slim

# Set the working directory in the container
WORKDIR /app

# Copy the compiled Java class file into the container
COPY HelloWorld.class .

# Command to run the Java program
CMD ["java", "HelloWorld"]

이미지 빌드하기

docker build -t hello-world-java .

Docker container 실행하기

docker run hello-world-java

Kubernetes Deployment로 실행하기

deployment.yaml 작성

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello-world-java
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hello-world-java
  template:
    metadata:
      labels:
        app: hello-world-java
    spec:
      containers:
      - name: hello-world-java
        image: <your-dockerhub-username>/hello-world-java
        ports:
        - containerPort: 8080  # 필요에 따라 포트 수정

deployment 생성하기

kubectl apply -f deployment.yaml

service 생성하기

apiVersion: v1
kind: Service
metadata:
  name: hello-world-java-service
spec:
  selector:
    app: hello-world-java
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080  # 필요에 따라 수정
  type: LoadBalancer  # 또는 NodePort
kubectl apply -f service.yaml

결론

이러한 컴포넌트들은 JVM이 자바 프로그램을 효과적으로 실행하고, 메모리를 관리하며, 보안을 유지하는 데 필수적입니다. 각각의 구성 요소는 자바 생태계의 높은 이식성과 성능을 가능하게 하여, 개발자들이 효율적으로 애플리케이션을 개발할 수 있도록 돕습니다.