티스토리 뷰
똑같은 기능의 객체를 매번 생성하기보다는 하나의 객체를 재사용하면 분명히 성능적으로 이점을 얻을 수 있습니다.
예를들면 이전에 봤던 Boolean.valueOf를 생각해보면 됩니다.
좀더 와닿는 설명을 위해 아래와 같은 코드를 작성했습니다. 이 코드는 매우 극단적인 예시를 보여줍니다.
public static void main(String[] args) {
long bef;
long aft;
bef = System.currentTimeMillis();
for(int i=0; i<100000000; i++) {
String str = new String("Hello");
}
aft = System.currentTimeMillis();
long result = aft - bef;
System.out.println("result = " + result);
}
반복문이 실행될 때마다 String 인스턴스를 새로 만들고 있습니다. 매우 불필요한 행동이지요.
이 코드를 개선해보면 아래의 코드와 같이 작성할 수 있습니다.
public static void main(String[] args) {
long bef;
long aft;
bef = System.currentTimeMillis();
for(int i=0; i<100000000; i++) {
String str = "Hello";
}
aft = System.currentTimeMillis();
long result = aft - bef;
System.out.println("result = " + result);
}
위의 두개의 코드는 성능면에서도 꽤나 큰 차이를 보여줍니다. 단순히 실행 시간만 계산했을때, 아래의 코드가 약 5배정도 성능이 뛰어납니다. 이러한 성능의 차이는 무엇인가?를 해결하기 위해서는 Java의 Garbage Collection 동작원리를 이해해야 합니다.
Garbage Collection은 나중에 자세한 포스팅을 하기로 하고, 지금은 매번 인스턴스를 생성함으로써 불필요한 메모리 해제 작업이 필요하다는 정도만 이해하고 넘어가겠습니다.
어떤 객체는 생성 비용이 아주 비싼 것도 있습니다. 당연하게도 비용이 비싼 객체를 지속적으로 생성하면 애플리케이션의 성능은 저하됩니다. 문제는 어떤 인스턴스의 생성 비용이 비싼지 판별할 수 없다는 것이죠.
public class RomaNumber {
public 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 인스턴스가 사용되고 바로 버려져서 곧바로 GC의 대상이 되기에 비효율적입니다.
성능을 개선하기 위해서는 정규표현식을 표현하는 불변인 Pattern 인스턴스를 클래스 초기화 과정에서 직접 생성해두고, 나중에 메서드가 호출될 때마다 이 인스턴스를 재사용하면 됩니다.
public class RomaNumber {
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})$");
public static boolean isRomanNumeral(String s) {
return ROMAN.matcher(s).matches();
}
}
이렇게 불변인 Pattern 인스턴스를 재사용하면 성능을 상당히 끌어올릴 수 있습니다. 눈으로 확인해보기 위해 개선전 코드와 개선후 코드를 100만번 실행했고, 실행시간을 출력하니 아래와 같은 결과를 얻을 수 있었습니다.
// 개선전
result = 3197
// 개선후
result = 377
약 8.5배 정도의 성능 차이가 나는군요.
위에서 살펴본 예시말고, 불필요한 객체를 만들어내는 또 다른 예시로 '오토박싱(auto boxing), 오토 언박싱(auto unboxing)'을 들 수 있습니다.
박싱은 프로그래머가 기본 타입과 박싱된 기본 타입을 섞어 쓸때 자동으로 상호 변환해주는 기술을 말하는데요, 좀더 제대로 개념을 잡고 넘어가겠습니다. 자바에는 아래와 같은 2가지 타입이 존재합니다.
- 기본 타입 : int, long, float, double, boolean
- Wrapper class : Integer, Long, Float, Double, Boolean
// auto boxing
int num1 = 100;
Integer num2 = num1;
// auto unboxing
Integer num3 = new Integer(100);
int num4 = num3;
그리고 기본 타입 -> Wrapper을 박싱, Wrapper -> 기본 타입을 언박싱이라고 부릅니다. JDK 1.5부터 이 연산을 자동으로 처리해주는데요, 이 두가지 박싱은 컴파일러가 처리해주며 내부적으로 구현 되어있습니다.
음.. 박싱과 언방식이 뭔지는 어렵지가 않은데, 굳이? 이 과정이 있어야할까요? Wrapper class를 사용하지 않으면 되는데.
라기엔 Wrapper class 덕분에 기본형을 Object로 만들 수 있다는 이점이 있습니다. 예를들어 제네릭에도 이제 기본형을 대입할 수 있지요. 그래서 Wrapper class가 존재하는 것입니다.
이제 아래의 코드를 보겠습니다. 앞서 언급했듯이 오토 박싱과 언박싱은 내부적으로 컴파일러가 연산을 알아서 해줍니다. 그리고 이 과정에서 불필요한 성능 저하가 일어날 수 있습니다.
public static void main(String[] args) {
long bef;
long aft;
bef = System.currentTimeMillis();
Long sum = 0L;
for (long i=0; i<=10000000; i++) {
sum += i;
}
aft = System.currentTimeMillis();
long result = aft - bef;
System.out.println("result = " + result);
}
위의 코드는 문제가 없습니다. 하지만 성능을 개선할 수 있는 부분이 분명히 존재합니다.
Long sum = 0L;
for (long i=0; i<=10000000; i++) {
sum += i;
}
바로 이부분인데요, for문 안에서 굉장히 많은 일이 일어나고 있습니다.
- sum이 long 타입으로 언박싱됩니다.
- sum에 i를 더합니다.
- sum을 다시 Long 타입으로 박싱합니다.
더하는 연산이 핵심인데, 박싱과 언박싱이 추가되었고 당연히 효율성이 낮아질 수 밖에 없죠. 이를 개선하기 위해서는 박싱된 타입을 기본 타입으로 변경하여 의도하지 않은 오토박싱의 발생을 차단하면 됩니다.
long sum = 0L;
for (long i=0; i<=10000000; i++) {
sum += i;
}
이번 주제가 '객체 생성을 피해라'를 말하는 것은 아닙니다.
대신 객체를 생성할 때, 이 객체가 정말 필요한것인가? 한번쯤 고민해본다면 분명 더 뛰어난 애플리케이션을 만들 수 있을 것입니다.
Ref : 이펙티브 자바 Effective Java 3/E
'Language > Java' 카테고리의 다른 글
[Java] Item 7 : 다 쓴 객체 참조를 해제하라 (1) | 2022.10.19 |
---|---|
[Java] Item 5 : 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (1) | 2022.10.13 |
[Java] Item 4 : 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) | 2022.10.11 |
[Java] Item 3 : private 생성자나 열거 타입으로 싱글톤임을 보증하라 (0) | 2022.10.10 |
[Java] Item 2 : 생성자에 매개변수가 많다면 빌더를 고려하라 (0) | 2022.10.09 |
- Total
- Today
- Yesterday
- spring
- java
- Database
- algorithm
- mmu
- soft delete
- go
- Operating System
- OS
- fiber
- network
- ARP
- effective
- Effective Java
- paging
- GORM
- cs
- 공지
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |