티스토리 뷰

JAVA

[JAVA] JVM

PeonyF 2023. 1. 14. 11:53
반응형

JVM 구조 

.java파일 → 컴파일 → .class(바이트 코드) 파일 생성 이렇게 생성된 .class파일들을 엮어서 JVM이 운영체제로부터 할당받은 메모리 영역인 Runtime Data Area로 적재하는 역할을 한다.

 

- linking

- Verify 바이트 코드 검증 기는 생성된 바이트 코드가 올바른지 여부를 검증하게 된다. 검증이 실패하면 검증 오류가 발생한다.

- Prepare 모든 정적 변수에 대해 메모리가 할당되고 기본값이 지정된다. - Resolve 모든 기호 메모리 참조가 메서드 영역의 원래 참조로 바뀐다.

- initialization: 클래스 로딩의 마지막 단계로 여기서 모든 staic 변수는 원래 값으로 지정되고 static 블록이 실행된다.

 

 

JVM 탄생배경

printf("hi"); 라는 함수를 기계어인 2진수로 작성 기계가 100 종류 있다면 언어를 100개 공부해야 하기 때문

-> 그래서 나온 것이 c언어와 같은 프로그래밍 언어 프로그램이 직접 os와 상호작용 했기 때문에 os에게 의존적 -> 하드웨어나 운영체제에 따라서 다르게 소스코드를 작성해야 했음

ex. 어떤 컴퓨터에서는 int의 크기가 4바이트인 반면에 어떤 컴퓨터에서는 아니라는 점

 

JVM 이란

- Java Virtual Machine : JVM은 OS가 ByteCode를 이해할 수 있도록 해석을 도와줌

- 쓰는 이유 :  컴파일된 Java 클래스 파일(bytecode)를 다양한 OS 환경에서 실행 가능하게 해준다.(Java와 OS 사이에서 중계자 역할)

- Java compiler는 .java 파일을 .class 라는 Java byte code로 변환 시켜 준다. 그러나 Byte Code는 기계어가 아니기 때문에 OS에서 바로 실행되지 않는다. 이때 JVM은 OS가 ByteCode를 이해할 수 있도록 해석해준다. 

 

 

JVM역할과 기능

1. 자바 코드의 컴파일과 클래스 로딩

  • JVM은 자바 코드를 컴파일하여 바이트 코드(Bytecode)로 변환합니다. 이때, 컴파일된 클래스 파일은 JVM에서 실행 가능한 형태로 변환됩니다.
  • JVM은 실행할 클래스를 찾아서 메모리에 로딩합니다. 이때, 클래스 파일의 정보를 저장하는 메타데이터(metadata)도 함께 로딩됩니다.

2. 메모리 관리

  • JVM은 자바 프로그램이 사용하는 메모리를 관리합니다. JVM은 Heap 영역과 Stack 영역으로 나뉘어져 있으며, Heap 영역은 객체를 저장하는 메모리 공간이고, Stack 영역은 메서드 호출과 관련된 데이터를 저장하는 메모리 공간입니다. JVM은 Heap 영역의 메모리를 동적으로 할당하고 해제하여 메모리를 효율적으로 사용할 수 있도록 합니다.

3. Garbage Collection

  • JVM은 더 이상 사용하지 않는 객체를 자동으로 해제하여 메모리를 회수하는 Garbage Collection 기능을 제공합니다. 이를 통해, 개발자가 명시적으로 메모리를 해제하는 번거로움을 줄일 수 있습니다.

4. 자바 코드 실행

  • JVM은 메모리에 로딩된 클래스 파일의 바이트 코드를 해석하여 실행합니다. 이때, 바이트 코드는 기계어보다는 느리지만, 플랫폼 독립적인 형태이므로, 자바 프로그램이 여러 플랫폼에서 동일하게 동작할 수 있습니다.
  1. 예외 처리
  • JVM은 자바 프로그램이 실행 중에 발생하는 예외(Exception)를 처리합니다. 예외 처리를 통해, 프로그램이 비정상적으로 종료되는 것을 방지할 수 있습니다.

 

