앞선 글에서, GC 는 프로그램 동적으로 할당했던 메모리 영역 중에서 필요없게 된 영역 해제 하는 메모리 관리 기법 이라고 하였습니다.

 

또한 Java에서 GC 는 JVM 의 가비지컬렉터 가 동작한다고 하였는데요,

 

이번 글에서는 Java GC 에 대해서 조금 더 알아보려고 합니다.

 

* Heap영역 : 자바에서 객체를 생성하면 Heap 영역에 저장

Minor GC & Major GC

JVM 은 Heap영역을 설계할 때 2가지 전제조건으로 설계 되었습니다.

1. 대부분의 객체가 금방 접근 불가능한 상태가 된다.

2. 오래된 객체에서 새로운 객체로의 참조는 드물게 존재한다.

 

이는 대부분의 객체가 일회성인 경우가 많고, 메모리에 오래 남아있는 경우가 드물다는 것을 의미합니다.

 

이에 따라 Young영역 , Old 영역 으로 구분을 하여 설계하였습니다.

 

Young 영역 : 새로운 객체의 영역

- 새로운 객체는 이곳에 할당되며, 대부분의 객체가 일회성인 만큼 대부분의 객체는 Young 영역에 오래 남아있지 않고 사라집니다.

- Young 영역을 관리하는 GC 를 Minor GC 라고 합니다.

또한, Young 영역의 구조는 아래와 같이 이루어져 있습니다.

- Eden 영역 : 새로 생성된 객체가 할당되는 영역

Eden 영역이 가득찰때마다, MinorGC 실행

- Survivor1 영역 : Eden 영역에서 1번 이상 살아남은 경우 할당되는 영역

- Survivor2 영역 : Survivor1 영역이 가득찬 경우 Survivor1 영역에서 살아남는 객체가 할당되는 영역

Survivor2 영역에서 계속 해서 살아남으면, 해당영역의 객체들은 Old 영역으로 복사

 

Old 영역 : 오래된 객체의 영역

- 새로운 객체가 Young 영역에서 사라질 때, Minor GC 를 거쳤지만, 해제되지 않고 살아남은 객체들이 이곳으로 복사됩니다.

- Old 영역에서도 언젠가는 사라져야하는데 이러한 Old 영역을 관리하는 GC 를 Major GC 라고 합니다.

- Old 영역은 Young 영역보다 크며, MinorGC 에 비해 MajorGC는 10배 이상의 시간을 필요로 합니다.

- 이는 MajorGC 가 자주 호출된다면, 성능에 영향을 끼칩니다.

- 그에 따라 MajorGC 는 Old 영역의 메모리가 부족해질 때 호출됩니다.

Card Table 

위에서 오래된 객체에서 새로운 객체로의 참조는 드물게 존재한다고 하였습니다.

즉, 오래된 객체가 새로운 객체를 참조할 수도 있다. 라는 것을 의미합니다.

 

그렇다면,

Minor GC 는 Young 영역에서 해제되어야하는 대상을 찾아야할 때, Old 영역도 확인해야 겠네? 라는 결론이 나오게 됩니다.

하지만 문제가 하나 있습니다. Old 영역에 있는 객체가 Young 영역에 있는 객체를 참조하는 경우는 드물게 발생한다 에서

Old 영역중에 0개 or 1개 의 객체만 Young 영역의 객체를 참조할 수 있다는 것입니다.

 

n 개의 객체를 제거대상인지 식별하기 위하여 m개의 객체를 검사한다면 n*m 번의 검사가 실행됩니다.

이러한 문제를 해결하기 위하여 Card Table 이 등장합니다.

 

Card Table : Old 영역의 객체가 Young 영역의 객체를 참조하는 경우에 그에 대한 정보를 저장합니다.

- Minor GC 가 동작할때 Young 영역에 있는 객체들은 Old 영역에 있는 객체와의 n*m 번의 검사가 아니라,

Card Table 에 있는 객체를 확인하여 Young 영역의 객체중 GC 의 대상에서 벗어나야 하는 객체를 식별합니다.

 

기본적인 GC 공통 동작 : Stop The World & Mark And Sweep

MinorGC 와 MajorGC 는 세부적으로 동작방식이 다릅니다.

간단하게 MinorGC 에서는 CardTable 도 사용하는 로직도 필요하다는 것을 예로 들 수 있겠죠?

 

