Java 코드를 Lisp에서 직접 실행하는 OpenLDK 런타임
원제 OpenLDK: Bringing the Java World into Common Lisp
101 포인트댓글 15
Key Point
Java 라이브러리를 Common Lisp 애플리케이션과 통합해야 하는 개발자에게는 새로운 선택지를 제시하고, 각 JVM 언어를 하나의 식 안에서 연결하는 폴리글롯 프로그래밍의 가능성을 보여준다.
핵심 요약
- Common Lisp으로 작성된 JIT 컴파일러인 OpenLDK는 Java 바이트코드를 읽어 Lisp으로 번역한 뒤 SBCL을 통해 네이티브 코드로 컴파일하며, Java 객체와 Lisp 애플리케이션이 동일 프로세스·힙에서 살아간다.
- 클래스 로드 시 OpenLDK가 CLOS 표현을 만들고 메서드 스텁을 설치하며, 메서드 첫 호출 시 바이트코드→중간표현→Lisp→네이티브 코드 순으로 컴파일되고 스텁을 대체한다.
- Java 필드는 CLOS 슬롯이 되고, 인스턴스 메서드는 CLOS 제네릭 함수의 메서드가 되며, 정적 메서드는 일반 Lisp 함수가 되고, Java 예외는 Lisp 조건 계층구조에 대응된다.
- Kotlin, Scala, Groovy, Clojure, Kawa, JScheme, ABCL 등 7가지 JVM 언어를 한 Lisp 식 안에서 중첩할 수 있으며, 각 언어의 네이티브 문법을 유지하면서 값을 전달할 수 있다.
- Lisp은 :before, :after, :around 메서드로 Java 메서드 호출을 가로챌 수 있고, CLOS 메서드 조합이 적용되며, Lisp에서 정의한 클래스를 Java가 일반 Java 클래스로 인식할 수 있다.
- Java 예외는 타입화된 Lisp 조건이 되고, 역방향으로도 Lisp 에러가 Java 예외로 변환되며, 양쪽 모두 고유한 에러 처리 방식을 쓸 수 있다.
- 컴파일된 메서드는 지속적 코드 캐시에 보관되며, 다음 실행 시 의존하는 클래스가 변하지 않으면 캐시된 코드를 로드할 수 있고, 서로 다른 이미지도 컴파일된 JDK 메서드를 재사용할 수 있다.
- ASDF 빌드 시스템이 Maven JAR 컴포넌트 및 지원 언어 소스 컴포넌트를 통합하며, Kotlin과 Scala 파일은 ASDF 컴파일 중에 컴파일되고 클래스는 이후 컴포넌트에서 사용 가능하다.
- Java의 javac 컴파일러가 OpenLDK에서 작동하고, Clojure는 프로토콜·레코드·아톰·퓨처를, ABCL은 Common Lisp을 Java 바이트코드로 컴파일한 뒤 SBCL에서 다시 컴파일하는 루프를 실행한다.
- Spring Boot와 Quarkus 애플리케이션이 실행 가능하며, Spring PetClinic은 내장 Tomcat·Hibernate·H2와 함께 작동한다.
- 이미지 저장 후 재시작 시 Spring Boot 스냅샷은 83.9ms에 시작하는데, GraalVM 네이티브 이미지(53.5ms)보다는 느리지만 표준 HotSpot(595.1ms)보다 훨씬 빠르다.
- GraalVM은 더 빠르고 작지만(41MB) OpenLDK는 저장된 컨텍스트를 재사용할 수 있으며, GraalVM과 달리 폐쇄 세계 가정을 하지 않아 런타임에 새 클래스와 미사용 메서드가 컴파일될 수 있다.
- SBCL Linux를 대상으로 개발 중이며 바이트코드 검증 미구현 등 호환성 차이가 있고, 저장된 이미지 배포 시 클래스패스 파일과 JDK를 런타임에서 사용 가능해야 한다.