JVM 구조 

Method Area

- 클래스 파일의 프로그램의 흐름을 구성하는 바이트 코드가 로드되는 곳이다.

- 클래스의 *.class 파일을 읽고 분석하여, 그 클래스에 대한 정보를 저장

- JVM 구동 시작시에 생성이 되면, 종료시까지 유지되는 공통 영역 

-> JVM의 다른 메모리 영역에서 해당 정보에 대한 요청이 올 시에 실제 물리 메모리 주소로 변환해서 전달

- 클래스 멤버 변수의 이름, 데이터 타입, 접근 제어자 정보 같은 필드 정보와 메서드의 이름, 리턴 타입, 파라미터, 접근 제어자 정보 같은 메서드 정보, Type정보(Interface인지 class인지), Constant Pool(상수 풀 : 문자 상수, 타입, 필드, 객체 참조가 저장됨), final class 변수 등이 이 영역에 생성된다.

- '클래스이름.클래스변수'와 같은 형식으로 사용한다. 예를 들어 Add 클래스의 클래스 변수 cv가 있다고 가정하면, 'Add.cv'와 같이 사용

-새로운 Object를 만들 때, Object의 method를 호출할 때 Method Area에서 필요한 값을 가져온다. ClassLoader가 새로운 class 를 로드해올 때, Method Area 에 Class 정보를 저장한다.

 

Stack Area

- 스택영역에는 지역변수(로컬변수)와 매개변수(파라미터)가 저장된다.

  •   프로그램의 실행과정에서 '임시로 할당'되고 그게 끝나면 바로 소멸되는 것이 저장된다.

- Method가 호출되면 Method와 Method 정보는 Stack에 쌓기에 되며, Method 호출이 종료될 때 Stack point에서 제거된다.

- Method 정보는 해당 Method의 매개변수, 지역변수, 임시변수 그리고 어드레스(메소드 호출한 주소) 등을 저장하고 Method 종료 시 메모리 공간이 사라진다.

  1.  호출스택은 메서드가 호출될 시 할당되는 영역에 메서드의 작업에 필요한 메모리 공간을 제공
  2. 메서드 호출 시마다 호출스택에 호출된 메서드를 위한 각각의 스택프레임(그 메서드만을 위한 공간) 메모리가 할당 및 생성
  3. 이 메모리는 메서드가 작업을 수행하는 동안 지역변수(매개변수 포함)들과 연산의 중간결과 등을 저장하는데 사용된다.
  4. 메서드가 작업을 마치면 할당되었던 메모리 공간은 반환되어 비어진다.

- 각 Thread 별로 따로 할당되는 영역 - 장점: Heap 메모리 영역보다 비교적 빠르다, 각각의 Thread 별로 메모리를 따로 할당하기 때문에 동시성 문제에서 자유로움 - 특징: 한 Method 를 호출할 때마다 새로운 Frame을 생성한다. Method가 완료되면, Frame은 지워진다

 

 

  1.  각 Thread 들은 메소드를 호출할 때마다 Frame 이라는 단위를 추가(push)
  2.  메소드가 마무리되며 결과를 반환하면 해당 Frame 은 Stack 으로부터 제거(pop)
  3.  이렇게 구성된 Java Stack 은 메소드가 호출될 때마다 Frame 이 쌓임

 

Stack Area - operandStack

- 메서드의 실제 작업 공간. Operand Stack과 Local Variable Array 사이에서 데이터를 교환하고 다른 메서드 호출 결과를 추가하거나(push) 꺼낸다(pop).

 

