///
Search
💡

item 6. 불필요한 객체 생성을 피해라 Avoid creating unnecessary objects

불필요한 객체 생성이란?

불변객체로 다루어져서 동일한 인스턴스를 또 생성할 필요가 없는 경우가 있다. 예를들어 String 타입은 자바에서 특별하게 다루어져서, 개발자가 의도하지 않는 이상 동일한 문자열에 대해 같은 인스턴스가 활용된다.
String s = new String("bikini"); // DON'T DO THIS! 새로운 객체가 생성된다 String s = "bikini"; // 동일한 객체가 생성된다
Java
복사
이처럼 가능하다면 불필요한 객체 생성을 피하는 것이 좋다. 불변 객체가 아닌 가변 객체 더라도, 사용중에 재사용되지 않을 것이란 걸 안다면 동일 객체를 사용해도 무방하다.
책에서 소개되는 생성이 불필요한 객체는 다음과 같다.
1.
불변 객체, 혹은 상태가 변하지 않을 것임이 분명한 가변 객체
2.
생성 비용이 커서 캐싱의 이점을 누릴 수 있는 객체
3.
어댑터 패턴(뷰라고도 한다)에서 사용하는 어댑터, 어댑터는 뒷단 객체에 대한 연결만 하기 때문에 뒷단 객체가 여러개더라도 굳이 여러 객체가 필요하지 않다.

왜 불필요한 객체 생성을 피해야하는가?

불필요한 객체 생성은 성능에 영향을 줄 수 있기 때문이다. 어떤 객체는 생성비용 자체가 매우 비싸다.

생성 비용이 큰 객체

예를들어 정규표현식으로 검사하는 다음과 같은 로직이 있다고 생각해보자.
// Performance can be greatly improved! static boolean isRomanNumeral(String s) { return s.matches("^(?=.)M*(C[MD]|D?C{0,3})" + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$"); }
Java
복사
Pattern 객체는 컴파일 될 때 유한상태기계를 생성하므로 생성비용이 비싸다.
어플리케이션 전체에서 단 한번 검사한다면 상관 없겠지만, 빈번하게 활용되는 경우 매번 정규표현식 객체에 사용되는 Pattern 객체가 생성, 그리고 GC에 의해 파괴됨으로써 성능에 악영향을 줄 수 있다.
다음처럼 재사용 될 수 있는 객체에 대해선 재사용 함으로써 성능을 개선할 수 있다.
// Reusing expensive object for improved performance public class RomanNumerals { private static final Pattern ROMAN = Pattern.compile( "^(?=.)M*(C[MD]|D?C{0,3})" + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$"); static boolean isRomanNumeral(String s) { return ROMAN.matcher(s).matches(); } }
Java
복사
비즈니스 로직에 의미를 부여할 수 있기 때문에 더 가독성 있는 코드가 되는 효과는 덤이다.

성능.. 더 좋은 성능...!

참고로 static final 필드에 대해서 지연 초기화 전략(Item 83) 을 사용해서 성능을 더욱 끌어올릴 수 있겠지만, 사실상 유의미한 성능상 개선은 없고 코드가 복잡해지는 경우도 많으므로 적용할 땐 고민 좀 해보자.

의도치 않은 오토박싱

이런 예시는 오토박싱에서도 찾아볼 수 있다.
// Hideously slow! Can you spot the object creation? private static long sum() { Long sum = 0L; for (long i = 0; i <= Integer.MAX_VALUE; i++) sum += i; return sum; }
Java
복사
다음은 Long 타입과 long 타입간의 오토박싱이 매우 많이 일어날 것이다. 유의미한 성능 악화가 일어난다. sum을 Long으로 선언했다는 하나의 잘못으로 인해!
의도치 않은 오토박싱을 사용하지 않도록 주의하자.

하지만 객체 생성을 두려워 말라

요즘의 JVM과 GC는 성능이 뛰어나기 때문에 작은 객체를 생성하고 회수하는 비용은 매우 작다. 프로그램의 명확성, 간결성, 기능성을 개선하기 위한 객체 추가는 권장된다. 무의미한 객체 생성을 주의하라는 메시지이다!
아주 무거운 객체가 아니고서야 자체적인 나만의 객체 풀 을 만들지는 말라. 객체 풀 생성 자체가 매우 큰 비용이 된다. 잘 활용된 객체 풀의 예시는 DB Connection Pool이다. 데이터베이스와의 연결 수립비용은 매우 크고, 수립된 커넥션을 재활용 하는 것은 좋은 전략이다. 하지만 나만의 객체 풀을 만들고 유지하는 건 보통 메모리 사용량을 늘려서 성능을 악화하고 코드를 헷갈리게 할(increase memory footprint) 뿐이다.
이 아이템은 방어적 복사를 하라(item 50)과 대치된다. 여기서 얻을 수 있는 교훈은, 기능상 개선 사항이 있는 경우 객체 생성을 두려워하지 말라는 것이다! 불필요한 객체 생성을 하지말라는 말에서 중요한 포인트는 불필요한 이지, 생성을 하지말라 가 아니다.

용어정리

한글명
영어명
어댑터 패턴
adapter pattern
오토박싱
auto boxing
정규표현식
regular expression
지연 초기화
lazily initializing
객체 풀
object pool