자바 프로그래밍을 시작할 때 알아야 할 기본적인 정보를 정리해보자 !

앞선 시간에는 프로그래밍 개발 단계에 대해서 공부해보았다. 이번에는 Java 로 프로그램을 만들고 실행하는 방법 및 JVM 의 동작에 대해서 자세히 공부해보자.

 

🧩 JDK (Java Development Kit)

  • 자바 프로그램을 개발하고 실행하기 위해서는 먼저 Java SE(자바 표준 문법) 의 구현체인 JDK 를 설치해야 한다.

  • JDK 는 자바 개발 키트의 약자로, 개발자들이 자바로 개발하는데 사용하는 SDK 키트이다. (SDK? Software Development Kit! 소프트웨어를 만들기 위한 라이브러리, 컴파일러, 디버거, 코드샘플 및 문서로 구성된 개발 키트를 말한다.)

🧩 JRE (Java Runtime Enviroment)

  • 자바 실행 환경의 약자. JVM 이 자바 프로그램 파일을 실행시킬 때 반드시 필요한 라이브러리 및 기타 필수 파일을 갖고 있다.

  • JDK 를 사용하여 작성된 Java 코드를, 이를 실행하는데 필요한 필수 라이브러리와 JVM 을 결합하여 JVM이 실행 가능하도록 인스턴스를 작성한다.

🧩 JVM (Java Virtual Machine)

  • 자바 바이트코드를 실행할 수 있는 주체!

  • 먼저 컴파일에 대해서 이해해야 한다. 컴파일은 인간의 언어로 이해 가능한 소스 코드를 컴퓨터가 이해할 수 있는 기계어로 변환해주는 작업을 말하는데, .java 파일을 컴파일 하면 생성되는 .class 파일은 기계어가 아닌 바이트코드 파일이다. 결과적으로 이 파일을 갖고서는 컴퓨터에서 실행시킬 수가 없고, 바이트코드 파일을 기계어로 또 변환해줘야 한다.

  • 그런데 중요한 것은, 자바 프로그램은 운영체제 소프트웨어 위에서 실행된다는 점이다. 다시 말해 운영체제에 의존적이다. 그렇다면 윈도우에서 개발한 파일을 맥에서 실행시킬 수 없다는 이야기일까? 답은 NO 이다. 바이트코드 파일은 운영체제와 상관없이 모두 동일한 내용으로 생성되지만, JVM 이 각각의 운영체제에서 이해하는 기계어로 번역을 해준다.

  • 때문에, JVM 을 포함하는 JDK 는 운영체제별로 다르게 설치되어야 한다.

  • JVM 의 역할을 단순하게 정리해보자면, 1. 자바 바이너리 코드를 읽고 ➡️ 2. 자바 바이너리 코드를 검증한다. ➡️ 3. 자바 바이너리 코드를 실행한다.

🧩 Java 의 메모리 관리와 가비지 컬렉션

  • JVM 은 프로그램을 실행시키기 위해서 OS 로부터 메모리 공간을 할당받는다.

  • 컴퓨터에서 메모리(RAM)는 수많은 번지들로 구성된 데이터 저장 공간이다. 프로그램은 데이터를 메모리에 저장하고 읽는 작업을 빈번히 수행한다. 이때 데이터를 어디에, 어떤 방식으로 저장할지 정해져 있지 않다면 메모리 관리가 무척 어려워진다. 프로그래밍 언어는 이 문제를 해결하기 위해 변수를 사용한다.

  • 변수는 하나의 값을 저장할 수 있는 메모리 번지에 붙여진 이름이다. 변수를 통해 프로그램은 메모리 번지에 값을 저장하고 읽을 수 있다. JVM 에서 메모리 관리를 어떻게 하는지 자세히 알아보자.

🍥 Java 의 메모리 관리

