컴공 채널도 있었네 ㅋㅋ

여기에도 올려야지

원글:https://arca.live/b/programmer/61417534



Stop-The-World

오늘 일찍 퇴근한 기념으로 Stop-The-World 라는 개념에 대해 잠깐 소개해볼까해.

저거 내가 지어낸 거 아님 ㄹㅇ 실제로 있는 단어임.


이 개념을 설명하기 앞서서 GC(Garbage Collector), 가비지 컬렉터라는 개념에 대해 짧게 설명할게

최대한 비전공자도 이해할 수 있도록 설명하도록 노력해볼게.

근데 쓰레드라거나.. 잡다한 내용은 설명 안하고 패스하고 넘어갈수도있슴 양해바람.


기본적으로 프로그래머는 변수를 사용하는데 메모리를 할당하지

이걸 할당해놓고 방치하면 메모리는 폭발적으로 증가하고, 프로그램이 OOM(Out of Memory)를 외치며 뒤져버림

C나 C++이 이 메모리관리가 프로그래머가 할당하고 프로그래머가 해제하는 방식이라 존나 빡세기 때문에

초심자는 메모리누수를 쉽게 겪을 수 밖에 없지.

그래서 나온 개념이 GC임 이 GC라는 친구는 안쓰일것 같은 메모리를 발견하면

수거해서 메모리를 확보하는 아주 고마운 친구임. 즉, 프로그래머의 실수를 어느정도 카바쳐주는 친구임.

이 개념이 도입된건 LISP 이란 언어인데, 궁금하면 나무위키 켜라.

그러고 나서 Java에서는 Stop-The-World 라는 개념으로 gc를 도입해

일단 제일 유명한 알고리즘인 Mark-and-Sweep, 이놈은 프로그램이 시작한 root 지점부터

객체 참조를 전부 Mark 하고 죽여야 되겠다 싶은 놈들을 싹 쓸어버림

놀랍게도 이 작업을 모든 쓰레드를 멈춰놓고 실행한다. 왜? 객체 정리중에 누군가가 참조를 시도하면 Mark 작업이 오염되니까.


말 그대로 Stop the world임 GC가 더 월드!!!!를 외치고 10ms 정도 메모리를 조지는데 사용한다는 거임.

그리고 모든 쓰레드가 멈춘다는건 일시적으로 프로그램이 멈춘다는 의미임. 이게 타격이 매우 큰데

동시 접속자 100~10000명 규모면 몰라도 10만명이 넘는 동시접속자가 10ms 정도 응답이 잠깐 멈춘다 생각해보셈

왜 게임 서버가 Java로 구현 하면 안되는지 감이 오지?

물론 메이플스토리 프리섭 같은 소규모 서버는 자바로 구현해도 문제없다. 동시접속자가 100명은 되려나.

이 놈의 GC때문에 많은 개발자들이 골머리를 앓으며 GC튜닝을 한다.


심지어 GC를 끄는곳도 있음.

아 물론 Stop-the-world랑은 관계가 없기는 한데, GC 때문에 성능저하가 발생해서 꺼버린 일화임


바로 인스타그램이 python으로 구현된 지들 서버의 GC를 과감하게 꺼버림 구글에 검색하면 꽤 유명한 일화다.


에에에엥 GC 꺼버리면 메모리는 누가 관리해요???


그래서 GC말고도 메모리를 수거하는 또다른 알고리즘이 Reference Counting 임

예제 코드: https://ideone.com/c0Islj


물론 저게 GC 역할을 하는건 아니고 GC는 어느정도 프로그래머의 실수를 커버하는데 Reference Counting 알고리즘은

프로그래머가 치명적인 실수를 하면 답이 없음.

우선 Reference Counting은 객체가 생성되고나서 이놈이 얼마나 참조했는지 Reference Counting을 시도함

컴공과 친구들은 1학년에 이산수학 배우지? 거기서 위상정렬이란걸 배울거고 indegree 라는 개념을 배울텐데 그 indegree가

Reference Count임, 간단하게 나를 참조하는애가 0이 되면 아 더 이상 난 쓸모가 없구나 하면서 뒈짖 해버리는거임

컴공과 친구들은 나중에 배우겠지만, Reference Count의 Atomic 만 보장되면 Java 의 Stop-The-World를 쓸 필요도 없는거임.

게다가 참조가 0이되면 즉각적으로 수거해서 속도도 빠르고 메모리 수거율도 좋음 쓰레드를 멈추지도 않으니까 더 좋지.

엥? 그러면 GC 만들지말고 죄다 저걸로 구현하면 되는거 아니에요??

내가 위상정렬을 언급한 이유가 지금부터 나옴

이 Reference Count 도 치명적인 단점이 있는데 Circular Reference 라는 상황이 발생하면 좆되게 된다.

이게 왜 좆되냐면 Reference Count가 0이 될때 수거한다고 했지? 근데 서로를 참조하는 상황이 발생한다고 해보셈.


https://ideone.com/JCcgtM 예제 코드


a -> A

b - > B

A -> B

B -> A

이런 상황에서

a, b를 지웠음

근데 A->B를 보고 있으니 B의 참조 횟수는 여전히 1이고

B->A를 보고있으니 A의 참조횟수는 여전히 1임

근데 얘내들을 제어해줄 애가 다 뒤져버렸네????

그렇기 때문에 저런 순환참조도 제어할 수 있는 GC를 도입한거임

Python은 GC와 Reference Counting 두가지를 모두 도입했기 때문에

저런 순환참조만 없으면 GC를 쓰지 않아도 Reference Counting 만으로 메모리 관리가 되었던거임

즉, 인스타그램 프로그래머의 역량이 매우 중요했지.


C++11 즉, 모던 C++에도 이런 Reference Counting 개념을 도입했는데 그것이 RAII, 그중에서도 shared_memory임

궁금하면 배워보는것도 나쁘지 않음.


아 근데 이거 도입해서 느려졌다고 불평이 많았는데 그 이유는 다음과 같음


이런식으로 참조는 한번씩 밖에 안되어있는 트리구조가 있다고 하자.

A가 수거되면 얘내를 쭉 순회해보면서 전부 수거해가야겠지?

근데 이게 지금처럼 작은 상황이면 모르겠는데, 10만개쯤 만들었다치자.

그거 수거가 발생하면 어떻게 될거 같아? 그리고 그걸 DFS로 찾기 시작하면?

당연히 수거가 발생한 시점에 굉장한 블락이 걸리겠지?

예제 코드: https://ideone.com/FqIkO5

이 코드에서 보면 알겠지만, Python의 이러한 수거 방식은 DFS를 따르고 있음.


글을 쓰다보니 갑자기 Reference Counting 으로 빠져버렸네

Go, C# 이런 애들도 이러한 GC를 다루는 작업을 언제든지 할 수 있고

Python 도 지원함, 내가 알기론 C#도 C++과 Python 처럼 Reference Counting을 지원해서 GC를 꺼도 상관없는걸로 암.

그리고 GC는 날로 발전하고 있는 중이다. 뭔가 또 새로운 알고리즘이 튀어나오려 하는 중임.



아무튼 GC 그것도 특히나 Java는 더 월드!!!!!! 를 외치며 너의 프로그램을 멈춘다는 재밌는 사실.


긴 글 읽어줘서 고마워.


참고자료: https://d2.naver.com/helloworld/1329, https://www.geeksforgeeks.org/mark-and-sweep-garbage-collection-algorithm/

그외 구글 다수.