본문 바로가기
Spring

자바의 리플렉션(Reflection)

by 초보개발자96 2023. 9. 2.

리플렉션은 객체의 구체적인 타입을 알지 못해도 해당 객체의 멤버변수, 메서드 등에 접근할 수 있도록 해주는 기능입니다. 

자바 어플리케이션이 컴파일되고 실행될 때, 클래스 로더는 자바 바이트코드(.class)를 읽어서 메모리에 저장합니다. 특히, Class<T> 타입의 인스턴스를 만들어주는데요, 항상 있다는 것을 보장할 수 있기 때문에 아래와 같이 클래스를 만들기만 하면 바로 사용할 수 있습니다.

 

class Member (
  val name: String,
)

val memberClass = Member::class.java
val memberClass2 = Member("chobo").class

 

이러한 Class<T> 인스턴스는 해당 클래스에 정의된 멤버변수, 메서드, 생성자 등 클래스의 모든 정보에 접근할 수 있습니다. 

이처럼 리플렉션은 런타임 시점에 정해지지 않은 인스턴스를 조작할 수 있는 유연성을 제공해주지만 주의하여 사용해야 합니다.

 


 

성능 이슈

컴파일 이후에 생성된 바이트코드는 JVM 의 인터프리터를 통해 한 줄 씩 기계어로 번역되어 실행되는데요, 여기서 발생하는 성능 이슈를 해결하기 위해 여러 방식을 도입했습니다. 그 중, JIT(Just-In-Time) 컴파일러의 캐싱을 통해 한 번 기계어로 번역된 메서드가 다시 실행되면 다시 번역하지 않고 이전에 번역한 것을 사용할 수 있게 하는 방식으로 최적화합니다. 

 

Java 실행 과정

 

자바의 컴파일된 결과물은 특정 머신에 종속되지 않기 위해 바이너리 코드를 생성하지 않고 JVM 이 실행할 수 있는 바이트 코드를 생성합니다.

 

하지만 리플렉션은 클래스의 바이트 코드를 이용하는 것이 아닌, 메모리에 해당 클래스의 특성을 투영한 데이터를 이용하므로 매 번 호출되는 메서드라도 최적화가 되지 않게 됩니다.

이외에도 Typeless 로 인한 추적이 불가하여 리팩토링에 어려움, Private 필드 접근 가능함으로 인해 캡슐화 깨짐 등 여러가지 문제가 있으므로, 프로덕션 코드에서 사용하는 것은 지양하는 것이 좋아 보입니다.

 

참고로 클래스 로더는 기본적으로 실행에 필요한 .class 만 메모리에 올려 실행하고, 나머지는 필요할 때 로딩하는 Lazy loading 전략을 따릅니다. 따라서, 성능 테스트 혹은 배포 시에 초기 warm up 을 하면 좋을 수 있습니다.