이전 포스팅에서 Java의 컴파일 과정 및 JVM에 대해서 알아보았습니다.

 

 

[Java] Java의 컴파일 과정을 최대한 쉽게 이해해보자

도대체 .java 확장자 파일은 어떻게 우리가 원하는 대로 실행될 수 있을까요? 이것에 대한 궁금증을 갖고 Java의 컴파일 과정에 대해서 공부하게 되었습니다. 틀린 부분이 있다면 지체없이 댓글을

ssocoit.tistory.com

 

이것저것 다룬다고 직접 그림까지 그려가며 작성했는데, 각각의 메모리의 역할(특히 Heap과 Stack 영역)실행 엔진의 가비지컬렉션에 대한 부분은 간단하게 언급만 하고 넘어갔었죠?

 

이 부분에 대해 보다 자세히 알아보기 위해서 다시 한번 포스팅을 작성합니다.

 

목차

     


    0. 가비지컬렉션이란?

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

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

    이것을 바로 가비지컬렉션(이하 GC)이라고 부릅니다.

     

    기본적으로 JVM이 알아서 정리해주지만,

    System.gc();

    혹은

    Runtime.getRuntime().gc();

    를 호출하여 명시적으로 GC를 발생시킬 수 있습니다.

     

     

    System (Java Platform SE 8 )

    Sets the system property indicated by the specified key. First, if a security manager exists, its SecurityManager.checkPermission method is called with a PropertyPermission(key, "write") permission. This may result in a SecurityException being thrown. If n

    docs.oracle.com

    출처:https://docs.oracle.com/javase/8/docs/api/java/lang/System.html#gc--

     

    하지만 이 방식의 경우 개발자가 GC를 컨트롤하는 것이기 때문에 휴먼 에러가 발생할 확률이 높기 때문에 권장하지 않는 방식입니다.

     

    GC 한번에.. 약 20000배의 속도차이를 보여주는 모습...

    JVM을 믿읍시다 (?)

    public class GCTimeCheck {
        public static void main(String[] args) {
            long startTime = System.nanoTime();
            long endTime = System.nanoTime();
            System.out.println(endTime - startTime + "ns"); // 약 300ns
        }
    }
    
    public class GCTimeCheck2 {
        public static void main(String[] args) {
            long startTime = System.nanoTime();
            System.gc();
            long endTime = System.nanoTime();
            System.out.println(endTime - startTime + "ns"); // 약 7000000ns
        }
    }

     


    1. Java의 메모리 관리

    이전 포스팅에서 Heap 영역에 대해서 간단하게 인스턴스나 객체가 생성되는 경우에 Heap영역에 저장된다고 했습니다.

    그리고 Heap 영역에 할당되었던 메모리는 사용이 되지 않는다고 하더라도 자동으로 사라지지 않습니다.

    반면 Stack 영역의 경우 메서드가 생성될 때 스택 프레임이 생성됐다가 메서드가 종료되면 해당 스택 프레임이 자동으로 pop되기 때문에 GC가 필요하지 않습니다.

     

    (TMI : Java 메모리의 Heap 영역자료구조에서의 Heap은 다른 Heap이니 조심!!)

     

    1.0. Heap 영역과 Stack 영역을 활용한 관리 예시

    이해를 돕기 위해 아래 예시를 하나 들고왔습니다.

     

    public class HeapTest {
        public static void main(String[] args) {
            String t = null; // Integer 타입의 객체 t 선언 및 Stack 영역에 공간 할당
            System.out.println(t); // null
            t = "ㅎㅇ"; // Heap 영역에 "ㅎㅇ"를 할당하고, 객체 t에 "ㅎㅇ"가 저장된 주소를 할당
            System.out.println(t); // @32ab41b2
            t = "ㅂㅇ"; // Heap 영역에 "ㅂㅇ"를 할당하고, 객체 t에 "ㅎㅇ"의 주소가 아닌 "ㅂㅇ"가 저장된 주소를 할당
            System.out.println(t); // @32abd256
        }
    }

     

    t라는 지역변수의 경우 Stack 영역에 할당되고

    null, "ㅎㅇ", "ㅂㅇ" 와 같은 데이터들은 Heap 영역에 할당됩니다.

    간단히 말해서, 모든 Object 타입(Integer, String, ArrayList) 등은 Heap 영역에 생성되고, Heap 영역에 있는 오브젝트를 가리키는 레퍼런스 변수들이 Stack 영역에 할당되는 것이죠,

    (String의 상위 객체는 Object라는 것을 생각하면 쉽게 이해할 수 있습니다!)

     

    위 예시와 같은 상황에서 "ㅎㅇ"는 더이상 참조되지 않기 때문에 GC의 대상이 됩니다.

     

    출처:https://dzone.com/articles/java-memory-management

     


    2. Heap 영역의 구조

     

    GC의 등장 배경과 예시를 살펴보았으니, Heap 영역의 구조에 대해 조금 더 심도있게 살펴봅시다.

     

    JVM은 이런 Heap 영역에 저장되는 데이터 및 메모리를 효율적으로 최적화하기 위해서 여러 공간을 나눠서 사용하고 있습니다.

    Heap 영역의 경우 크게 Young Generation(이하 Young 영역)Old Generation(이하 Old 영역)으로 나뉘어집니다.

     

    2.0. Young Generation

    인스턴스를 처음 생성하면 Young 영역의 메모리가 배정됩니다.

    특히 그 중에서도 맨 처음에는 Eden 영역에 할당이 됩니다.

     

    그러다가 Eden 영역이 꽉 차서 더이상 메모리를 할당할 수 없는 상황이 되면,

    Minor GC(Minor Garbage Collection)에 의해서 Eden 영역에 있는 요소의 참조 여부를 살피고 GC를 진행합니다.

    이렇게 Young 영역에서 발생하는 GCMinor GC라고 부릅니다.

     

    그리고 한번 GC가 되어서 정리된 데이터들은 Survivor Space로 이동됩니다.

     

    그럼 여기서 궁금증이 하나 생깁니다.

    Survivor Space가 2개가 있는데, 어떤걸 쓰냐는 의문점이 생길 수 있죠.

     

    Minor GC가 끝난 데이터들은 먼저 S0 영역으로 이동합니다.

    그리고 Eden 영역S1 영역이 Clear됩니다.

    (S1영역에 뭐가 없는데 왜 Clear하는 지 궁금하시다면, S1영역도 Minor GC가 일어나긴 했는데 아직 안에 내용물이 없기 때문에 생략한 것입니다!)

     

    다시 Eden 영역을 채웁니다.

    그리고 Eden 영역이 다시 꽉 찹니다.

    이번엔 Eden 영역S0 영역에 대해서 Minor GC가 발생합니다.

    여기서도 살아남은 친구들은 S1 영역으로 복사되고, Eden 영역S0 영역이 Clear됩니다.

     

    정리하면 S0, S1 영역중에서 하나의 영역에만 데이터가 존재하게 됩니다.

    Eden 영역S0, S1 영역중 하나에 대한 Minor GC의 결과물을 Minor GC가 진행되지 않은 Survivor Space에 옮겨놓고 기존 영역들을 정리하는 방식인 것이죠!

     

    2.1. Old Generation

    반복되는 과정 속에서 S0 영역과 S1 영역을 반복적으로 이동하는 데이터가 있을 수 있습니다.

    이런 친구들은 age값이 증가하게 되는데, 특정 값 이상이 되면 Old Generation으로 데이터가 옮겨지게 됩니다.

    이렇게 Young 영역에서 Old 영역으로 데이터가 옮겨지는 것을 Promotion이라고 부릅니다.

     

    Promotion이 반복되면 Old 영역에 데이터가 쌓이게 되고, Eden 영역과 같은 방식으로 Old 영역 역시 GC를 통해 메모리에서 필요없어진 요소들을 제거하게 됩니다.

    이렇게 Old 영역에서 발생하는 GCMajor GC라고 부릅니다.

     

    그리고 Heap 전체에 대해서 GC가 동작하는 것, Major GCMinor GC가 동시에 일어나는 것을 Full GC라고 부릅니다.

     

    아주 드문 경우지만, Old 영역에서 Young 영역을 참조하는 경우가 있을 수 있습니다.

    이런 경우를 대비해서 Old 영역에는 참조 확인 용도의 카드 테이블이 존재해서, Young 영역Minor GC가 동작할 때 모든 Old 영역의 요소를 확인하는 것이 아닌 해당 카드 테이블만 확인하는 방식을 사용할 수 있습니다.

     

     


    3. Garbage Collection의 동작방식

    그렇다면 JVM은 어떤 방식으로 GC 대상을 파악하는 걸까요?

     

    적용 알고리즘의 경우 Serial GC, Parallel GC, CMS GC, G1 GC 등등 다양하지만, 기본적으로 Stop The WorldMark & Sweep에 기초를 두고 있습니다.

     

    먼저 GC를 하기 위해 Stop The World를 진행합니다.

    Marking 작업을 위해서 모든 스레드를 중단시키는데, 이것을 Stop The World라고 부릅니다.

    (GC를 튜닝하는 과정은 이 Stop The World 시간을 줄이는 것이라고 보시면 됩니다!)

     

    모든 스레드를 멈추고, 스택 내의 모든 지역 변수를 스캔하면서 각각 어떤 오브젝트를 참조하고 있는지를 찾는 과정이 바로 Marking 작업이고, 참조가 되어있지 않은 오브젝트들을 Heap에서 제거(Sweep)하게 됩니다.

    이 방식을 Mark & Sweep이라고 부릅니다.

     

    각각의 GC 알고리즘에 대해 더 자세하게 알고 싶다면 아래 사이트들을 확인하시면 됩니다!

     

     

    Java Garbage Collection Basics

    Java Overview Java is a programming language and computing platform first released by Sun Microsystems in 1995. It is the underlying technology that powers Java programs including utilities, games, and business applications. Java runs on more than 850 mill

    www.oracle.com

     

    Garbage Collection (GC) : 가비지 컬렉션

    Garbage Collection java에서는 개발자가 코드로 메모리를 명시적으로 해제하지 않기 때문에 Garbage Collector가 더이상 필요없는 객체를 찾아 지우는 작업을 한다. stop-the-world GC를 실행하기 위해 JVM이 애

    seungahyoo.tistory.com

    NAVER D2 - Java Garbage Collection

    https://d2.naver.com/helloworld/1329


    4. 정리

    가비지컬렉션(GC)Heap 영역에 있는 데이터들 중에서 불필요해진 데이터들을 자동으로 정리해주는 작업입니다.

    일반적으로 JVM이 알아서 해주지만, 개발자가 임의로 진행할 수도 있습니다.

    하지만 개발자가 직접 GC에 관여하는 방식은 권장하지 않습니다.

     

    동작 방식의 경우 Stop The World를 통해 스레드를 멈추고 Mark & Sweep 방식으로 더이상 참조되지 않는 요소들을 걸러내서 메모리에서 할당 해제를 진행하게 됩니다.

    그리고 효율적인 Heap 메모리 사용을 위해 Young 영역과 Old 영역으로 나눠서 데이터를 관리하고, 각 영역의 GC를 Minor GC, Major GC라고 부르고 두 GC를 합쳐서 Full GC라고 부릅니다.

     

    또한, JavaScript의 가비지컬렉션 동작 방식과도 굉장히 유사합니다.

    Java가 Stack 영역의 지역변수를 기준으로 marking 과정을 진행하고 참조 여부를 판단한다면, JavaScript는 root부터 root가 참조하는 모든 객체를 방문하고 mark 하는 방식으로 참조를 판단한다는 차이를 가지고 있습니다.

     


    5. 참고자료

     

    Java Garbage Collection Basics

    Java Overview Java is a programming language and computing platform first released by Sun Microsystems in 1995. It is the underlying technology that powers Java programs including utilities, games, and business applications. Java runs on more than 850 mill

    www.oracle.com

     

    자바 메모리 관리 - 가비지 컬렉션

    개요 Java 가비지 컬렉션에 대해서 공부한 내용을 정리해본다. Java 에서 메모리 관리는 어떻게 이루어지는지 이해하고 있으면 좋다. 자바 메모리 관리 - 스택 & 힙 를 먼저 읽는 것을 추천한다. 모

    yaboong.github.io

     

    [자바 기초] 3. 자바의 동작원리 : Garbage Collection

    1. Garbage Collection(GC) 개념 기본 개념은 쉽다. 어떤 인스턴스가 생성되어 메모리 공간을 차지한 상태에서 해당 인스턴스가 프로그램에서 사용되지 않게 되었다면(null 처리 되었거나 해당 인스턴스

    whitepro.tistory.com

     

    Java Platform SE 8

     

    docs.oracle.com

     

    JAVA의 GC의 종류 및 특징

    GC(Garbage Collector) 란? JAVA 개발을 하면서 Out Of Memory Error는 접할 수 밖에 없으며, 해당 에러가 운영환경에서 발생하게 되었을 때 서비스 장애로 까지 이어 질 수 있다. 때문에 메모리를 괸리해주는

    blog.ycpark.net

     

    [JAVA/자바] 메모리 구조(static, stack, heap)

     이번 글은 자바(JAVA)를 사용하는 입장에서 알아야 할 메모리 구조 및 특징에 대해서 알아...

    blog.naver.com

     

    반응형
    • 네이버 블로그 공유하기
    • 네이버 밴드에 공유하기
    • 페이스북 공유하기
    • 카카오스토리 공유하기