하지만 기본적으로 GC 가 실행될 때 공통적으로 동작하는 방식이 있습니다.

 

1. Stop The World

- JVM 의 가비지컬렉터가 GC 를 실행하기 위하여 GC 를 실행하는 쓰레드를 제외한 나머지 쓰레드를 모두 정지시킵니다.

GC 작업이 종료되면, 나머지 쓰레드를 다시 동작합니다.

즉, Stop The World란 JVM 이 GC 를 실행시키기 위하여 다른 쓰레드를 정지&재개하는 동작입니다.

 

2. Mark And Sweep

- GC 가 동작할 때, 사용되는 메모리와 사용도지 않는 메모리를 식별하는 Mark 작업과 사용도지 않는 메모리가 식별된 경우, 이를 해제하기 위한 Sweep 작업입니다.

 

 

 

요약

자바의 GC 는 MinorGC 와 MajorGC 로 나뉩니다.

MinorGC : Young 영역에 대한 GC, Young 영역의 구조중 Eden영역이 가득차면 실행됨. 실행속도가 빠르다.

MajorGC : Old 영역에 대한 GC, Old 영역이 가득차면 실행됨. 실행속도가 느리다.

 

* 객체의 잘못된 사용으로 GC 의 대상이 되지 못하여 메모리 누수가 일어나는 경우 Old영역에 할당되는 객체가 늘어나게 되고,

Old영역이 가득찰때마다 MajorGC 가 실행되는데, MajorGC 에서 해제되는 메모리가 적어지는 만큼 Old영역이 가득차는 경우가 빈번해집니다. 따라서 MajorGC 가 자주 호출되게 되고, 이는 곧 성능에 치명적으로 다가옵니다.

그 이후, Old영역이 가득 찾지만, 더이상 MajorGC 의 대상에 속하지 못하여 사용할 수 있는 메모리가 없어진 순간

펑~ 펑~ 펑 .....

 

메모리누수를 조심합시당 :)

'BackUp (관리중지) > CS 학습' 카테고리의 다른 글

DI (Dependency Injection )  (0) 2021.04.29
REST API  (0) 2021.04.28
GC ( Garbage Collection )  (0) 2021.04.28
동시성 이슈  (0) 2021.04.27
쓰레드(Thread) / 프로세스(Process)  (0) 2021.04.27

GC란,

Java 를 경험한 사람들은 가비지컬렉션, 가비지컬렉션 하는 말들을 못들어봤을리가 없습니다.

그러나, Java 로 개발하는 사람들에게 가비지컬렉션이 뭔가요? 라는 질문을 하면 흠칫 하는 경우가 많습니다.

알고 있는데, 설명을 하지 못하는 것은 모른다고 보는게 맞습니다.

 

오늘은 이렇게 모르면 안되는, 하지만 모르는 경우가 많은

가비지 컬렉션(GC) 에 대하서 알아보려고 합니다.

 

GC (Garbage Collection) 란,

프로그램동적으로 할당했던 메모리 영역 중에서 필요없게 된 영역해제 하는 메모리 관리 기법입니다.

 

" 더이상 사용되지 않는 메모리를 제거합니다. "
" 참조되지 않는 메모리를 해제합니다."

" 자동으로 메모리를 관리해줍니다. "

와 같은 답변은 맞는듯 아닌듯 저 말만 듣고는 이해하기 쉽지 않습니다.

 

앞으로 GC 가 무엇인가요? 에 대한 답은 

프로그램 동적으로 할당했던 메모리 영역 중에서 필요없게 된 영역 해제 하는 메모리 관리 기법입니다.

로 했으면 합니다.

 

우리가 모르고 넘어가던 핵심포인트는 GC 는 메모리 관리 기법 중 하나 라는 것입니다.


GC 는 어쩌다 탄생했나?

기본적으로 C 와 C++ 등의 언어에서 사용되지 않는 메모리 영역을 해제하기 위해 수동으로 메모리를 관리하였습니다.

Lisp 라는 언어에서 이같은 수동 메모리 관리를 단순화하기 위하여 GC 가 발명되었습니다.

역사공부는 적당히 하고.... 결국

 

"수동 메모리 관리" -> "자동 메모리 관리" 를 하기 위해 탄생했다고 보면 됩니다.

 