Heap Area

  • - 인스턴스는 소멸 방법과 소멸 시점이 지역변수와 다르기 때문에 힙이라는 별도의 영역에 할당된다.
  • - new 연산자로 생성된 객체와 배열을 저장하는 공간(인스턴스 변수)
  • - 클래스 영역에 로드된 클래스만 생성가능
  • - 스택영역에 저장되는 로컬변수, 매개변수와 달리 힙영역에서 보관되는 메모리는 메소드 호출이 끝나도 사라지지 않고 유지된다.
  • - Garbage Collector를 통해 메모리 반환
  • - 메서드가 실행되면서 Stack영역에는 참조값만을 저장해놓고 Heap Area에 객체 데이터를 저장해 놓는다.

Heap Area - GC

- Heap 에서는 참조되지 않는 인스턴스와 배열에 대한 정보 또한 얻을 수 있기 때문에 GC 의 주 대상이기도 함. 이때, 인스턴스가 생성된 후 시간에 따라서 다음과 같이 5가지 부분으로 나눌 수가 있음.

 

1. Permanent Generation

  • Perm 영역은 보통 Class Meta 정보나 Method의 메타 정보, static 변수와 상수 정보들이 저장되는 공간으로 흔히 메타데이터 저장 영역이라고 한다.
  • 이 영역은 Java8부터 Native Memory 영역으로 이동하였다. (기존의 Perm 영역에 존재하는 static object는 Heap 영역으로 옮겨졌다.)

2. New/Young 영역

  •  Eden : Object(객체)가 최초로 Heap에 할당되는 장소이다. 만일 Eden 영역이 가득 찼다면, Object의 참조 여부를 파악하고 Live Object는 Survivor 영역으로 넘긴다. 그리고 참조가 사라진 Garbage Object이면 남겨 놓는다. 그리고 모든 Live Ojbect가 Survivor 영역으로 넘어갔다면 Eden 영역을 모두 청소한다.
  • Survivor 0/1 : Survivor 영역은 Survivor0과 Survivor1로 구성되며 Eden 영역에 살아남은 Object들이 잠시 머무르는 곳이며 Live Object들은 하나의 Subject 영역만 사용하게 되며 이러한 전반적인 과정을 Minor GC라고 한다.
  • Old 영역 : Old 영역은 새로 Heap에 할당된 Object가 들어오는 것이 아닌, Survivor 영역에서 살아남아 오랫동안 참조되었고 앞으로도 사용될 확률이 높은 Object들을 저장하는 영역이다. 이러한 Promotion 과정 중 Old 영역의 메모리가 충분하지 않으면 해당 영역에서 GC가 발생하는데 이를 Major GC라고 한다. (Tenured 영역에서 발생한 GC)

3. Minor Garbage Collection

  1. 새로운 객체를 eden 영역에 할당하기 시작
  2. eden 영역이 꽉 차게 되면 survivor1 영역에 옮김
  3. eden영역을 비우고 survivor1 영역에서 사용하지 않는 객체를 삭제(가비지 컬렉션).
  4. 다시 eden 영역에서부터 객체를 할당하기 시작
  5. 위의 과정을 반복
  6. survivor1 영역이 꽉 차게 되면 survivor2 영역에 이를 복사하여 옮긴 뒤 survivor1 영역을 비움
  7. survivor2 영역에서 사용하지 않는 객체를 삭제합니다(가비지 컬렉션).
  8. 다시 eden 영역에서부터 객체를 할당합니다.
  9. eden 영역이 꽉 차게 되면 이를 survivor2 영역으로 옮긴 뒤 gc진행
  10. 이때부턴 survivor2 영역이 꽉 차기 전까지 위의 과정을 반복 => survivor1과 survivor2 영역은 항상 둘 중 한 영역은 비워진 상태로 존재함
  11. survivor2 영역이 꽉 차게 되면 이를 old 영역으로 옮기고 맨 처음 과정에서부터 다시 시작!

4. Major Garbage Collection (Full Garbage Collection)

- Old영역에 있는 모든 객체들을 검사 - 참조되지 않은 객체들을 한꺼번에 삭제

- Minor Garbage Collection에 비해 시간이 오래 걸리고 실행 중 프로세스가 정지

  • → 또 다른 특징은 GC가 수행되는 동안 GC를 수행하는 스레드가 아닌 다른 모든 쓰레드가 일시 정지된다. 
  • → 특히 Full GC가 일어나서 수 초간 모든 스레드가 정지한다면 장애로 이어지는 치명적인 문제가 생길 수 있음

GC가 필요한 이유?

프로그램에서 더 이상 사용하지 않는 객체를 자동으로 해제하여, 메모리를 회수하는 기능

자동 메모리 관리

  • GC는 개발자가 명시적으로 메모리를 해제하는 번거로움을 줄이고, 메모리 누수(memory leak)를 방지하여 프로그램의 안정성과 성능을 향상시킵니다.

효율적인 메모리 사용

  • GC는 더 이상 사용하지 않는 객체를 해제하여, 메모리를 효율적으로 사용할 수 있도록 합니다.

플랫폼 독립성

  • GC는 자바의 플랫폼 독립성을 지원하는 중요한 요소입니다. GC는 자바 프로그램이 동일한 동작을 보장하면서, 다양한 플랫폼에서 실행될 수 있도록 합니다.

 

어떤 변수의 값이 null이 되었다면, 이 값은 GC가 될 가능성이 있을까요?

값이 null인 변수는 해당 변수가 참조하던 객체를 더 이상 참조하지 않기 때문에, GC의 대상이 될 수 있습니다. 하지만, 값이 null인 변수가 GC의 대상이 될 수 있는지 여부는 해당 변수를 참조하는 다른 변수나 객체의 참조 상황에 따라 달라질 수 있습니다. GC가 수행되는 시점에서 해당 변수가 더 이상 참조되지 않는다면, GC의 대상이 될 수 있습니다.

 

finalize() 를 수동으로 호출하는 것은 왜 문제가 될 수 있을까요?

finalize() 메서드는 객체가 GC(Garbage Collector)에 의해 수집되기 직전에 호출되는 메서드입니다. 이 메서드를 수동으로 호출하는 것은 권장되지 않습니다.

첫째로, finalize() 메서드의 호출 시점은 불확실합니다. finalize() 메서드는 GC에 의해 호출되므로, GC의 작업 스케줄링에 따라 호출 시점이 결정됩니다. 따라서, finalize() 메서드를 명시적으로 호출해도 객체가 즉시 제거되지 않을 수 있습니다.

둘째로, finalize() 메서드는 객체 해제 과정에서 마지막에 호출되는 메서드이기 때문에, 이 메서드 내부에서는 다른 객체나 자원을 참조하거나 변경하는 작업을 수행하지 않는 것이 좋습니다. 만약 finalize() 메서드에서 자원을 참조하거나 변경하는 작업을 수행한다면, 예기치 않은 결과가 발생할 수 있습니다.

따라서, finalize() 메서드를 수동으로 호출하는 것은 권장되지 않습니다. 대신, 객체가 GC에 의해 자동으로 수집될 때 finalize() 메서드가 호출되도록 구현하는 것이 좋습니다. 또한, finalize() 메서드를 사용하여 자원 해제를 수행하는 것보다는, try-finally 블록 등을 사용하여 명시적으로 자원을 해제하는 것이 좋습니다.

 

Native Method Stack 

  • JAVA가 아닌 Native 언어(C/C++)을 실행하기 위해 사용되는 공간
  • 자바 외의 다른 언어에서 제공되는 메서드들이 저장되는 공간
  • JNI(Java Native Interface)를 통해 호출되는 C/C++ 등의 코드를 수행하기 위한 Stack
  •  다른 프로그래밍 언어로 작성된 메소드들(순수하게 Java 로 구성된 코드만을 사용할 수 없는 시스템의 자원이나 API)을 다루는 영역

PC Register 

  • JVM이 현재 실행할 명령어의 주소값을 저장하는 공간 즉 코드의 몇번째 줄을 실행하면 되는지 저장하는 공간 PC Register는 각 Thread 별로 하나씩 존재하며 현재 수행중인 Java Virtual Machine의 주소를 가지게 된다.  Thread가 생성 될 때마다 생성되는 공간   -> Thread 는 각자의 메소드를 실행하게 됨
  • 최근에 실행 중인 JVM 에서는 명령어 주소값을 저장할 공간이 필요함  ->PC Register에서 관리하여 추적
  • Thread가 어떤 부분을 어떤 명령으로 실행할 지에 대한 기록
  •  현재 스레드가 실행되는 부분의 주소와 명령을 저장하고 있는 영역 -> 이것을 이용해서 스레드를 돌아가면서 수행할 수 있게 한다

 

 

정리

 

  • class =클래스 파일의 프로그램의 흐름을 구성하는 바이트 코드가 로드되는 곳이다. JVM이 실행 될 때 저장된다.  JVM이 종료되면 삭제된다.
  • heap =  객체가 읽히면 활성화되는 영역. 활성화 되어, 그 객체의 실제 정보(class 내용)가 저장된다. 만약 객체가 사용되지 않는다면 이 영역은 gc로 인해 삭제된다.
  • stack = 메소드가 실행되면 저장되는 영역. 메소드 실행이 종료되면 삭제된다.

 

JVM을 사용함으로써 얻을 수 있는 장점과 단점은 무엇인가요?

장점

플랫폼 독립성

  • JVM은 운영체제나 하드웨어와 상관없이 동일한 바이트 코드를 실행할 수 있기 때문에, 자바 프로그램의 플랫폼 독립성을 보장합니다.

메모리 관리

  • JVM은 Garbage Collection 기능을 제공하여, 개발자가 명시적으로 메모리를 해제하는 번거로움을 줄이고, 메모리 누수를 방지하여 프로그램의 안정성과 성능을 향상시킵니다.

예외 처리

  • JVM은 자바 프로그램이 실행 중에 발생하는 예외를 처리하여, 비정상적으로 종료되는 것을 방지할 수 있습니다.

다양한 라이브러리

  • JVM은 다양한 자바 라이브러리를 사용할 수 있도록 지원하여, 개발 시간을 단축하고, 코드의 품질을 향상시킬 수 있습니다.

단점

성능

  • JVM은 바이트 코드를 해석하고 실행하는 과정에서 성능 손실이 발생할 수 있습니다. 하지만, 최근의 JVM은 JIT(Just-In-Time) 컴파일러와 같은 기술을 사용하여, 성능을 개선하는 노력이 이루어지고 있습니다.

메모리 사용량

  • JVM은 프로그램 실행에 필요한 메모리를 동적으로 할당하고 해제하기 때문에, 일부 시스템에서는 메모리 사용량이 부담스러울 수 있습니다.

 

그럼, 자바 말고 다른 언어는 JVM 위에 올릴 수 없나요?

자바 이외의 언어는 JVM 위에 올릴 수 있습니다. 예를 들어, Kotlin, Groovy, Scala 등의 언어는 모두 JVM 위에서 실행될 수 있습니다.

C#은 .NET Framework나 .NET Core를 사용하여 CLR(Common Language Runtime)에서 실행됩니다. .NET Framework나 .NET Core는 JVM과 유사한 역할을 수행하는 런타임 환경으로, C# 코드를 MSIL(Microsoft Intermediate Language)이라는 중간 언어로 컴파일하고, CLR에서 실행합니다.

C++은 JVM이나 CLR과 같은 가상 머신을 사용하지 않습니다. C++ 컴파일러는 소스 코드를 기계어로 바로 컴파일하여 실행 가능한 파일을 생성합니다. 따라서, C++ 코드를 직접 JVM이나 CLR에서 실행하는 것은 불가능합니다.

그러나, C#이나 C++ 코드를 Java 바이트 코드로 변환하는 도구나, Java 코드를 C#이나 C++로 변환하는 도구가 존재하기도 합니다. 이를 통해, 다른 언어에서 작성된 코드를 JVM이나 CLR에서 실행할 수 있는 경우도 있습니다. 하지만, 이러한 변환 과정에서 성능 저하나 호환성 문제가 발생할 수 있습니다.

 

JVM과 내부에서 실행되고 있는 프로그램은 부모 프로세스 - 자식 프로세스 관계를 갖고 있다고 봐도 무방한가요?

JVM은 운영체제 위에서 실행되는 프로그램이며, 내부에서는 스레드(thread)라는 개념을 사용하여 동시에 여러 작업을 처리할 수 있습니다. 따라서, 부모 프로세스 - 자식 프로세스 관계와는 다릅니다.

스레드는 프로세스 내부에서 실행되는 실행 단위로, 한 프로세스 내에서 여러 스레드를 생성하여 동시에 여러 작업을 처리할 수 있습니다. 이러한 스레드는 자원 공유를 통해 프로세스 내부의 다른 스레드와 통신하며, 이를 통해 효율적인 작업 처리가 가능해집니다.

따라서, JVM과 내부에서 실행되는 프로그램은 부모 프로세스 - 자식 프로세스 관계를 갖는 것이 아니라, 단일 프로세스 내에서 스레드를 통해 동시에 작업을 처리하는 구조입니다.

 

출처 : https://gptjs409.github.io/java/2019/09/04/jvm.html https://velog.io/@chldntjr0425/JVM-%EA%B5%AC%EC%A1%B0-%EB%B0%8F-%EC%9B%90%EB%A6%AC https://technote-mezza.tistory.com/72 https://kotlinworld.com/3 https://mattlee.tistory.com/16 https://tecoble.techcourse.co.kr/post/2021-08-09-jvm-memory/ https://velog.io/@jsj3282/JVM%EA%B3%BC-%EC%9E%90%EB%B0%94-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B5%AC%EC%A1%B0Runtime-Data-Area https://hanna97.tistory.com/entry/Method-Area%EC%99%80-JVM-Stack-Heap-%EB%A9%94%EB%AA%A8%EB%A6%AC%EC%9D%98-%EA%B5%AC%EC%A1%B0%EC%99%80-%EC%9A%A9%EB%8F%84 https://memostack.tistory.com/227 https://tgio.tistory.com/36 https://blog.naver.com/force44/130095188664 https://beststar-1.tistory.com/14 https://johngrib.github.io/wiki/jvm-stack/#fn:2-6-3 https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=2000yujin&logNo=130156226754 https://jeong-pro.tistory.com/148 https://www.devkuma.com/docs/jvm/memory-structure/ http://m.egloos.zum.com/newkong/v/4096029 https://velog.io/@hagyoung99/JavaMemory https://linuxism.ustd.ip.or.kr/2 https://blog.naver.com/force44/130095188664 https://gptjs409.github.io/java/2019/09/04/jvm.html https://ict-nroo.tistory.com/19 http://huelet.tistory.com/entry/JVM-%EB%A9%94%EB%AA%A8%EB%A6%AC%EA%B5%AC%EC%A1%B0 https://gmlwjd9405.github.io/2018/09/14/process-vs-thread.html https://jwprogramming.tistory.com/16 https://velog.io/@junttang/SP-4.3-Thread-based-Concurrent-Server

반응형

'JAVA' 카테고리의 다른 글

[JAVA] 람다  (1) 2023.01.30
자바 문자열 파싱(parsing) : StringTokenizer  (0) 2023.01.27
[JAVA] 객체지향설계 5원칙 - SOLID  (0) 2022.11.07
[JAVA] Stream  (0) 2022.09.14
ArrayList 정렬 : Comparable 과 Comparator  (0) 2020.01.06
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함