Java: JDK 7의 새로운 기능 - Java외 언어 지원
앞 블로그 글에서 살펴본 JDK 7의 새로운 기능 중 Java 외 다른 개발 언어 지원 기능에 대해 좀 더 알아보자.
Java Virtual Machine은 사실 Java 언어와는 아무런 연관이 없다. Java Virtual Machine 스팩 문서의 Introduction을 보면 아래와 같은 문장이 나온다.
The Java virtual machine knows nothing of the Java programming language, only of a particular binary format, the
즉, JVM은 Java 언어에 대해서는 아무것도 모르며 단지, byte 코드와 기타 정보를 가진 class 파일 포맷에 대해서만 알고 있다는 것이다. Java 언어가 아니라도 Java Virtual Machine 스팩 문서에서 정의한 클래스 포맷과 byte 코드 명령어 규칙을 따르기만 하면 JVM에서 실행할 수 있다.
Java가 비록 전 세계적으로 가장 많은 개발자를 거느린 훌륭한 개발 언어임은 틀림없지만 Ruby나 Python과 같이 새롭게 떠오르고 있는 동적 언어를 사용하길 원하는 개발자도 있다. 그렇다면, Java의 이점과 동적 스크립트 언어의 장점을 함께 활용할 수 있다면 좋지 않을까?
또한, 언어 개발자로서는 Java Virtual Machine의 고성능과 좋은 기능을 언어 런타임으로 재활용할 수 있는 장점이 있다. 예를 들어, 새로운 스크립트 언어를 개발하는 데 가비지 콜렉션이 필요하다면 새로 구현하는 것보다는 Java Virtual Machine에 기대는 편이 났다. JIT 컴파일러, 다양한 운영체제 지원 등 많은 장점이 있을 것이다. 덤으로 Java 클래스 라이브러리를 이용할 수 있는 장점도 있다.
매우 활발한 모습을 보이고 있는 JRuby의 경우를 보면 Ruby의 모든 기능을 제공하면서 Java의 클래스 라이브러리도 사용할 수 있다. 성능은 기존 Ruby보다 약 2.5배 정도를 자랑한다. Java Virtual Machine을 활용한 덕분이라 하겠다. 더구나 여러 운영체제나 CPU에 별도로 포팅할 필요도 없다!
이처럼 JDK 6.0에서도 이미 여러 스크립트 언어를 지원할 수 있다면 굳이 JDK 7.0에서 새로운 개발 언어 지원이라는 기능을 강조하는 이유는 무엇일까? 이점을 이해하기 위해서는 Java Virtual Machine의 깊숙한 내면을 살펴보아야 한다. 자... 심호흡 한번 하고.
기존 Java Virtual Machine에서의 동적 스크립트 언어 지원 문제
여러분이 잘 알고 있듯이 Java는 정적 형 언어이다. 객체든 변수든 사용하기 위해서는 프로그램 코드에 형을 지정해 주어야 한다. 어떤 메쏘드를 호출하기 위해서는 메쏘드를 가진 객체, 메쏘드의 인자 값, 반환 값이 어떤 형태인지 알고 있어야 한다.
이와 달리, Ruby와 같은 동적 형 언어는 메쏘드를 호출하기 위해 객체의 형이 무엇인지 몰라도 상관없다. 런타임이 실행 중에 적절한 메쏘드를 호출하거나 혹은, 런타임 에러를 발생시킨다.
def testMethod(a)
# a가 어떤 형이지? yieldResult라는 메쏘드를 가졌나?
# Ruby 런타임이 실행 시간에 알아서 처리한다.
a.yieldResult()
end
이런 Ruby의 동적인 특성을 Java Virtual Machine에서 자연스럽게 구현할 방법은 없다. 왜 그런지 살펴보자. JVM에서 메쏘드를 호출하기 위해 정의한 byte code 명령어에 4가지가 있다.
Ruby를 JVM에 구현할 때 invokevirtual 혹은 invokeinterface 명령어를 이용하여 Ruby 메쏘드를 호출하도록 해야 할 것이다. 문제는 JVM이 이 byte code 명령어들을 제대로 사용하기 위해서는 메쏘드의 클래스, 메쏘드 이름, 선언 형식 등의 정보를 필요로 한다는 것이다. 예를 들어보자.
단순한 해결책으로 java.lang.reflect.Method 객체를 이용하여 lessThan 메쏘드를 호출하도록 구현할 수 있다. 런타임 시에 reflection을 이용하여 a 객체의 "lessThan"에 대한 Method 객체를 생성하고 invokevirtual은 생성한 Method 객체의 invoke를 호출하여 원하는 결과를 얻는다.
매번 메쏘드를 호출 할 때마다 이런 과정이 이루어져야 한다면 당연히 수행 성능이 떨어질 것이다. 사실 이런 문제를 해결하기 위해 JDK 7.0에서는 새로운 Java Virtual Machine 명령어를 추가한 것이다.
JDK 7.0의 해결책 - invokedynamic
JDK 7.0의 Java Virtual Machine은 새로운 명령어 - invokedynamic - 을 추가하여 동적 스크립트 언어가 Java 언어와 거의 같은 성능을 낼 수 있도록 한다.
invokedynamic 명령어는 앞에서 살펴본 invokevirtual과 달리 메쏘드 호출을 위해 클래스 정보가 필요하지 않다. 호출할 메쏘드의 이름과 선언 형식만 제공하면 런타임에 적절한 메쏘드를 호출할 수 있다.
invokedynamic은 어떻게 런타임에 적절한 메쏘드를 호출할 수 있을까? JDK 7.0은 CallSite, MethodHandle 그리고 bootstrap 메쏘드 등을 이용하여 invokedynamic이 가능케 한다. 동작 원리를 살펴 보자.
하지만, JRuby의 경우에는 이미 위와 유사한 방식의 구현을 따르고 있는 것으로 보여 과연 JDK 7.0의 새로운 방법을 적용할 때 얼마나 성능 향상을 이룰 수 있을지 모르겠다. 이에 대해서는 아래 참고 자료에서 A First Taste Of InvokeDynamic 글을 읽어보아야 겠다. 지금은 졸려서 이만...
UPDATE:
JRuby 1.6을 개발하고 있는 EngineYard의 Thomas Enbo가 SD Times와의 인터뷰에서 아래와 같은 언급을 하였다.
"“We can already do all the things ‘invoke dynamic’ allows in our codebase today, but it means we have to generate lots of Java classes to accomplish the same thing," said Enebo. "We end up loading lots of classes, which slows down load time and eats up method inlining budgets. Invoke dynamic is not supposed to be a part of the inline budget, so in theory, things could end up being dramatically faster. So far, however, they've just been a little faster." "
즉, JDK 7이 제공하는 invokedynamic을 이용하면 JRuby 1.6에서 성능 향상이 많이 기대된다는 것인데 현재는 살짝 성능 향상이 보이는 정도란다.
참고자료:
Java Virtual Machine은 사실 Java 언어와는 아무런 연관이 없다. Java Virtual Machine 스팩 문서의 Introduction을 보면 아래와 같은 문장이 나온다.
The Java virtual machine knows nothing of the Java programming language, only of a particular binary format, the
class
file format. A class
file contains Java virtual machine instructions (or bytecodes) and a symbol table, as well as other ancillary information.즉, JVM은 Java 언어에 대해서는 아무것도 모르며 단지, byte 코드와 기타 정보를 가진 class 파일 포맷에 대해서만 알고 있다는 것이다. Java 언어가 아니라도 Java Virtual Machine 스팩 문서에서 정의한 클래스 포맷과 byte 코드 명령어 규칙을 따르기만 하면 JVM에서 실행할 수 있다.
- JRuby - Java로 구현한 Ruby
- Jython - Java로 구현한 Python
- Groovy - JVM에서 실행되는 새로운 동적 스크립트 언어
- Rhino - Java로 구현한 JavaScript
- JavaFX - Oracle이 RIA를 위해 새로 만든 언어
- Mirah - Ruby와 유사한 문법의 정적 타입 언어
Java가 비록 전 세계적으로 가장 많은 개발자를 거느린 훌륭한 개발 언어임은 틀림없지만 Ruby나 Python과 같이 새롭게 떠오르고 있는 동적 언어를 사용하길 원하는 개발자도 있다. 그렇다면, Java의 이점과 동적 스크립트 언어의 장점을 함께 활용할 수 있다면 좋지 않을까?
또한, 언어 개발자로서는 Java Virtual Machine의 고성능과 좋은 기능을 언어 런타임으로 재활용할 수 있는 장점이 있다. 예를 들어, 새로운 스크립트 언어를 개발하는 데 가비지 콜렉션이 필요하다면 새로 구현하는 것보다는 Java Virtual Machine에 기대는 편이 났다. JIT 컴파일러, 다양한 운영체제 지원 등 많은 장점이 있을 것이다. 덤으로 Java 클래스 라이브러리를 이용할 수 있는 장점도 있다.
매우 활발한 모습을 보이고 있는 JRuby의 경우를 보면 Ruby의 모든 기능을 제공하면서 Java의 클래스 라이브러리도 사용할 수 있다. 성능은 기존 Ruby보다 약 2.5배 정도를 자랑한다. Java Virtual Machine을 활용한 덕분이라 하겠다. 더구나 여러 운영체제나 CPU에 별도로 포팅할 필요도 없다!
(Source: Engine Yard Blog Page)
이처럼 JDK 6.0에서도 이미 여러 스크립트 언어를 지원할 수 있다면 굳이 JDK 7.0에서 새로운 개발 언어 지원이라는 기능을 강조하는 이유는 무엇일까? 이점을 이해하기 위해서는 Java Virtual Machine의 깊숙한 내면을 살펴보아야 한다. 자... 심호흡 한번 하고.
기존 Java Virtual Machine에서의 동적 스크립트 언어 지원 문제
여러분이 잘 알고 있듯이 Java는 정적 형 언어이다. 객체든 변수든 사용하기 위해서는 프로그램 코드에 형을 지정해 주어야 한다. 어떤 메쏘드를 호출하기 위해서는 메쏘드를 가진 객체, 메쏘드의 인자 값, 반환 값이 어떤 형태인지 알고 있어야 한다.
이와 달리, Ruby와 같은 동적 형 언어는 메쏘드를 호출하기 위해 객체의 형이 무엇인지 몰라도 상관없다. 런타임이 실행 중에 적절한 메쏘드를 호출하거나 혹은, 런타임 에러를 발생시킨다.
def testMethod(a)
# a가 어떤 형이지? yieldResult라는 메쏘드를 가졌나?
# Ruby 런타임이 실행 시간에 알아서 처리한다.
a.yieldResult()
end
이런 Ruby의 동적인 특성을 Java Virtual Machine에서 자연스럽게 구현할 방법은 없다. 왜 그런지 살펴보자. JVM에서 메쏘드를 호출하기 위해 정의한 byte code 명령어에 4가지가 있다.
- invokevirtual
- invokeinterface
- invokestatic
- invokespecial
Ruby를 JVM에 구현할 때 invokevirtual 혹은 invokeinterface 명령어를 이용하여 Ruby 메쏘드를 호출하도록 해야 할 것이다. 문제는 JVM이 이 byte code 명령어들을 제대로 사용하기 위해서는 메쏘드의 클래스, 메쏘드 이름, 선언 형식 등의 정보를 필요로 한다는 것이다. 예를 들어보자.
def max(a, b)위와 같은 Ruby 코드를 JVM에서 동작하도록 byte code 명령어로 컴파일하는 경우를 생각해보자. a.lessThan(b)를 byte code로 변환하기 위해서는 a의 클래스가 무엇인지 알아야 invokevirtual을 이용하여 lessThan을 호출하도록 컴파일할 수 있는데 Ruby 코드에는 a에 대한 아무런 정보도 없다. a에 대한 형 정보는 컴파일 타임이 아닌 런타임에만 알 수 있다.
if a.lessThan(b)
b
else
a
end
end
단순한 해결책으로 java.lang.reflect.Method 객체를 이용하여 lessThan 메쏘드를 호출하도록 구현할 수 있다. 런타임 시에 reflection을 이용하여 a 객체의 "lessThan"에 대한 Method 객체를 생성하고 invokevirtual은 생성한 Method 객체의 invoke를 호출하여 원하는 결과를 얻는다.
매번 메쏘드를 호출 할 때마다 이런 과정이 이루어져야 한다면 당연히 수행 성능이 떨어질 것이다. 사실 이런 문제를 해결하기 위해 JDK 7.0에서는 새로운 Java Virtual Machine 명령어를 추가한 것이다.
JDK 7.0의 해결책 - invokedynamic
JDK 7.0의 Java Virtual Machine은 새로운 명령어 - invokedynamic - 을 추가하여 동적 스크립트 언어가 Java 언어와 거의 같은 성능을 낼 수 있도록 한다.
invokedynamic 명령어는 앞에서 살펴본 invokevirtual과 달리 메쏘드 호출을 위해 클래스 정보가 필요하지 않다. 호출할 메쏘드의 이름과 선언 형식만 제공하면 런타임에 적절한 메쏘드를 호출할 수 있다.
invokedynamic은 어떻게 런타임에 적절한 메쏘드를 호출할 수 있을까? JDK 7.0은 CallSite, MethodHandle 그리고 bootstrap 메쏘드 등을 이용하여 invokedynamic이 가능케 한다. 동작 원리를 살펴 보자.
- 동적 스크립트 언어의 메쏘드 호출에 해당하는 부분을 invokedynamic 명령어로 컴파일한다.
- 실행 시 JVM이 invokedynamic 명령어를 만나면 이에 대한 CallSite가 있는지 본다.
- 만일 CallSite가 없다면 현재 invokedynamic을 위해 등록된 bootstrap 메쏘드를 호출한다. bootstrap 메쏘드는 동적 언어의 런타임이 제공하는 것이다.
- bootstrap 메쏘드는 동적 스크립트 언어의 메쏘드가 호출될 수 있도록 CallSite 객체를 생성하여 반환한다. (CallSite 객체는 실제 호출할 메쏘드에 대한 MethodHandle을 설정할 수 있는 setTarget 그리고 설정된 MethodHandle을 얻어오는 getTarget 메쏘드를 제공한다.)
- invokedynamic은 CallSite에 등록된 MethodHandle을 이용하여 메쏘드를 호출한다.
- 이미 CallSite가 등록된 invokedynamic이 다시 실행되는 경우 위 과정을 생략하고 바로 메쏘드를 호출할 수 있다.
하지만, JRuby의 경우에는 이미 위와 유사한 방식의 구현을 따르고 있는 것으로 보여 과연 JDK 7.0의 새로운 방법을 적용할 때 얼마나 성능 향상을 이룰 수 있을지 모르겠다. 이에 대해서는 아래 참고 자료에서 A First Taste Of InvokeDynamic 글을 읽어보아야 겠다. 지금은 졸려서 이만...
UPDATE:
JRuby 1.6을 개발하고 있는 EngineYard의 Thomas Enbo가 SD Times와의 인터뷰에서 아래와 같은 언급을 하였다.
"“We can already do all the things ‘invoke dynamic’ allows in our codebase today, but it means we have to generate lots of Java classes to accomplish the same thing," said Enebo. "We end up loading lots of classes, which slows down load time and eats up method inlining budgets. Invoke dynamic is not supposed to be a part of the inline budget, so in theory, things could end up being dramatically faster. So far, however, they've just been a little faster." "
즉, JDK 7이 제공하는 invokedynamic을 이용하면 JRuby 1.6에서 성능 향상이 많이 기대된다는 것인데 현재는 살짝 성능 향상이 보이는 정도란다.
참고자료:
- Support for Dynamically Typed Languages in the Java Virtual Machine
- List of JVM Languages
- Rails and Merb Merge: Performance
- Kicking JRuby Performance Up a Notch
- JDK 7.0 Early Document
- Java Virtual Machine Spec 2nd - 3.11.8 Method Invocation ...
- Type System - Wikipedia
- InvokeDynamic: Actually Useful?
- A First Taste Of InvokeDynamic
- Top 5 scripting languages on the JVM
댓글
댓글 쓰기