Effective Java | Item 6. 불필요한 객체 생성을 피해라
Effective Java 3/E 판을 읽고 정리한 기록입니다.
불필요한 객체 생성
불필요한 객체 생성이란, 이미 존재하는 객체를 재사용할 수 있는 상황에서 새로운 객체를 생성하거나,
불필요하게 많은 객체를 생성하는 것을 의미합니다.
예시
반복적으로 동일한 객체 생성
매번 동일한 값의 객체를 새로 생성하는 대신,
동일한 객체를 재사용할 수 있는 상황에서 객체를 계속 생성하는 것
// 불필요한 객체 생성
String str1 = new String("Hello");
String str2 = new String("Hello");
// 객체 재사용
String str3 = "Hello";
String str4 = "Hello";
불필요한 오토박싱과 언박싱
기본형 데이터 타입을 사용하는 것이 더 효율적인 상황에서, 래퍼 클래스 객체를 불필요하게 생성하는 것
// 불필요한 오토박싱
Integer sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
// 기본형 데이터 타입 사용
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
불변 객체의 비효율적 사용
불변 객체를 반복적으로 생성하는 대신, 재사용 가능한 객체를 활용하지 않는 것.
// 불필요한 객체 생성
Boolean bool1 = new Boolean(true);
Boolean bool2 = new Boolean(true);
// 객체 재사용
Boolean bool1 = Boolean.TRUE;
Boolean bool2 = Boolean.TRUE;
객체 풀링을 활용하지 않는 경우
빈번하게 생성되고 소멸되는 객체를 관리하기 위해 객체 풀링을 사용할 수 있는 상황에서, 이를 활용하지 않는 것
// 객체 풀링을 사용하지 않는 경우
for (int i = 0; i < 1000; i++) {
Connection conn = new Connection();
// ...
conn.close();
}
// 객체 풀링 사용
ConnectionPool pool = new ConnectionPool();
for (int i = 0; i < 1000; i++) {
Connection conn = pool.getConnection();
// ...
pool.releaseConnection(conn);
}
자바에서 객체 생성의 비용
자바에서 객체 생성의 비용은 여러 측면에서 고려할 수 있으며,
이는 성능 최적화와 메모리 관리를 위해 중요한 요소입니다.
객체 생성의 비용은 주로 다음과 같은 요소들로 구성됩니다.
1. 메모리 할당 비용
새로운 객체를 생성할 때마다 메모리가 할당됩니다.
이는 힙(heap) 메모리에 공간을 확보하는 과정을 포함하며, 큰 객체일수록 더 많은 메모리가 필요합니다.
메모리 할당은 메모리 관리자가 힙 공간에서 적절한 크기의 빈 공간을 찾아 할당하는 과정이기 때문에 시간과 자원을 소비합니다.
2. 초기화 비용
객체가 생성되면 생성자(Constructor)가 호출되어 객체의 초기 상태를 설정합니다.
이 과정에서 멤버 변수들이 초기화되고, 필요한 설정 작업이 이루어집니다.
복잡한 객체일수록 초기화 과정도 복잡해지고, 그에 따른 비용도 증가합니다.
가비지 컬렉션 비용
자바는 가비지 컬렉터(Garbage Collector)를 통해 더이상 사용되지 않는 객체를 자동으로 정리합니다.
불필요하게 많은 객체를 생성하면 가비지 컬렉션이 더 자주 발생하여, 이는 애플리케이션 성능에 부정적인 영향을 미칠 수 있습니다.
가비지 컬렉션은 CPU 시간을 소모하며, 특히 큰 힙을 가진 애플리케이션에서는 일시적인 성능 저하를 유발할 수 있습니다.
캐시 효율성 저하
객체를 많이 생성하면 CPU 캐시 효율성이 떨어질 수 있습니다. 자주 사용되는 데이터가 캐시에 적재되지 않고 힙 메모리에만 존재하게 되면, 메모리 접근 시간이 길어져 전체 성능이 저하될 수 있습니다. 이는 특히 대규모 데이터 처리 애플리케이션에서 중요한 문제입니다.
위와같이 불필요한 객체 생성은 객체 생성과 가비지 컬렉션을 많이 사용하게 되면 CPU 사이클을 소모하기에 성능에 문제가 될 수 있고,
불필요한 객체는 메모리를 낭비하며 코드의 가독성과 유지보수성이 떨어지기 때문입니다.
불필요한 객체 생성을 피하는 방법
1. 재사용 가능한 객체 사용
String 리터럴의 재사용, 불변 객체의 활용
public class StringExample {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "Hello";
String s3 = new String("Hello");
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
}
}
new String() 은 String인스턴스를 새로 만들기 때문에 절대로 따라하지 않아야한다. -> String과 중복 기능
2. 팩토리 메서드 패턴 활용
Boolean.valueOf(boolean) 메서드 사용
public class BooleanExample {
public static void main(String[] args) {
Boolean b1 = Boolean.valueOf(true);
Boolean b2 = Boolean.valueOf(true);
System.out.println(b1 == b2); // true
}
}