GC(Garbage Collector)

GC는 아무도 참조하지 않는 객체의 메모리를 해제해주는 기능(가비지 컬렉션)을 한다.

GC 알고리즘 2가지

Reference Counting

  • 각각의 객체는 Reference Counting 을 하며 Reference Count가 0인 객체를 가비지 컬렉션한다.
  • 단점: 순환 참조 문제

Mark And Sweep

  • root 영역에서부터 참조값을 확인하여 참조하지 않는 객체를 가비지 컬렉션한다.
  • 장점: 순환 참조 문제 해결 가능
  • 단점: 애플리케이션과 GC 실행이 병행된다. / 의도적으로 GC를 실행시켜야 한다.
    (가비지 컬렉션의 주체는 GC이므로 의도적으로 GC를 실행시키는건 레퍼런스 카운트도 마찬가지인듯)

JVM 메모리 구조

모든 쓰레드가 공유하는 영역

- 메서드, 힙 영역

 

각 쓰레드마다 고유하게 생성하며 쓰레드 종료 시 소멸하는 영역

 

- 스택, PC Register, Native Method Stack 영역

 

Method 영역

  • 메서드들의 코드를 저장
  • static 변수 등이 속함

Heap 영역

  • new 인스턴스가 속함

Stack 영역

  • 로컬 변수, 중간 연산 결과 등이 속함

Mark And Sweep 알고리즘이 참조값을 확인하는 root 영역

  • 메서드 영역의 static 변수
  • stack 영역의 로컬 변수
  • native method stack 영역의 저수준 언어의 객체

GC 동작 순서

 

자바 8기준의 GC 동작 순서이며, 위의 그림을 참고하자.

 

1. Heap 영역에 위와 같은 구조의 단위 영역을 만든다.

2. 객체 생성 시 Eden 영역에 추가되고 Eden 영역이 다 차면 GC가 일어난다.
(Minor GC)

3. GC 후 남은 객체들은 age bit가 1 증가하고 survival 영역으로 이동한다.
(survival 영역 0, 1 중 둘 중 하나는 비어있고, Minor GC 마다 둘 중의 한 영역으로 이동 시킨다.)

Eden이 처음 다 찼을 때(1번째 Minor GC)
Eden이 두번째로 다 찼을 때(2번째 Minor GC)

4. 위와 같은 과정을 반복하고, age bit가 특정 수치에 도달하면 Old Generation 영역으로 이동한다.
(Promotion 이라고 함)

5. Old Generation 영역이 다 차면 GC가 일어난다.(Major GC)


GC의 종류

여러 종류가 있지만 Pararell, G1 만 간단하게 소개하겠다.

Pararell

자바 8의 default GC 이며, 여러 개의 쓰레드로 가비지 컬렉션을 수행하여 Stop-The-World가 짧다. 

G1

자바 9부터의 default GC 이며

GC 동작 순서는 비슷하지만 힙 영역에 단위 영역 할당 방식에 차이가 있다.

G1 GC는 아래 그림처럼 힙 영역에 Region 이라는 단위로 나누어 메모리를 할당한다.

이 영역들이 Eden, Survival, Old Generation 의 역할을 한다.

기존 GC에서는 힙 영역에 Young, Old Generation 을 포함한 구조의 단위 영역을 만들었다.

따라서 이 단위 영역에서 사용하지 않는 메모리들은 낭비가 된다.(메모리 파편화)

G1 에서는 아래처럼 Region으로 관리하여 메모리 파편화를 줄인다.

 

 

참고자료)

테코톡 GC영상

'언어 > Java' 카테고리의 다른 글

Builder 패턴을 사용해야하는 이유  (0) 2023.09.23

Builder 패턴이란?

빌더 패턴은 생성과 관련된 디자인 패턴 중 하나로, 생성과 관련된 문제를 해결하고자 고안된 패턴이다. 

객체를 생성할 때 생성자 패턴, 정적 메소드 패턴, 수정자 패턴, 빌더 패턴 등을 사용할 수 있다.

 


빌더 패턴(Builder Pattern)을 사용해야 하는 이유

@Getter
@ToString
@NoArgsConstructor
public class PostCreate {

