본문 바로가기

java

[JAVA] JVM 에 대하여 ( + Garbage Collection)

 

https://howtodoinjava.com/java/basics/jdk-jre-jvm/

사이트와 책 'JVM Performance Optimizing 및 성능 분석 사례'를 함께 보았다. 

 

 

1. 왜 Java Virtual Machine 일까?

2.  자바 프로그래밍의 실행(Execution of a Java Program)  With JVM

개략적으로 살펴보면, 

 

 작성한 .java 파일을 .class 파일로 컴파일 하여 얻은 바이트 코드가 JVM 에서 실행된다. 그래서 바이트 코드로 올라온 것부터 JVM의 영역이라고 할 수 있을 것 같다.  헷갈리지 말아야 할 점은 JVM은 바이트 코드가 런타임 환경에서 실행될수 있는 정보를 제공하는 명세일뿐이다. 다양한 벤더들은 이 명세의 구현 방법들을 제공하고 있는데 아래의 사이트에서 그 리스트 자세한 내용을 다룬다.

 

https://en.wikipedia.org/wiki/List_of_Java_virtual_machines

 

List of Java virtual machines - Wikipedia

Wikipedia list article This article provides non-exhaustive lists of Java SE Java virtual machines (JVMs). It does not include every Java ME vendor. Note that Java EE runs on the standard Java SE JVM but that some vendors specialize in providing a modified

en.wikipedia.org

 

가장 대중적인 JVM 구현은 Hotspot 으로 Oracle 에서 소유하며 제공되고 있다. 

JVM 은 다양한 기술(incorporating a state-of-the-art memory model, garbage collector, and adaptive optimizer) 을 이용해서 자바 애플리케이션들을 위한 최적의 퍼포먼스들을 제공한다. 

 

 

JVM 과 실행되는 자바의 구현 과정을 그림으로 자세히 살펴보면,

Java Execution Flow 1 

1. 에디터나 IDE(Intergrated development enviroment) 를 이용해서 Simple.java 코드를 작성한다.

2. 프로그램은 바이트 코드로 컴파일 되어야 하며 Java Compiler(javac) 는 소스코드를 Simple.class 로 컴파일 한다. 

3. 컴파일된 클래스 파일은 어떠한 플랫폼이나 OS에서 JVM (Java virtual machine) 에 의해 실행 될 수 있다. 

4. JVM은 바이트 코드를 기계가 실행할 수 있는 네이티브 시스템 코드로 변환한다.

 

컴파일 과정을 통하여 생성된 Class 파일을 JVM 으로 로딩하고 ByteCode 를 해석(interpret)하는 과정을 거쳐 메모리 등의 리소스를 할당하고 관리하며 정보를 처리하는 일련의 작업들을 포괄한다. 이때 JVM은 Thread 관리 및 Garbage Collection 과 같은 메모리 정리 작업도 수행하게 된다. 

 

 

이번엔 JVM 의 내부 구조를 확대한다.

 

 

 

 

 [그림] 은 기본적인 Java 프로그램의 수행과정을 도식화 한 그림으로 JVM 상에서 Class Loader 를 통해 Class 파일들을 로딩시키고, 로딩된 Class 파일들은 Execution Engine 통해 해석된다. 해석된 프로그램은 Runtime Data Areas 에 배치되어 실질적인 수행이 이루어지게 된다. 

 

  • Java Source : 사용자가 작성한 Java 코드이다. (확장자 .java) 
  • Java Compiler : Java Source 파일을 JVM 이 해석할 수 있는 Java Byte Code 로 변경한다.
  • Java Byte Code : Java Copiler 에 의해 수행될 결과물이다. (확장자 .class 파일)
  • Execution Engine : Loding 된 클래스의 Bytecode를 해석(Interpret) 한다.
  • Runtime Data Area : JVM 이라는 프로세스가 프로그램을 수행하기 위해 OS 에서 할당받은 메모리 공간이다. 

 