GC 장단점

GC 를 기본적으로 제공하는 언어나, GC를 구현한 프로그램 에서는 다음과 같은 장단점이 존재합니다.

<장점>

아래와 같은 버그를 줄이거나, 방지할 수 있습니다.

 

1. 유효하지 않은 포인터 접근

- 이미 해제된 메모리에 접근하는 경우에 발생하는 버그입니다.

만약 메모리에 다른 값이 새로 할당되었다면, 해당 메모리를 바라보던 포인트는 전혀 다른 값을 반환하게 됩니다.

 

2. 이중 해제

- 이미 해제된 메모리를 다시 해제하는 경우에 발생하는 버그입니다.

ex) free() 같이 수동으로 메모리를 해제 하는 과정에서 이미 해제된 메모리를 한번더 해제 하면서 에러를 발생시킵니다.

 

3. 메모리 누수(메모리 릭)

- 프로그램이 더이상 필요로 하지 않는 메모리 영역이 해제되지 않고 남아있는 경우를 말합니다.

메모리 누수가 반복되는 경우 사용가능한 메모리 영역이 고갈나게되고, 그 경우 프로그램은 중단됩니다.

* 메모리 누수가 아님에도 메모리 영역이 고갈나는 경우는 GC 로도 막을 수 없습니다.

 

<단점>

 

1. 해제해야 하는 메모리를 결정하는 오버헤드

- GC 는 프로그램 동적으로 할당했던 메모리 영역 중에서 필요없게 된 영역 해제 하는 메모리 관리 기법입니다.

라고 하였습니다. 그렇다면 필요없게 된 영역을 누가 알려주죠? 개발자가 직접 알려주나요? 나 여기 필요없어.

그럴거면 수동으로 메모리를 관리하는 것과 무슨 차이가....

따라서, GC가 동작할 때, 필요없게 된 영역을 결정해야 하는 로직이 동작합니다.

이때 결정하기 위한 오버헤드가 발생합니다.

 

2. 할당된 메모리가 해제되는 시점을 알 수 없다.

- 일반적으로 GC 는 개발자가 호출하지 않습니다.(자바의 경우 JVM 의 가비지컬렉터 가 이를 실행합니다.) 즉, 개발자가 "변수=null" 을 통해서 더이상 사용하지 않기 위해 참조를 해제한다고 하더라도, GC가 동작하기 전까지는 메모리 영역에서 해제되지 않는다는 겁니다.

 

안드로이드를 기점으로 생각해볼까요? Activity 가 실행되고 있을 때, 해당 Activity 가 종료되면 Activity 가 사용하던 메모리 영역은 그 즉시 해제되나요? 확신할 수 있나요?

 

GC원리를 학습하면, 해제해야하는 대상을 결정하는 로직을 보게 됩니다. 그런 로직에 따라서 결정되는데 프로그램을 개발하는 순간에 GC 가 해제해야 하는 대상을 결정하는 로직과, GC가 일어나는 타이밍들을 100% 고려하여 작성할 수 있나요?

 

저희는 GC가 언제 동작하는지 예측하는것도 어려울 뿐더러, 그 GC 가 동작하는 순간에 어떤 메모리 영역을 해제해야 대상으로 선택했는지 에 대해서 알 수 없다고 봐야합니다.

 

 

< Java GC 에 대해서 좀 더 알아보고 싶다면 >

devhyeon0312.tistory.com/17 게시글을 확인해주세요 🤗

'BackUp (관리중지) > CS 학습' 카테고리의 다른 글

REST API  (0) 2021.04.28
Java GC  (0) 2021.04.28
동시성 이슈  (0) 2021.04.27
쓰레드(Thread) / 프로세스(Process)  (0) 2021.04.27
동기(Synchronous) / 비동기(Asynchronous)  (2) 2021.04.27

Android 학습을 위한 단톡방에서 질문이 하나 올라왔습니다.

 

질문으로 공개된 코드

이런식으로 바텀네비게이션으로 프래그먼트 전환식으로 제작을하였는데
바텀네비게이션을 누를때마다 새롭게 갱신해줄려면 어떻게해야하나요 ?
프래그먼트 null 부분조건문을 삭제해버리면
점점 어플이 느려지는 현상이나오구요 흑

 

