Dev Book

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

Jemlog 2022. 5. 19. 01:01

똑같은 기능을 하는 객체는 매번 생성하기 보다 객체 하나를 재활용 하는 방법을 선택하자.

 

객체 재활용의 대표적인 예로 String 클래스가 있다.

String 클래스의 문자열 생성 과정을 살펴보자.

String s = "java" // String constant pool을 사용하는 방법
String s2 = new String("java") // 일반적인 객체 생성을 사용하는 방법

위의 리터럴(" ")을 사용하는 방법은 String의 intern() 메서드를 사용해 String constant pool 내부의 문자열 객체를 재활용 한다. 

 

반면 아래의 객체 생성 방법은 Heap에 문자열 객체를 생성한다. 

String s1 = new String("java")
String s2 = new String("java")

위의 코드의 두 문자열은 단어가 같지만 Heap 안에 다른 객체로 만들어진다. 결국 위의 코드가 반복문 내에서 계속 호출된다면 Heap 내에는 무수히 많은 문자열 객체가 생성된다. 

 

객체의 재활용 사례로 Map 인터페이스의 keySet 메서드가 있다. 

책에는 "keySet 메서드를 통해 매번 같은 Set 인스턴스를 만들어낼지도 모른다." 라고 명시되어 있다. 코드로 직접 확인해보자.

HashMap<Integer,String> map = new HashMap<>();
map.put(1,"사과");
map.put(2,"바나나");
map.put(3,"포도");

Set<Integer> set1 = map.keySet();
Set<Integer> set2 = map.keySet();
System.out.println(set1 == set2); // true

위의 코드에서 set1과 set2의 주소값을 == 연산자로 비교해봤을때 true가 나온다. 즉 둘은 같은 객체를 가리키고 있다.

이처럼 항상 같은 데이터를 보여주는 객체는 내부적으로 재활용을 해서 사용하는 경우가 많다. 

 

또한 불필요한 객체가 만들어질 수 있는 케이스로는 오토 박싱(Auto Boxing)이 있다. 

Long sum = 0L;
for(long i = 0; i < Integer.MAX_VALUE; i++)
	sum += i; // 이 부분에서 Long 타입인 sum에 long 타입인 i를 더하기 위해 오토박싱이 일어남

위의 코드대로라면 for문에 돌때마다 새로운 Long 객체를 만들어줘야 한다. 

따라서 primitive type으로 맞춰서 의도치 않게 오토 박싱이 일어나지 않도록 해야 한다.