[Effective Java] Item6. 불필요한 객체 생성을 피하라

최강일·2024년 4월 6일
0

Effective Java

목록 보기
5/9

똑같은 기능의 객체를 매번 생성하기보단 객체 하나를 재사용하는 편이 낫다.
그런데 이때 객체 생성이 너무 빈번하고 불필요하게 이루어지면 문제가 될 수 있다.

문제 예시

String s = new String("bikini");

위 코드는 하지 말아야 할 극단적인 예다.
실행될 때마다 String 인스턴스를 새로 만든다.
생성자에 넘겨진 "bikini" 자체가 이 생성자로 만들어내려는 String과 기능적으로 완전히 똑같다.
이런 코드가 무수히 반복되어 호출되면 수백만 개의 인스턴스가 만들어진다.

개선된 버전을 보자.

String s = "bikini";

이 코드는 새로운 인스턴스를 매번 만드는 대신 하나의 String 인스턴스를 사용한다.
즉, 필요에 따라 기존의 객체를 반환함으로써 불필요한 객체 생성을 막을 수 있다.

다시 예를 들어보자.

public class RomanNumerals {

    static boolean isRomanNumeralSlow(String s) {
        return s.matches("^(?=.)M*(C[MD]|D?C{0,3})"
                + "(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
    }

위의 예시는, 입력으로 주어진 문자열이 유효한 로마 숫자인지를 판별한다.
문제점을 찾을 수 없을 만큼 짧은 코드이지만, 문제가 잠재적으로 존재한다.

반환값에서 사용되고 있는 match 메서드가 너무 비효율적이기 때문이다.

  • 위에서 match 메서드는 내부적으로 Pattern 인스턴스를 생성한다.
  • Pattern은 문자열에 대한 판별용으로만 잠시 사용되고 버려진다.
  • Pattern은 생성비용이 매우 높다.

그러므로 값비싼 객체를 재사용해 성능을 개선한다.

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 isRomanNumeralFast(String s) {
        return ROMAN.matcher(s).matches();
    }

위의 예시에서 문제가 됬던 Pattern 인스턴스를 처음부터 선언 해두었다.
Pattern 인스턴스는 매번 달라지는것 없이 같은 모양이므로, 아예 처음부터 생성해놓고 그것을 재사용하도록 만든것이다.

위와 같이 함으로써,성능은 훨씬 좋아졌고 코드 또한 매우 명확해졌다.

불필요한 객체 생성 또다른 예제(오토박싱)

오토박싱이란 기본 타입(int 등)을 Wrapper 클래스의 객체 (Integer)로 변환하는 것이다.

Integer a = 100;

Integer는 Wrapper 클래스이므로,오토박싱이 일어나 new Integer(100) 으로 객체를 생성해준다.
문제는 이러한 오토박싱 기능이 불필요한 객체 생성을 발생시킬 때가 있다는 것이다.

public class Sum {
    private static long sum() {
        Long sum = 0L;

        for (long i = 0; i <= Integer.MAX_VALUE; i++)
            sum += i;
        return sum;
   
}

위에서 sum 변수를 long이 아닌 Long으로 선언한 것이 문제가 된다.
저렇게 될 경우 매순간 객체를 새롭게 생성하게 된다.
완벽하게 비효율적이고 자원을 낭비하는 코드이다.

그러므로, Wrapper 클래스보다는 기본 타입을 사용하고, 의도치 않은 오토박싱이 숨어들지 않도록 해야한다.

마치며

지금까지 불필요한 객체 생성을 피하는 것에 대해서 알아봤다.

다만 여기서 주의해야 할 것은,
"객체 생성은 비싸니까 피해야 한다." 라는 말이 아니라는 것이다.

요새 나오는 JVM의 경우 성능이 좋기 때문에,
프로그램의 명확성, 간결성, 기능을 위해서 객체를 생성하는 것은 오히려 좋다.

또, 이번 장이 "기본 객체를 재사용해야 한다면 새로운 객체를 만들지 마라" 인데,
다른 장은 "새로운 객체를 만들어야 한다면 기존 객체를 재사용하지 마라" 이다.

쉽게 말해 절대적인게 없다는 것이다.
상황에 맞게 판단하여 사용하자~

profile
Search & Backend Engineer

0개의 댓글