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

- 똑같은 기능의 객체를 매번 생성하기보다는 객체 하나를 재사용하는 편이 나을 때가 많다.
- 특히 불변 객체는 언제든 재사용할 수 있다.

## String 인스턴스에 대하여

```java
String s=new String("bikini"); // 따라 하지 말 것!
```

- 위 문장은 실행될 때마다 String 인스턴스를 새로 만든다.
- new 키워드는 이전에 동일한 값이 사용되었는지 여부에 관계없이 항상 새 인스턴스를 생성하도록 한다.

```java
String s="bikini";
```

- 이 코드는 새로운 인스턴스를 매번 만드는 대신 하나의 String 인스턴스를
  사용한다([String Constant Pool in Java](https://www.geeksforgeeks.org/string-constant-pool-in-java/) 참고).
- 자바는 문자열을 직접 할당할 시 문자열 상수 풀 내부에 모든 값을 저장한다.
- 문자열 상수 풀은 힙 내에 있는 작은 캐시이다.

![image](https://user-images.githubusercontent.com/68052095/125787075-a10689e8-4f51-47f4-9726-1051e34cf460.png)

- 이 방식을 사용한다면 같은 가상 머신 안에서 이와 똑같은 문자열 리터럴을 사용하는 모든 코드가 같은 객체를 재사용함이 보장된다.

## 정적 팩터리를 제공하는 불변 클래스

- 정적 팩터리 메서드를 사용해 불필요한 객체 생성을 피할 수 있다.
- Boolean(String) 생성자 대신 Boolean.valueOf(String) 팩터리 메서드를 사용하는 것이 좋다.
- 위 생성자는 자바 9에서 사용 자제(deprecated) API로 지정되었다.
- 생성자는 호출할 때마다 새로운 객체를 만들지만, 팩터리 메서드는 전혀 그렇지 않다.
- 불변 객체가 아닌 가변 객체라 해도 사용 중에 변경되지 않을 것임을 안다면 재사용할 수 있다.

## 비싼 객체와 캐싱

- 생성 비용이 아주 비싼 객체도 더러 있다. 이런 '비싼 객체'가 반복해서 필요하다면 캐싱하여 재사용할 수 있다.

### 성능을 훨씬 더 끌어올릴 수 있다!

```java
public class RomanNumerals {
    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})");
    }
}
```

- 위 방식의 문제는 String.matches 메서드를 사용한다는 데 있다.
- **String.matches는 정규표현식으로 문자열 형태를 확인하는 가장 쉬운 방법이지만, 성능이 중요한 상황에서 반복해 사용하기엔 적합하지 않다.**
- 이 메서드가 만드는 정규표현식용 Pattern 인스턴스는, 한 번 쓰고 버려져서 곧바로 가비지 컬렉션 대상이 된다.
- Pattern은 입력받은 정규표현식에 해당하는 유한 상태 머신(finite state machine)을 만들기 때문에 인스턴스 생성 비용이 높다.

### 값비싼 객체를 재사용해 성능을 개선한다.

```java
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();
    }
}
```

- 성능을 개선하려면 필요한 정규표현식을 표현하는 (불변인) Pattern 인스턴스를 클래스 초기화(정적 초기화) 과정에서 직접 생성해 캐싱해두고, 나중에 이 인스턴스를 재사용한다.
- 추가로, 개선 전에서는 존재조차 몰랐던 Pattern 인스턴스를 static final 필드로 끄집어내고 이름을 지어주어 코드의 의미가 훨씬 잘 드러난다.
- 지연 초기화(lazy initialization)로 불필요한 초기화를 없앨 수는 있지만, 권하지는 않는다. 코드는 복잡하게 만들지만 성능은 크게 개선되지 않을 때가 많기 때문이다.

## 오토박싱(auto boxing)

- 오토박싱은 프로그래머가 기본 타입과 박싱된 기본 타입을 섞어 쓸 때 자동으로 상호 변환해주는 기술이다.
- **오토박싱은 기본 타입과 그에 대응하는 박싱된 기본 타입의 구분을 흐려주지만, 완전히 없애주는 것은 아니다.**
- 의미상으로는 별다를 것 없지만 성능에서는 그렇지 않다.

```java
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으로 선언해서 불필요한 Long 인스턴스가 약 231개나 만들어진다(long 타입인 i가 Long 타입인 sum에 더해질 때마다).
- **박싱된 기본 타입보다는 기본 타입을 사용하고, 의도치 않은 오토박싱이 숨어들지 않도록 주의하자.**
- "객체 생성은 비싸니 피해야 한다"로 오해하지 말자. 프로그램의 명확성, 간결성, 기능을 위해서 객체를 추가로 생성하는 것은 일반적으로 좋은 일이다.

## 아주 무거운 객체가 아닌 다음에야 단순히 객체 생성을 피하고자 객체 풀(pool)을 만들지는 말자.

- 객체 풀을 만드는 게 나은 예가 있긴 하다.
- 데이터베이스 연결 같은 경우 생성 비용이 워낙 비싸니 재사용하는 편이 낫다.
- 하지만 일반적으로는 자체 객체 풀은 코드를 헷갈리게 만들고 메모리 사용량을 늘리고 성능을 떨어뜨린다.
- 요즘 JVM의 가비지 컬렉터는 상당히 잘 최적화 되어서 가벼운 객체용을 다룰 때는 직접 만든 객체 풀보다 훨씬 빠르다.

## 추가 정리

- "기존 객체를 재사용해야 한다면 새로운 객체를 만들지 말자."
- "새로운 객체를 만들어야 한다면 기존 객체를 재사용하지 말자."
- 방어적 복사가 필요한 상황에서 객체를 재사용했을 때의 피해가, 필요 없는 객체를 반복 생성했을 때의 피해보다 훨씬 크다.
- 방어적 복사에 실패하면 언제 터져 나올지 모르는 버그와 보안 구멍으로 이어지지만, 불필요한 객체 생성은 코드 형태와 성능에만 영향을 준다.

