본문 바로가기
개인 공부/Java (이펙티브 자바)

[이펙티브 자바] 아이템 6 : 불필요한 객체 생성을 피하라

by 희조당 2023. 1. 15.
728x90

🎯 학습 목표

  • Why??
  • String Constant Pool
  • Wrapper 클래스

📌 불필요한 객체 생성 피하기

불필요한 객체 생성에 대한 이야기는 아이템 1에서 잠깐이나마 엿볼 수 있었다.

인스턴스를 캐싱해 두면 불필요한 객체 생성을 피할 수 있다고 했다.

🤔 왜 피해야 할까?

Java를 조금이라도 공부했다면 GC(Gabage Collection)의 존재를 알 것이다.

메모리를 하나하나 관리해야하는 C언어와 다르게 이 GC가 알아서 메모리의 누수를 방지해 주기 때문이다.

하지만 GC이 아무리 좋아져도 무분별한 객체 생성은 성능을 저하시킨다. 즉, 성능 때문에 피해야한다.

👀 언제 그럴까?

책에서 제시하는 예시와 함께 언제 불필요한 객체가 생성되는지 알아보자.

1️⃣ String

String s = new String("문자열");

String 타입도 하나의 객체라서 new 연산자로 새로운 String 객체를 만들 수 있다.

근데 new 연산자는 객체를 생성할 때 메모리(Heap)에 새 공간을 할당받고, 해당 공간의 참조값을 리턴한다.

따라서 위의 코드가 호출될 때마다 새로운 공간을 할당받는 극한의 비효율성을 낳는다.

 

String s = "문자열";

위와 같이 리터럴로 작성하는 게 올바른 객체 생성법이다.

이렇게 작성하는 이유는 또 무엇일까? 결론부터 이야기하면 Java의 String Pool 때문이다.

 

다음 예제를 보면 책에서 valueOf()의 사용을 추천한 이유를 한 번에 알 수 있다.

String s1 = new String("문자열");
String s2 = new String("문자열");
String s3 = "문자열";
String s4 = String.valueOf("문자열");

// 동일성 비교
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s3 == s4);

// 동등성 비교
System.out.println(s1.equals(s2));
System.out.println(s2.equals(s3));
System.out.println(s3.equals(s4));

앞서 말한 String Pool은 JVM 내 Heap 영역에 생성되는 상수의 영역이다.

리터럴로 작성된 String은 내부적으로 intern()을 호출해서 String Pool에 위치한다.

Pool에 자리 잡은 String은 추가적인 공간을 할당받지 않아 동등한 값이라면 같은 참조값을 가리키게 되는 것이다.

2️⃣ Wrapper 클래스

Wrapper 클래스는 Java에서 제공하는 기본 타입을 객체로 표현하기 위해서 만들어졌다.

따라서 각 기본 타입에 대응하는 Wrapper 클래스가 존재하고 기본 타입으로 바꾸는 과정을 언박싱이라고 한다.

private static long sum() {
    Long sum = 0L;
    for (long i = 0; i <= Integer.MAX_VALUE; i++) {
        sum += i;
    }
    return sum;
}

위와 같이 언박싱이 매우 많이 일어나게 되면 sumlong으로 선언했을 때와 극명한 성능 차이를 가진다.

😎 좋은 예

책에서 나온 예시가 대표적인 예시이다.

private static final Pattern ROMAN = Pattern
        .compile("^(?=.*[A-Za-z])(?=.*\\d)(?=.*[@$!%*#?&])[A-Za-z\\d@$!%*#?&]{8,20}$");

static boolean isRomanNumeral(String s) {
    return ROMAN.matcher(s).matches();
}

Pattern은  인스턴스 생성 비용이 높기 때문에 미리 만들어 캐싱해 두고 두고두고 사용하는 방법이 좋다.

그래서 JPA의 EntityManagerFactory와 JDBC의 DB Connection을 한 번만 만들어 재사용하는 것이다.

😋 정리

사실 어떤 객체가 생성 비용이 높은지 다 알 수 없다.

정적 팩토리 메서드를 지원한다면 적극 사용하도록 하고, 객체를 생성할 때는 많이 고민을 해보자!


-Reference

https://blog.naver.com/adamdoha/222817943149

 

😋 지극히 개인적인 블로그지만 훈수와 조언은 제 성장에 도움이 됩니다 😋

댓글