TL;DR
jam-vm은 패치된 OpenJDK와 GraalVM에서 JVM 가비지 컬렉터를 대체해, Haskell 클로저와 Java 객체가 호스트 힙을 공유하게 함.- GHC 스타일 일반화 약한 포인터는 키·값·선택적 파이널라이저의 연결 관계와 키 재활성화를 가비지 컬렉터가 직접 처리하는 방식임.
- Java의
WeakReference조합만으로는 값의 역참조와 키의 생존 관계를 올바르게 구현할 수 없어, 컬렉터가 연결 관계를 이해해야 함. - 현재 지원 모드는 압축 일반 객체 포인터(Compressed Ordinary Object Pointer)뿐이며, 힙은 최대 32GB로 제한되는 대신 포인터 크기는 64비트에서 32비트로 줄어듦.
SubstrateVM이식이 진행 중이며, 완성되면 GraalVM에서 GHC 언어 기능을 완전히 지원하는 데 가장 큰 장애물이 제거될 전망임.
jam에서 jam-vm까지
- 며칠 전 다른 C++ 프로젝트용으로 SIMD 멀티스레드 마크 앤 컴팩트 가비지 컬렉터를 작성하고
jam이라고 이름 붙임. - 이후
jam에 GHC 스타일System.Mem.Weak파이널라이저와 일반화 약한 포인터를 추가하는 방법을 찾아냄. - 같은 시기, THC를 위해 GHC의 해당 부분을 JVM에서 동작시키려던 기존 계획이 무너짐.
- Luite Stegeman이 GHCJS에서 했던 것처럼 파이널라이저 의미론을 맞추기 위해 Haskell 힙에 추가 도달 가능성 탐색을 수행하는 방안을 검토했으나, 복잡한 방식으로 판단함.
- 시험 삼아 HotSpot의 가비지 컬렉터 인터페이스를 통해 JVM 가비지 컬렉터를 직접 교체했고, 예상대로 동작함.
- 그 결과물인
jam-vm은 패치된 OpenJDK와 GraalVM 빌드에서 가비지 컬렉터로 실행되며, 호스트 힙의 실제 Java 객체를 수집함. - 이에 따라 Haskell 클로저가 다른 객체와 호스트 힙을 공유할 수 있음.
JVM 통합의 제약
- 가비지 컬렉터를 JDK 자체를 다시 빌드하지 않고 JNI를 통해 연결할 수 있다면 좋겠지만, 확장성은 이 지점에서 제한됨.
- HotSpot 인터페이스는 HotSpot에 가비지 컬렉터를 추가할 수 있게 하지만, 기본 JVM이 이를 라이브러리로 불러오도록 하지는 않음.
일반화 약한 포인터와 파이널라이저
- 일반화 약한 포인터는 키, 값, 선택적 파이널라이저를 연결함.
- 키가 독립적으로 도달 가능하면 연결 관계가 값을 살아 있게 유지함.
- 값이 키를 다시 가리키더라도 그 순환 참조만으로 연결 관계가 영구히 살아남지는 않음.
- 파이널라이저도 키를 참조할 수 있고 키를 다시 살릴 수도 있지만, 기존의 약한 연결 관계는 계속 사라진 상태로 남음.
- 강한 참조로 유지되는 값 옆에 Java
WeakReference를 두는 방식은 값의 역참조가 키를 살릴 수 있어 같은 의미론을 제공하지 못함. - 두 참조를 모두 약하게 만들면 키가 살아 있는 동안에도 값이 사라질 수 있음.
- 따라서 가비지 컬렉터가 키와 값의 연결 관계 자체를 이해해야 함.
Java 파이널라이제이션과 Haskell의 설계
- Java 파이널라이제이션을 둘러싼 수십 년의 어려움과 대조적으로, Haskell은 1999년에 순환 참조와 객체 재활성화를 일관되게 다루는 더 풍부한 설계를 갖추고 있었음.
- 이 의미론을 컬렉터에 이식하는 작업은 반나절 정도의 일이었음.
- 그렇다고 파이널라이저 실행이 즉각적으로 보장되거나 Java의 보안 및 생명주기 문제가 사라지는 것은 아님.
- 파이널라이저가 키를 다시 살릴 수 있다는 점도 극복할 수 없는 장애물은 아니며, 이를 위한 설계가 이미 27년 동안 존재함.
SubstrateVM 이식과 GraalVM 지원
- 현재
SubstrateVM으로 이식하는 작업을 마무리 중이며, 이를 통해 Native Image에서도 사용할 수 있게 하는 단계임. jam의 기본 기능이 이미 THC의 기능과 밀접하게 맞아 있어, THC 파이널라이저 연결은 대부분 세부 작업을 마무리하는 수준임.- 이 작업이 완료되면 GraalVM에서 GHC 언어 기능을 완전히 지원하는 데 가장 큰 장애물이 제거됨.
메모리 제한과 향후 작업
- 현재 컬렉터는 압축 일반 객체 포인터(Compressed Ordinary Object Pointer) 모드만 지원함.
- 64비트 플랫폼에서도 힙은 최대 32GB이며, 젊은 세대와 오래된 세대에 각각 16GB가 배정됨.
- 그 대신 포인터 크기가 64비트에서 32비트로 줄어 L1 캐시에 두 배 많은 포인터를 담을 수 있음.
- 포인터 중심 구조에서는 캐시에 더 많은 유용한 데이터를 담을 수 있으며, 정확한 이득은 객체 배치에 따라 달라짐.
- 향후 작업에는 평가된 썽크에 적용되는 필드 접근자 전달과 썽크 전달 포인터의 제자리 제거가 포함될 수 있음.
- 가비지 컬렉터 전체를 직접 관리하면 이전에는 거의 불가능했던 여러 종류의 작업이 가능해짐.
코드와 문서
- 코드는
jam과jam-vm에 있으며, 가비지 컬렉터 문서와 JVM 통합 문서도 제공됨.
댓글 (0)
로그인하면 이 기사에 내 생각을 남길 수 있어요