Java의 Python의 Garbage Collection 비교

냥린이·2021년 12월 7일
0

Python과 Java는 본질적인 차이가 있다. Python은 C에 객체 지향적 기능을 추가하여 만든 언어지만, Java는 처음부터 객체 지향 언어를 염두하고 작성된 언어이다. Python은 오픈소스이며 여러 언어로 재작성 되고 실험적인 시도가 계속되고 있는 언어인 반면, Java는 Oracle에서 라이선스를 가지고 유지보수 하는 언어이다. Python은 동적 타이핑 언어인 반면 Java는 정적 타입 언어이다. Python은 괄호와 ; 을 없애고 들여쓰기를 도입하므로 시각적으로 간결하고 쉬운 문법을 가졌지만 Java는 상대적으로 까다로운 문법을 가지고 있다. 사용처에서도 차이를 보이는데 Python은 웹 개발과 간단한 스크립트 작성 시에 많이 쓰이며 특히 데이터 엔지니어와 인공지능 부분에서 수요가 많은 반면, Java는 (특히 한국에서는) 서버 개발에 집중되어 있는 경향을 보인다.
프로그래밍 언어론 관점에서 살펴봤을 때 가장 큰 차이는 컴파일러와 인터프리터방식의 차이라고 할 수 있다. GIL을 가지고 있는 인터프리터 언어인 Python은 최종적으로 컴파일 되는 Java에 비해 속도적으로 열세이다. (GIL의 사용 여부는 Python의 구현체마다 조금씩 상이하다.) Java와 Python의 Garbage Collection 및 힙 구조에 대한 더 자세한 설명은 다음 글에서 추가한다.

(1) 메모리 관리

Java와 Python 구현의 차이는 Garbage Collection이라고 할 수 있다.
Java는 프로그램 코드에서 메모리를 명시적으로 지정하여 해제하지 않는다. Garbage Collector는 더 이상 필요하지 않은 객체를 찾아 지우는 작업을 하며 ‘Weak generation hypothesis’라는 가설 하에 만들어졌다. 대부분의 객체는 금방 unreachable한 상태가 된다는 것과 오래된 객체에서 젊은 객체로의 존재는 아주 가끔 발생한다는 가정이다.
이러한 가설 하에 가상머신에서는 Young과 Old라는 물리적 영역으로 분리된다. Young Generation 영역은 새롭게 생성한 객체가 위하는 곳으로 대부분 객체가 여기에 생성되었다가 금방 unreachable한 상태가 되어 사라진다. 이 영역에서 객체가 사라질 때 Minor GC가 발생되었다고 말한다. Old Generation 영역은 unreachable 상태가 되지 않아 young 영역에서 살아남은 객체가 복사되는 공간이다. 대부분 young 영역보다 크게 할당하며 크기가 큰 만큼 young 보다 GC가 적게 발생한다. 이 영역에서 객체가 사라지는 것을 Major GC(or Full GC)라고 한다. Old generation 영역에서도 살아남은 객체는 Permanent Generation으로 이동된다. 객체나 intern된 문자열 정보를 저장하는 곳이며 이름과는 이곳이라고 객체를 영원히 보관하지는 않는다. 여기서도 GC가 발생하며 이것은 Major GC에 포함된다.
Java의 Garbage Collection 튜닝에서 가장 중요한 이슈는 GC를 실행하기 위해 JVM이 어플리케이션 실행을 멈추는 ‘stop-the-world’ 시간을 줄이는 것이다. 간혹 System.gc() 메소드를 이용해 해제하거나 명시적으로 객체를 null로 지정하는 경우가 있는데, system.gc() 메소드를 호출하는 것은 시스템의 성능에 매우 큰 영향을 끼치므로 반드시 지양해야 한다.
Python은 기본적으로 garbage collection과 reference counting을 통해 할당된 메모리를 관리한다. 앞서 언급한 바와 같이 기본적으로 참조 횟수가 0이 된 객체를 메모리에서 해제하는 레퍼런스 카운팅 방식을 사용하지만, 참조 횟수가 0은 아니지만 도달할 수 없는 상태인 순환 참조가 발생했을 때는 가비지 컬렉션으로 그 상황을 해결한다.
Cpython의 메모리 관리 중 가장 특징적인 것은 GIL의 사용이며 Python의 선천적인 구현 결함이기도 하다. Jpython, IronPython과 다르게 PyPy도 GIL을 사용한다.

(2) JIT 컴파일러 사용

Python 구현체 중 PyPy는 Cpython처럼 C 런타임 라이브러리에 기반을 두지만, Rpython 툴체인으로 인터프리터 소스에 JIT 텀파일을 위한 힌트를 추가하여 ava와 동일하게 JIT 컴파일러를 사용한다. Cpython에 비해 PyPy가 빠른 시간적 성능을 보이는 원인이다.
Java와 JIT와 PyPy의 JIT 차이는, PyPy의 JIT는 Meta tracing JIT라는 새로운 개념을 도입하여 메소드 단위로 최적화하는 전통적인 JIT 와 다르게 런타임에서 자주 실행되는 루프를 최적화한다. PyPy는 일종의 인터프리터 제작 프레임워크이자 언어인 RPython (Restricted Python)으로 만들어졌다. Rpython은 Meta tracing JIT를 C로 구현해 툴체인을 포함한다. Rpython으로 인터프리터를 작성하고 툴체인으로 힌드를 추가하면 RPython 해석기가 알아서 인터프리터에 Mete tracing JIT를 빌드하여 JIT Compile 되는 언어 구현을 만들어 준다. 이렇듯 PyPy는 언어 사양(파이썬 언어 규칙, BF 언어 규칙)과 구현(실제 인터프리터 제작)을 분리함으로 어떤 동적 언어에서도 자동으로 JIT 컴파일러를 생성할 수 있다.

참고자료

Python GC가 작동하는 원리
Java의 가비지 컬렉션
Interview Question for Beginner – Python

profile
홀로서기 기록장

1개의 댓글

comment-user-thumbnail
2023년 6월 24일

좋은글 잘 읽었습니다. 다만, young 과 old 는 물리적으로는 Heap 내에 함께 위치하고, 논리적으로만 구분되는 걸로 알아요

답글 달기