라는 질문이 올라왔는데요,


질문을 토대로 예상해보면 제가 생각한 앱의 모습은 아래와 유사할 것이라고 판단하였습니다.

만약, 파란부분을 클릭했을 때, 초록색Fragment 가 보여진다고 했을 때,

파란부분을 클릭할 때마다 해당 Fragment 가 Update 되기를 원하는 것이라고 생각했습니다.

 

저는 이번 질문에 대해서 해결하기 위한 코드를 드리지는 않았습니다. 그와 관련된 링크를 제시하지도 않았습니다.

 

그러나, 아래와 같이 3가지 답변을 제공하였습니다.

 

1. 해당 코드에서 null 조건부를 없애면 갱신이 되는데, 에 대한 이유가 무엇인가?

일단, 만약 하단네비게이션 아이템을 클릭했을 경우에 해당 코드가 실행되는 것이라면, 데이터를 갱신하는 코드는 존재하지 않네요.

 

Fragment 는 Activity 실행중에 추가 및 삭제가 가능하다. ( devhyeon0312.tistory.com/6 에 작성된 내용) 는 특징이 존재합니다.

그렇다면 위 코드에서 null 조건을 없애지 않으면 갱신이 안되는데, 왜 null 조건을 없애면 갱신이 되는걸까요?

위 코드에서 fragment 가 null 인경우 인스턴스화를 하여 Activity 에 추가하는 코드가 존재합니다.

null 이 아니라면, 기존에 생성한 fragment 를 보여주고 있습니다.

 

null 조건부만 제거했을 경우에는, 기존에 생성한 fragment 의 인스턴스화된 여부는 고려하지 않고, 무조껀 재생성 후 추가를 하게 됩니다.

작성된 fragment들의 코드를 알 수는 없지만, fragment 를 생성할 때 데이터를 가져와서 fragment 생성을 완료하는 것이라면 당연히 데이터 갱신이 일어났다고 판단 할 수 있습니다.

 

2. null 조건부를 없앴을 때 앱이 점점 느려지는 현상에 대한 이유가 무엇인가?

context 에서 GC 가 동작하지 않는 상황에 대해서 작성했던 것을 보신분이 계실까요? ( devhyeon0312.tistory.com/3 ) 에 나와있는데요,

fragment 라고 해서 다를게 없습니다. GC 의 상황은 언제든 항상 신경써야 하는 부분이니까요.

 

해당 내부 코드가 어떻게 작성되어 있는지는 알 수 없지만, 클릭할때마다 앱이 느려진다는 말을 토대로 저는 직감했습니다.

인스턴스화된 fragment 가 제거되지 않고 있다. (메모리에서 사라지지 않고 있다.)

 

단순히 new 를 했다고 해서 fragment 가 메모리에서 사라지지 않는 것은 아닐거라고 판단됩니다. 어딘가에서 해당 프래그먼트를 참조하고 있거나, 프래그먼트가 다른 메모리를 참조하면서 해당 프래그먼트가 GC의 대상에 속하지 않는 이유가 존재할 것이라고 생각합니다.

 

 

3. 그렇다면 어떤 방법으로 접근해야하는가?

갱신을 위해서 Fragment 를 꼭 재생성해야하는가? (이미 생성되었는데?) 에 대한 질문부터 생각해보면, 당연히 No 라고 답할 것입니다.

이미 기존에 만들어준 Fragment 가 있는데, Fragment 를 다시 생성하는 것은 비효율적일 것입니다.

 

따라서 저는 Fragment 의 데이터를 갱신하는 코드를 추가로 작성해두고, 해당 코드를 실행하는 방향을 생각해 보는 쪽을 추천하였습니다.

 

해결하는 방법은 여러가지가 있을 수 있습니다. 전체적인 코드에 따라서 적절한 해결방법이 존재할 것으로 보여집니다.

단순히 "해결코드" 만을 제시하기 보다는 해당상황이 발생한 이유에 대해서 알아본다면, 자연스럽게 현재 코드에 적합한 해결방안을 찾으실 수 있을 것이라고 판단하였습니다.



제가 생각한 것이 곧 답이라고 생각하지 않습니다.

혹시 다른 의견과 지식을 갖고계신분이라면, 댓글을 통해 제시해주시면 감사하겠습니다.

+ Recent posts