책/Effective Java

불필요한 객체 생성 피하는 방법

728x90
반응형
static boolean isRomanNumeral(String s) {

	return s.matches(" ... ");
 }

# 정적 팩토리 메서드 방법 

  • 똑같은 기능을 제공하는 객체를 반복적으로 생성하는 것보다 객체 하나를 재사용하는 것이 더 효율적 
/*
 실행될 때마다 new 를 통해 새로운 인스턴스 생성 
 - 쓸데없는 String 인스턴스가 계속해서 생성된다. 
*/

			String s = new String("Xonmin");

/*
	하나의 String 인스턴스 생성
    JVM 안에서 이와 똑같은 문자열 리터럴을 사용하는 모든 코드는 같은 객체를 재사용하게 된다. 
*/ 

			String s = "Xonmin";

 

  • 생성자 대신 정적 팩토리 메서드를 통해 불변 클래스에서 불필요한 객체 생성 회피 
    • 가변 객체라고 해도 사용 중에 변경되지 않을 것이라면 재사용 O 
    • ex) Boolean.valueOf(String)  / Boolean(String) 

 

 

 

# 캐싱을 통한 객체 재사용 

생성 비용이 비싼 비효율적인 코드

static boolean isRomanNumeral(String s) {

	return s.matches(" ... ");
 }
  • 생성 비용이 비싼 객체에 효율적 
    • ex ) 정규표현식용 Pattern 인스턴스 -  String.matches
      • Pattern 인스턴스는 표현식에 대해  FSM을 만들기 때문에 생성 비용 ↑
    • 메서드 내부에서 만들경우 한 번 정규표현 문자열 확인 후, GC의 대상으로 

 

개선된 방법 

public class RomanNumerals {

	private static final Pattern ROMAN = Pattern.compile("...");

	static boolean isRomanNumeral(String s) {
		return ROMAN.matcher(s).matches();
    }
 }
클래스 초기화 과정에서 생성 후, 캐싱해두고 
나중에 static final 로 선언되어있기 때문에 호출될 때마다 참조하여 인스턴스를 재사용하자 
- 메서드 명을 통해 코드의 의미를 사용할 때 명확하게 해주는 장점 

 

# 불필요한 인스턴스 만드는 경우 

  • Adapter
실제 작업은 뒷단 객체에 위임하고, 자신은 제 2의 인터페이스 역할을 하는 객체 
어댑터는 뒷단 객체만 관리하면 된다. → 뒷단 객체 외에는 관리할 상태가 없으므로 뒷단 객체 1 : 어댑터 1 

 

Map - keySet()

 

keySet() -  Map 내의 모든 키들을 담은 Set 반환

기존의 생성된 keySet() 을 통해 생성한 Set이 존재하고 이 때 새로운 KeySet()을 호출하면 " 두가지 모두 같은 Set 인스턴스 "

모두가 똑같은 Map 인스턴스를 대변하기 때문에 keySet을 여러개 만들어도 상관없음 하지만 그럴 이유가 없음  

public class KeySetTestCode(){

	public static void main(String[] args){
    	Map<String,String> maps = new HashMap<>();
        maps.put("xonmin","dev");
        maps.put("s","desdv");
        maps.put("sdsd","dddddd")
        maps.put("adasdad","dddddsa");
    	
        
        Set<String> keySet1 = maps.keySet();
        Set<String> keySet2 = maps.keySet();
        
        System.out.println(keySet1.hashCode()); // -1900481726
        System.out.println(keySet1.hashCode()); // -1900481726
    }

}

 

AutoBoxing (오토 박싱) 

 

기본 타입과 박싱된 기본 타입을 섞어 쓸 때 자동으로 상호 변환해주는 기술 
오토박싱은 기본 타입과 그에 매핑되는 박싱된 기본 타입의 구분을 흐리지만, 완전히 없애지 못한다. - 성능 문제 야기 가능성 

 

# ex )

private static long sum(){
	Long sum = 0L;
    for (long i = 0; i<=Integeer.MAX_VALUE; i++){
    	sum += i;
     }
     return sum;   
}
  • Long 타입으로 선언하여 2^31 개의 인스턴스 생성 - long 타입이 i 가  wrapper 타입인 Long sum에 더해질 때마다
박싱된 기본 타입보다는 기본 타입을 사용하고 의도치 않은 오토박싱이 없도록 주의하자 

 

# 함께 고려해야할 사항 

  • 프로그램의 명확성과 간결성, 기능을 위해서는 객체를 추가로 생성하는 것은 오히려 좋은 일 
  • JVM 에서 작은 객체를 생성하고 회수하는 일은 부하가 크지 않다. 
  • 이후에 나올 "새로운 객체를 만들어야 한다면 기존 객체를 재사용하지 마라"(Item 50) 과 대조적이지만, 방어적 복사(defensive copy)가 필요한 상황에서는 객체를 재사용했을 때의 피해가 필요 없는 객체를 반복 생성하는 것보다 피해가 훨씬 크기 때문에 이를 명심해야한다. 
  • 객체 생성은 코드 형태와 성능에만 영향을 미치지만  방어적 복사에 실패한다면 버그와 보안적 이슈로 이어지는 대참사로 이어지기 때문이다. 
728x90
반응형

' > Effective Java' 카테고리의 다른 글

equals 재정의  (0) 2022.01.16
다 쓴 객체 참조 해제하기  (0) 2022.01.10
다중 instance 방지 & 의존성 주입[Dependency Injection]  (0) 2022.01.04
Singleton 보장 방법  (0) 2022.01.03
Builder 패턴  (0) 2022.01.03