JAVA/Effective Java

[아이템 6] 불필요한 객체 생성을 피하라

꾸준함. 2024. 1. 21. 15:38

저자는 아래의 세 가지 케이스에 대해서 불필요한 객체 생성을 피하라고 정리했습니다.

  • 문자열
  • 정규식 패턴
  • 오토 박싱

 
위 세 가지 케이스를 정리하기 전에 우선 하나를 명확히 짚고 가야 합니다.
"아이템 6"의 제목만으로 객체 생성이 비싼 작업이라고 추측해서는 안 됩니다.
객체 생성 자체가 비싼 것은 아니기 때문에 위 세 가지 케이스를 제외하고는 얼마든지 객체를 생성해도 상관없습니다.

 

문자열

JVM(Java Virtual Machine)은 내부적으로 문자열을 pool이라는 일종의 해시맵에 캐싱하고 다른 곳에서 동일한 문자열을 참조하려고 할 때 pool에서 꺼내 재사용하고 있습니다.
따라서 문자열은 굳이 new를 통해 동일한 문자열을 재생성하기보다는 문자열을 바로 할당하는 것이 메모리 측면에서 유리합니다.
 

 
 
* 위 예제에서는 hello와 hello3을 ==으로 비교하더라도 true가 나오긴 하지만 혹시나 다른 인스턴스일 수 있으므로 문자열 비교는 equals()로 하는 것을 추천합니다.

 

정규식 패턴

정규식 같은 경우 생성할 때 CPU를 사용하기 때문에 비싼 작업에 속합니다.

  • 내부적으로 패턴을 컴파일하는 과정이 비싼 작업
  • 정확히 말하자면 문자열 패턴 매칭할 때 유한 상태 머신(Finite State Machine)이 사용되며 유한 상태 머신은 문자열을 한 글자씩 읽어 들이면서 상태를 전이시키는 방식으로 동작하기 때문에 문자열의 길이에 비례하여 시간 복잡도가 증가할 수 있음
    • 특히, 일부 패턴이나 입력 문자열이 매우 길 경우 유한 상태 머신은 많은 상태 전이를 수행해야 하므로 작업이 느려질 수 있음

 
또한 정규식 패턴을 필드를 바로 비교하는 메서드의 경우 내부적으로 정규표현식용 Pattern 인스턴스가 메서드가 호출될 때마다 한 번 쓰고 버려져서 곧바로 GC 대상이 됩니다.
GC가 돌 때마다 지연이 발생하기 때문에 이 또한 성능 측면에서 부정적입니다.
 

 
따라서 두 번 이상 쓰는 패턴일 경우 필드로 선언하고 캐싱해서 사용하는 것을 추천합니다.
실제로 아래 예시를 보면 패턴을 캐싱했을 때 캐싱하지 않았을 때보다 약 10배 빠른 것을 확인할 수 있습니다.
 

 

오토 박싱

오토박싱은 primitive 타입과 Wrapper 타입을 섞어 쓸 때 자동으로 상호 변환해 주는 기술입니다.
예를 들자면 primitive 타입 혹은 기본 타입인 long을 그에 상응하는 Wrapper 타입인 Long으로 상호 변환해 주는 기술입니다.
오토박싱이 자동으로 적용되기 때문에 구현하는데 불편함은 없지만 오토박싱을 통해 변환하는 과정에서 불필요한 객체가 생성될 수 있으므로 성능에는 소량이나마 영향을 끼칠 수 있습니다.
 
실제로 아래 예시를 보면 auto boxing이 적용되었을 때와 적용되지 않았을 때 성능 차이를 확인할 수 있습니다.
오토박싱이 적용되지 않을 경우 약 14배 빠른 것을 확인할 수 있습니다.


 

정리

문자열, 정규식 패턴, 그리고 오토 박싱에 대해서는 불필요한 객체 생성을 피해야 합니다.
다시 한번 강조하지만 위 세 가지 케이스에 대해서만 객체 생성을 피해야하고 대부분의 상황에서는 객체를 얼마든지 생성해도 됩니다.
요즘 JVM GC가 워낙 최적화가 잘 되어있기 때문에 걱정하지 않아도 됩니다.
 

참고

이펙티브 자바
이펙티브 자바 완벽 공략 1부 - 백기선 강사님

반응형