    @NotBlank(message = "제목을 입력해주세요.")
    private String title;

    @NotBlank(message = "내용을 입력해주세요.")
    private String content;
    
    public PostCreate(String title, String content) {
        this.title = title;
        this.content = content;
    }
}

 

객체에서 생성자가 필요할 때 보통 이렇게 코드를 작성하곤 하지만 이는 잠재적 버그를 내포하는 코드이다.

위와 같이 매개변수로 받는 두 필드 값의 타입이 같을 때 만약 순서가 달라지면 어떻게 될까?

누군가 실수로 이 생성자를 이용해 content, title 순으로 매개변수를 입력해도 개발자는 쉽게 발견하기 어렵다.

이런 코드들은 나중에 코드 길이가 길어지면 쉽게 발견하기 어려운 버그가 되곤 한다.

 

[ 빌더 패턴(Builder Pattern)의 장점 ]

  1.  필요한 데이터만 설정할 수 있다.
  2.  가독성을 높일 수 있다.
  3.  불변성을 보장할 수 있다.

 

1. 필요한 데이터만 설정할 수 있다.

@Getter
@ToString
@NoArgsConstructor
public class PostCreate {

    @NotBlank(message = "제목을 입력해주세요.")
    private String title;

    @NotBlank(message = "내용을 입력해주세요.")
    private String content;

    @Builder
    public PostCreate(String title, String content) {
        this.title = title;
        this.content = content;
    }
}
PostCreate request = PostCreate.builder()
            .content("글 내용입니다 하하")
            .build();

PostCreate request = PostCreate.builder()
            .title("글 제목입니다")
            .content("글 내용입니다 하하")
            .build();

빌더 패턴을 사용하면 위처럼 원하는 필드에 값을 넣을 수도 있고 필요없으면 해당 필드를 입력하지 않을 수도 있다.

 

2. 가독성을 높일 수 있다.

PostCreate postCreate = new PostCreate("글 내용입니다 하하", "글 제목입니다");

PostCreate request = PostCreate.builder()
            .title("글 제목입니다")
            .content("글 내용입니다 하하")
            .build();            
                      
PostCreate request = PostCreate.builder()
            .content("글 내용입니다 하하")
            .title("글 제목입니다")            
            .build();

맨 위처럼 생성자 패턴을 사용하면 내용과 제목의 순서를 반대로 입력하는 문제가 생길 수 있다.

이로 인해 버그가 발생한다면 이런 버그는 찾아내기가 정말 힘들다.

하지만 빌더 패턴을 사용하면 두번째와 세번째 경우처럼 순서를 반대로 쓰더라도

입력하는 필드값이 명확하므로 문제가 없다.

요즘엔 IDE 가 필드에 해당하는 힌트를 주기는 하지만 입력할 변수가 많아지면 그마저도 효용이 떨어진다.

특히나 혼자 개발하는게 아닌 협업을 하는 상황에서는 이런 가독성의 차이가 극명하게 느껴진다.

 

3. 불변성을 보장할 수 있다.

@Getter
@ToString
@NoArgsConstructor
public class PostCreate {

    @NotBlank(message = "제목을 입력해주세요.")
    private String title;

    @NotBlank(message = "내용을 입력해주세요.")
    private String content;

    @Builder
    public PostCreate(String title, String content) {
        this.title = title;
        this.content = content;
    }
}

위처럼 Setter 를 닫아놓고 생성자를 통해서만 객체에 접근이 가능하게 만들면 객체의 불변성을 보장할 수 있다.

불변성을 보장하는건 생성자 패턴과도 공통된 장점이다.

 


결론

객체를 생성하는 대부분의 경우에는 빌더 패턴을 적용하는것이 바람직하다.

하지만 빌더의 남용은 오히려 코드 길이를 비대하게 할 수 있으므로 변경 가능성 및 변수의 개수를 보고 판단하자.

변경 가능성이 전혀 없고 변수의 개수가 적다면 굳이 사용하지 않아도 된다.

 

 

'언어 > Java' 카테고리의 다른 글

GC와 JVM 메모리 영역  (0) 2024.02.14

+ Recent posts