영역설명
Method 영역변수 이름, 데이터 타입, 접근 제어자 정보, 필드 정보, 메소드 이름, 리턴 타입, 파라미터, type 정보, static 변수등이 생성되는 영역이다.
Heap 영역new 키워드로 생성된 객체와 배열이 생성되는 영역이다. 사용되지 않는다고 하더라도 자동으로 사라지지 않는다.
Stack 영역메서드가 생성될 때 스택 프레임이 생성되며, 여기에는 지역 변수, 파라미터, 리턴 값, 연산에 사용되는 값 등이 생성된다. 메서드가 종료되면 자동으로 사라진다.

🔎 💬 참고로, Java 메모리의 Heap 영역과 자료구조의 Heap 은 다른 Heap 이니 조심!

public class Test {
  public static void main(String[] args) {
    // String 타입의 객체 t 선언 (지역변수 t 는 Stack 영역에 할당)
    String t = null;
    System.out.println(t); // null

    // Heap 영역에 "Hello" 라는 데이터를 할당,
    // 객체 t 에 "Hello" 가 저장된 주소를 할당
    t = "Hello";
    System.out.println(t); // @32ab41b2

    // Heap 영역에 "Bye~" 를 새로 할당하고,
    // 객체 t 에 "Hello" 의 주소가 아닌 "Bye~" 의 주소를 새로 할당
    t = "Bye~";
    System.out.println(t); // @32abd256

  }
}
  • t 라는 지역변수는 Stack 영역에 할당되고,

  • 모든 Object 타입(Integer, String, ArrayList 등)은 Heap 영역에 할당된다. Heap 영역에 있는 오브젝트를 가리키는 레퍼런스 변수들이 Stack 영역에 할당되는 것이다.

  • 위 예시와 같은 상황에서, “Hello” 는 더 이상 참조되지 않기 때문에 가비지컬렉션의 대상이 된다.

🍥 Garbage Collection (GC)

  • 특정한 인스턴스가 생성되어 메모리 공간을 차지한 상태에서 해당 인스턴스가 더 이상 사용되지 않는 상황이 되었다면 그 인스턴스는 Garbage 가 된다.

  • C 의 경우 이렇게 불필요한 메모리를 잡아먹고 있는 인스턴스를 개발자가 직접 제거해줘야 하지만, Java 의 경우 JVM 이 알아서 불필요한 메모리를 정리해준다.

  • 이것을 가비지 컬렉션 (GC) 이라 한다.

  • 기본적으로 JVM 이 알아서 정리해주지만, System.gc(); 혹은 Runtime.getRuntime().gc(); 를 호출하여 GC 를 발생시킬 수 있다.

🧩 컴파일(Compile) 과 런타임(Runtime)

(😯여러가지 공부하다보니 컴파일과 런타임의 차이도 헷갈려서 정리해본다…)

  • 컴파일은 소스코드를 기계어로 변환하는 작업.

  • 런타임은 컴파일 과정을 마친 컴퓨터 프로그램이 실행되고 있는 환경, 또는 동작되는 동안의 시간을 말한다.

🍥 궁금한 것은 에러!

# 컴파일 에러

컴파일 에러는 프로그램이 컴파일링되는 과정에서 발생하는 에러로, 일반적인 컴파일 에러 발생시 컴파일러는 문제를 일으킨 소스코드 라인을 지적해준다.

- Syntax Error (코드 문법상 문제가 있는 경우)
- Type 체크 에러 (잘못된 타입을 사용한 경우)
- 파일 참조 오류

# 런타임 에러

소스코드가 이미 실행 가능한 프로그램으로 성공적으로 컴파일이 되었더라도 여전히 프로그램 실행 중에 버그를 일으킬 수 있다. 이렇게 프로그램 실행 중에 발생하는 형태의 오류를 런타임 에러라고 한다.

- 0 나누기 오류
- Null 참조 오류
- 메모리 부족 오류

 

🔖 참고문서

JVM 정리

JVM, JDK, JRE란 무엇인가?

JRE란?

Java 의 메모리 관리와 가비지컬렉션에 대하여

컴파일과 런타임