가비지컬렉터 와 메모리 관리
오늘 용호님네 블로그에 새 글이 올라와서 놀러갔다가 최신 댓글이 달린 글을 찾아가보니 아래와 같은 글이었습니다.
Flash Player 의 가비지 컬렉션(GC) 동작 방식에 대해
이거 오래 전에 쓴 글인데도 검색되어 꾸준히 댓글이 달리는걸 보니 어느 정도 개발하시다가 보면 다들 메모리 관리에 관심들을 가지시는 건가 라는 생각이 들었습니다.
사실 가비지컬렉팅에 집착하시는 것 자체가 제게는 좀 이상해 보이긴 합니다. 아주 근본적으로 메모리에 대해서 생각해 봅시다. 결국 메모리에 뭔가 썼다는 건 사용하기 위해 썼다는 거죠. 자기가 무엇을 위해 메모리를 사용하지 모르고 쓰는 경우는 없습니다. 그런데 왜 문제가 될까요?
문제가 되는 부분은 바로 메모리를 낭비하기 때문입니다. 메모리를 낭비하는 유형을 간단히 정리해봅시다.
- 재활용될 수 있는 객체인데도 그 때마다 new를 한다.
- 인스턴스의 갯수가 고정적이지는 않지만 최대 생성될 양을 알고 있는 경우는 최대양만큼 new를 해두고 사용해야 한다(결론적으로 최대 양까지 메모리를 사용할 것 임) 만약 이 경우 new를 그 때마다 한다면 역시 낭비될 소지가 다분하다.
- 적절히 직렬화하여 객체를 값으로 바꿔주지 않았다(값은 즉시 메모리에서 해지된다)
- 지역변수 즉 스택메모리 구조로 사용할 수 있는 것을 속성 등으로 잡았다.
객체를 값으로 바꾸어 처리하는 기술은 광범위한 주제라 나중에 다루기로 하고, 객체에 대해서만 생각해보면 한 가지로 압축할 수 있습니다. 메모리가 문제인 경우는 바로 계획하지 않고 new를 남발한다는 점입니다.
무계획적인 new
new를 계획적으로 사용한다는 건 무슨 뜻일까요?
OOP구조에서 클래스는 청사진에 해당되는 설계도 입니다. 다들 이 부분을 설계의 전부라고 생각하시는 경우가 많습니다. 물론 청사진은 설계도면이죠. 하지만 클래스 설계는 실제 프로그램이 구동될 때의 모습을 알려주지 않습니다.
예를 들어 DisplayObject 객체의 상속 구조도는 그런 제약하에서 DisplayObject 자식을 써야만 한다는 규칙같은 것으로, 그게 실제 swf 에서 만들어진 결과물을 의미하지 않습니다.
그럼 실제 설계는 언제 일어날까요? 그건 바로 호스트 코드에서 클래스로부터 인스턴스를 실체화하여 복잡한 연결관계를 지어준 상태에서 발생합니다. 이 부분이야 말로 더욱 중요한 프로그래밍 설계지만, 역설적으로 클래스가 이쁜데도 호스트 코드를 보면 엉망인 경우가 허다합니다.
인스턴스를 계획적으로 사용하려면 한번 new를 해서 만든 인스턴스 하나하나를 그 때 그 때 사용하고 버리는 부속으로 보는게 아니라 책임과 역활을 가진 하나의 유닛으로(구성원이나 가족으로) 보는 눈이 필요합니다.
예를 들어 new Bitmap 을 통해 생성한 비트맵 인스턴스에게 여러 단계로 의미를 부여해볼 수 있습니다.
- 화면에 표시할 비트맵조각이다. – 화면에 비트맵조각이 필요할 때마다 new를 하실 겁니다.
- 화면에 표시할 비트맵에 대해 처리하는 비트맵객체 군의 일부다 – 화면에 비트맵조각은 30개 이하라고 생각하여 30개의 비트맵을 미리 만들어 둔 뒤 이들의 bitmapData속성만 바꿔가면서 계속 화면을 재구성해 가실 겁니다.
- bitmap1 객체는 배경을 담당한다 – 하나의 bitmap1개체는 모든 화면에서 bitmapData를 갱신해가면서 배경을 담당하게 될 것입니다.
즉 필요한 메모리의 양은 필요한 인스턴스의 갯수인데 이는 결국 호스트 코드 설계를 얼마나 계획적이고 탄탄하게 준비 했는가에 달렸다는 거죠.
이러한 준비가 없이 그 장면장면, 로직로직 마다, 만들어둔 클래스를 다 끌어모아 new를 해서 해결해가는 식으로 전진하다 보니 머릿 속에 이를 정당화시키는 변명이, ‘현대 컴터들은 충분히 강력해서 객체로 다 만들고 가비지컬렉터가 알아서 메모리를 회수한다. 가비지컬렉터가 더 지능적으로 개선되어야한다’ 라는 식이죠.
그저 생각을 되돌려 보시면 무계획으로 메모리를 사용하고 계신 게 맞습니다.
구조적인 측면
다른 측면에서 고려해보면 모든 프로그램은 선형적인 진행을 하거나 순환적인 진행을 하기 마련입니다. 이 측면에서 생각을 더 해보겠습니다.
선형적인 측면 – 선형적인 측면에서는 A다음에 B다음에 C라는 식으로 진행됩니다. 따라서 A에서 사용한 모든 자원은 완전히 다른 B라는 상황에서 얼마든지 재활용해도 괜찮습니다. 왜냐면 A가 끝난 뒤 B로 가기 때문에 한 개의 객체가 A에서는 A내의 역활을 수행하고 B에서는 B내의 역활을 수행할 수 있기 때문입니다. 그렇게 사용하려면 각 장면에서 재활용될 객체를 정의할 때 보다 추상적이고 포괄적인 역활과 책임 측면에서 정의해야 합니다. 예를 들어 위에 설명했던 비트맵객체가 3번으로 정의된 경우과 1번으로 정의된 경우의 차이겠죠.
순환적인 측면 – 이 것 역시 어찌 보면 선형적인 측면의 연속입니다. 더군다나 순환적이란 뜻은 애시당초 선형적인 과정보다 변화의 폭이 좁습니다. 재활용될 부분은 훨씬 커지죠.
그 외에 정적으로 공유되는 부분도 물론 있겠죠? 그건 어짜피 사용되어 유지될 메모리란 뜻입니다. 따라서 삭제될 필요가 없죠.
즉 실제로 new를 한 뒤에 삭제되어야만 하는 객체가 얼마냐 있냐는 겁니다. 객체리사이클링에 대해서 얼마나 심각하게 연구해 봤는가의 문제이기도 합니다. 지구온난화를 초래한게 무분별한 자원의 사용이었다면 메모리오버플로우를 초래하는건 재활용하지 않는 객체들 때문일지도 모릅니다.
결론
이 모든 설명에서 하고 싶은 얘기는 간단합니다.
- 정말 필요한 만큼의 new를 한건가?
- 정말 new를 한 뒤에 지워야하는 객체인가?
이 부분이 충분히 고려되었다면 메모리의 사용그래프는 커졌다 작아졌다 하지 않고 점점 어떤 평행선에 가까워집니다. 상식적으로 생각해보세요. 리니지2를 실행할 때는 40메가 메모리가 필요한 법이고 네이트온을 사용할 때는 2메가 정도가 필요한 겁니다. 거기에서 파일전송이나 서버 접속이나 특별한 이벤트가 있을 때만 잠시 메모리가 올라갔다가 내려오는 거죠. 하지만 이 부분 조차도 처음부터 파일전송은 동시에 10개만 된다던가 등으로 인스턴스를 풀링해버리면 메모리 그래프는 점점 더 직선이 되어갑니다. 메모리를 사인파로 만들고 있는 본인의 프로그램이 얼마나 무계획한지 점검해볼 시간입니다 ^^
이러한 고려가 충분히 되었다면 가비지컬렉터가 작동을 빨리하던 늦게하던 그다지 영향은 없습니다. 애시당초 가비지컬렉터가 구동되지 않으면 메모리가 부족할 정도로 마구 new를 하고 있는 그 프로그램 자체가 엄청나게 엄한거죠.
관련된 글:
역시 좋은 글 쓰셨네요~~~~ 접근법이 역시 달라~ ㅎㅎㅎ
결국 애플리케이션이 얼마나 객체를 생성하는가 명확히 고려하고 쓸 수 있는 메모리를 정량화해서
new 연산자를 쓰는 것을 자제하며 gc를 동작시키는 것을 되도록 방지하도록 하는 것이 메모리 관리의 핵심이 되겠네요.
dispatchEvent( new Event()) 등을 남발해버리면 이런것도 문제가 될 수 있겠네요.
new Bitmap()을 하나 만들어놓고선 정말 필요한 수십개의 bitmapData를 돌려가면서 하는 작업하도록 하는게 좋은 방법일 수 있겠고요.
아무튼 이러한 관점으로 바라보면 Flex는 new를 신나게 만들어주니… GC 남발하는 꼴이군요. ㅎㅎ 그래서 어쩔 수 없이 Flex가 문제가 될 수 밖에 없는….
유연성과 효율성은 사실 언제나 적대관계일지도 모릅니다. 모든 프레임웍들은 다양한 환경에서 사용되길 바라기 때문에 보다 유연하고 보다 추상적으로 정의되길 원합니다. 그 결과 런타임에 상황을 판단하여 적절한 객체를 new를 해 유연성을 확보하는 방법이나 추상층을 복합적이고 여러 단계로 정의하여 수많은 참조와 다형성적인 측면에서의 중간 객체를 마구 남발하기 마련입니다.
그렇게 해서 얻는건 물론 확장성과 유연성입니다.
하지만 그러한 프레임웍..예를들어 플렉스에서 조차 메모리의 사용량에 대한 계획과 캐쉬전략을 세움으로서 어느 정도 일정한 메모리 사용을 유도할 수 있습니다. 수많은 new를 아무런 죄책감없이 하는 것은 어떠면 재활용품을 마구 쓰레기통에 버리는 것과 같은 마음인건지도 ^^
최근에 저도 자바 성능을 결정 짓는 코딩 습관과 튜닝 이야기 란 책을 보고 생각좀 많이 하게 되었습니다. ~_~
히카님 요즘 저 네이트온 못을어가서.. 만든거 보여줄데가 줄어들어서 외로우셨겠어용~ ㅋㅋ
이직 축하드려요. 무급에서 벗어나신 것도 축하드리구요 ^^