Runtime Data Areas

JVM Architecture 

 

  • Method Area : 클래스, 변수, Method, static 변수, 상수 정보 등이 저장되는 영역이다. (모든 Thread 가 공유한다.)
  • Heap Area : new 명령어로 생성된 인스턴스와 객체가 저장되는 구역이다. (Garbage Collection 이슈는 이 영역에서 일어나며, 모든 Thread 가 공유한다.
  • Stack Area : Method 내에서 사용되는 값들(매개변수, 지역변수, 리턴값 등)이 저장되는 구역으로 메소드가 호출될 때 LIFO 로 하나씩 생성되고 메소드 실행이 완료되면 LIFO 로 하나씩 지워진다. (각 Thread 별로 하나씩 생성된다.
  • PC Register : CPU 의 Register 와 역할이 비슷하다. 현재 수행 중인 JVM 명령의 주소값이 저장된다. (각 Thread 별로 하나씩 생성된다. 
  • Native Method Stack : 다른 언어(C/C++ 등)의 메소드 호출을 위해 할당되는 구역으로 언어에 맞게 Stack 이 형성되는 구역이다. 

이러한 실행 과정 속에서 JVM 은 필요에 따라 Thread Synchroniztion 과 Garbage Collection 과 같은 관리 작업을 수행하게 된다. 

 

 

3. JVM Architecture 

 위 그림에서 보았던 Runtime Data Area 영역의 JVM Memory 부분이다. 

JVM Architecture 1

3.1 Java Heap 

  •  Thread 공유의 정보는 Stack에 저장이 되고 Class 나 Method 정보, Bytecode 등은 Method Area 에 저장된다.  이 Java Heap 은 단순히 Instacne(Object) 와 Array 객체 두가지 종류만 저장되는 공간에 지나지 않는다.
  • 모든 Thread 들에 의해 공유되는 영역이며 같은 애플리케이션을 사용하는 Thread 사이에서는 공유된다
  • JVM 은 Java Heap 에 Memory를 할당하는 Instruction(Bytecode로 new, newarray, anewarray, multinewarry) 만 존재하고 메모리 해제를 위한 어떤 Java Code 나 Bytecode 도 존재하지 않는다. Java Heap의 메모리 해제는 오로지 Garbage Collection을 통해서만 수행된다. 

 3.1 Hotspot JVM 의 Heap 구조 

Hotspot JVM 의 Heap 구조

  1. Young Generation : Eden 영역과 Survior 의 영역 
  2. Eden : Object 가 Heap 에 최초로 할당되는 장소, 영역이 꽉 차게 되면 Live Object 일 경우 Survior 로 넘기고, 참조가 끊어진 Garbage Object 이면 그냥 남겨 놓는다. 모든 Live Object가 Survior 영역으로 넘어가면 Eden 영역을 모두 청소(Scanvenge) 한다. 
  3. Survior  : Eden 영역에서 살아남은 Object 들이 잠시 머무르는 곳
  4. Minor GC : Survior 영역에서 이루어 지는 GC 
  5. Old Generation : Young Generation 에서 Live Object 로 오래 살아남아 성숙된 Object 를 이동시키는 영역
  6. Major GC : Young -> Old Generation 영역으로 Promotion 과정중 Old Generation 의 메모리도 충분하지 않으면 해당 영역에서 발생하는 GC 
  7. Perm : 보통 Class 의 Meta 정보, Method 의 Meta 정보, Static 변수와 상수 정보들이 저장되는 공간으로 흔히 메타 데이터 저장 영역이라고도 한다. 이 영역은 Java 8 부터는 Native 영역으로 이동하여 Metaspace 영역으로 변경 되었다. (다만, 기존 Perm 영역에 존재하던 Static Object 는 Heap 영역으로 옮겨져서 GC 의 대상이 최대한 될 수 있도록 하였다.) 

 

Java8 에서의 JVM 메모리 구조 개선 사항

 최근 자바 8 에서 JVM 메모리 구조적인 개선 사항으로 Pem 영역이 Metaspace 영역으로 전환되고 기존 Perm 영역은 사라지게 되었다.

 Metaspace 영역은 Heap 이 아닌 Native 메모리 영역으로 취급하게 된다. 

 (Heap 영역은 JVM 에 의해 관리된 영역이며 Native 메모리는 OS 레벨에서 관리 하는 영역으로 구분된다.) 

 Metaspace 가 Native 메모리를 이용함으로서 개발자는 영역 확보의 상한을 크게 의식할 필요가 없어지게 되었다.

 

[이미지 출처 : https://brunch.co.kr/@heracul/1]

 

 

 

JVM 8이상 Heap 메모리 구조 및 옵션 [출처 : https://sharplee7.tistory.com/54]

 

JDK 7 이하는 옵션 방식이 다르기 때문에 따로 확인해 보아야 한다.

Heap 의 메모리 저장 정보는 아래와 같이 차이가 난다.

 

구분 상세구분 ~ Java 7 까지의 버전(Perm)  ~ Java 8 까지의 버전(Metasapce) 
저장 정보 Class 의 Meta 정보  저장  저장 
Method 의 Meta 정보  저장  저장 
Static Object 변수, 상수  저장  Heap 영역으로 이동
관리 포인트  메모리 관리(튜닝) Heap 영역 튜닝외에 
Peram 영역 별도 튜닝 필요
Heap 영역 튜닝
Native 영역 동적 조정 
(별도 옵션으로 조절 가능) 
메모리 측면  메모리 크기(옵션) -XX:PermSize 
-XX:MaxPermSize
-XX:MetaspaceSize 
-XX:MaxMetaspaceSize

 

아래 사이트에서 Metasize 조회 방법, JDK 7, JDK 8 으로 Heap 설정이 달라진 부분이 잘 나와있다. (그 안에 링크까지) 

https://blog.naver.com/PostView.nhn?blogId=pcmola&logNo=221811360247&parentCategoryNo=&categoryNo=30&viewDate=&isShowPopularPosts=true&from=search 

 

Java Heap Memory Size 확인 및 설정하기

Java Heap Memory Size 설정하기 1. 현재 시스템의 메모리 설정값 확인 초기/최대 힙 메모리가 이렇게 ...

blog.naver.com

 

 

4. Garbage Collection 

4.1 Garbage?

 Garbage : 사용되지 않은 Object 를 의미한다. 그리고 그 오브젝트를 모아 처리하는 것이 가비지 컬렉션이다.

4.2 Object 의 사용 여부 판단 방법 

 - Root Set 과의 관계로 판단한다. (Reachable or Not Reachable)  

 Root Set 은 아래와 같이 3가지 참조형태를 통해 Reachable Object 를 판단한다. 

  • Local variable Section, Operand Stack 에 Object 의 Reference 정보가 있다면 Reachable Object! 
  • Method Area에 로딩된 클래스 중 constance pool 에 있는 Reference 정보를 토대로 Thread에서 직접 참조 하지 않지만 Constance pool을 통해 간접 link 를 하고 있는 Object는 Reachable Object!
  • 아직 Memory에 남아 있으며 Native Method Area로 넘겨진 Object 의 Reference 가 JNI 형태로 참조 관계가 되어있는 Object 는 Reachable Object 이다.

4.3 Memiry Leak 가 발생했다는 것 With GC 

 

public class Main {
    public static void main(String[] args) {
        Leak lk = new Leak();
        for(int a =0; a < 90000000; a++) {
            lk.addList(a);
            lk.removeStr(a);
        }
    }
}

public class Leak {
    ArrayList list = new ArrayList();
    public void addList(int a) {
        list.add("add  :" + a);
    }

    public void removeStr(int i) {
        Object obj = list.get(i);
        obj = null;
    }
}

 

 

 

반응형