Java Reflection API
DTO Deserialize / 서비스 호출 시 loadClass()와 함께 일어나는 Java Reflection에 대하여
Binding
각종 변수들이 더 이상 변경되지 않는 값으로 구속되는 것.
변수가 타입에 의해 데이터형이 확정되어 메모리 주소를 가리키거나 값을 가지거나 호출될 함수를 결정하는 과정.
정적 바인딩 vs 동적 바인딩
- 정적 바인딩(ex. Method Overloading)
컴파일 시점(compile-time)에 결정됨.
컴파일 시점에 비용 발생. - 동적 바인딩(ex. Method Overriding)
런타임(run-time)에 결정됨.
실행 시점에 비용 발생.
Java Reflection API
Java의 Reflection API는 runtime에 아직 결정되지 않은 클래스에 접근하여 클래스의 메타데이터, 변수, 메서드 등을 가져올 수 있는 수단임.
그러나 runtime에 비용이 발생하므로 성능 저하를 발생시킨다.
따라서 Reflection을 최소화할 수 있다면 best.
예를 들어 DTO를 언마샬링할 때를 생각해보면, response가 어떠한 type으로 올지 모르는 상태에서 Field.get() 또는 Method.invoke() 등을 사용해 클래스의 필드와 메서드를 가져와야 하는데,
이때 데이터 클래스(DTO)들의 리스트를 대략 알고 있다면, DTO 클래스들을 미리 로드하고 캐싱하는 전략을 세울 수 있음.
또한 Reflection을 사용하더라도 Method.invoke()로 메서드를 가져오는 것보다, MethodHandle.invoke() (Java 7+)를 사용하면 JIT(Just-In-Time) compile 최적화가 가능함.
JIT 컴파일러 (Just-In-Time Compiler)
JVM이 Java 바이트코드를 실행할 때, 자주 호출되는 메서드나 코드를 실행 시점에 즉시 기계어(native code)로 컴파일하여 성능을 높이는 기술.
- JVM이 바이트코드를 실행 중,
- 반복 호출되는 코드를 발견하면
- 해당 코드를 즉시 Native 코드로 번역하여 다음 호출부터는 빠르게 실행
Method.invoke()는 매 호출마다 접근 권한 검사와 매개변수 변환이 필요하여 JIT 최적화가 어려우나,
MethodHandle.invoke()는 JVM이 호출 구조를 미리 알고 있어 JIT 최적화가 효과적으로 수행됨.