-
아이템6. 불필요한 객체 생성을 피하라Java/이펙티브 자바 2022. 11. 10. 17:39
핵심정리
- 문자열
ㆍ사실상 동일한 객체라서 매번 새로 만들 필요가 없다
ㆍnew String("자바") 을 사용하지 않고 문자열 리터럴("자바")을 사용해 기존에 동일한 문자열을 재사용하는 것이 좋다
○ JVM 은 pool에서 캐싱하고 있기에 new String을 한다면 강제적으로 새로운 문자열을 만드는 것이다.
public class Strings { public static void main(String[] args) { String hello = "hello"; // 이 방법 권장 X String hello2 = new String("hello"); String hello3 = "hello"; System.out.println(hello == hello2); // false System.out.println(hello.equals(hello2)); // true System.out.println(hello == hello3); // true System.out.println(hello.equals(hello3)); // true } }
- 정규식, Pattern
ㆍ생성 비용이 비싼 객체라서 반복해서 생성하기 보다, 캐싱하여 재사용하는 것이 좋다.
// 값비싼 객체를 재사용해 성능을 개선한다. (32쪽) public class RomanNumerals { // 코드 6-1 성능을 훨씬 더 끌어올릴 수 있다! static boolean isRomanNumeralSlow(String s) { return s.matches("^(?=.)M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$"); } // 코드 6-2 값비싼 객체를 재사용해 성능을 개선한다. 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})$"); static boolean isRomanNumeralFast(String s) { return ROMAN.matcher(s).matches(); } public static void main(String[] args) { boolean result = false; long start = System.nanoTime(); for (int j = 0; j < 100; j++) { // 성능 차이를 확인할려면 Slow -> Fast 메서드 변경 result = isRomanNumeralSlow("MCMLXXVI"); } long end = System.nanoTime(); System.out.println(end - start); System.out.println(result); } }
- 오토박싱(auto boxing)
ㆍ기본 타입(int)을 그게 상응하는 박싱된 기본 타입(Integer)으로 상호 변환해주는 기술.
ㆍprimitive -> wrapper : auto boxing / wrapper -> primitive : unboxing
ㆍ기본 타입과 박싱된 기본 타입을 섞어서 사용하면 변환하는 과정에서 불필요한 객체가 생성될 수 있다.
public class Sum { private static long sum() { Long sum = 0L; // 6초 long sum = 0L; // 1초 for (long i = 0; i <= Integer.MAX_VALUE; i++) { sum += i; } return sum; } public static void main(String[] args) { long start = System.nanoTime(); long x = sum(); long end = System.nanoTime(); System.out.println((end - start) / 1_000_000. + " ms."); System.out.println(x); } }
- "객체 생성은 비싸니 피하라."는 뜻으로 오해하면 안된다.
완벽 공략
- p31, 사용 자제 API(Deprecation)
- p32, 정규 표현식
- p32, 한 번 쓰고 버려져서 가비지 컬렉션 대상이 된다.
- p33, 초기화 지연 기법(아이템 83에서 다룸)
ㆍ진짜 가끔 사용되는 필드에 사용
- p34, 방어적 복사(아이템 50에서 다룸)
ㆍ새로운 객체를 만들때 기존 객체를 복사해서 만들지 마라
완벽 공략 16 - Deprecation
: 클라이언트가 사용하지 않길 바라는 코드가 있다면...
- 사용 자제를 권장하고 대안을 제시하는 방법이 있다.
- @Deprecated
ㆍ컴파일시 경고 메시지를 통해 사용 자제를 권장하는 API라는 것을 클라이언트에 알려줄 수 있다.
- @deprecated
ㆍ문서화(Javadoc)에 사용해, 왜 해당 API 사용을 지양하며, 그 대신 권장하는 API가 어떤 것인지 표기할 수 있다.
public class Deprecation { /** * @deprecated in favor of * {@link #Deprecation(String)} */ @Deprecated(forRemoval = true, since = "1.2") public Deprecation() { } private String name; public Deprecation(String name) { this.name = name; } }
완벽 공략 17 - 정규 표현식
: 내부적으로 Pattern이 쓰이는 곳
- String.match(String regex)
- String.split(String regex)
ㆍ대안, Pattern.compile(regex).split(str)
- String.replace*(String regex, String replacement)
ㆍ대안, Pattencompile(regex).matcher(str).replaceAll(repl)
- 과제) 자바 정규식 Pattern 문법 학습하기
- 참고 1) https://docs.oracle.com/javase/tutorial/essential/regex
- 참고 2) https://regex101.com 또는 https://regexr.com/
- split의 one-char String 는 빠르다. 컴파일하지 않아도 빠르게 처리된다.
- split도 여러 char를 가지고 할 때는 pattern을 미리 만드는 것이 좋다.
public class RegularExpression { private static final Pattern SPLIT_PATTERN = Pattern.compile(","); public static void main(String[] args) { long start = System.nanoTime(); for (int j = 0; j < 10000; j++) { String name = "keesun,whiteship"; name.split(","); // SPLIT_PATTERN.split(name); } System.out.println(System.nanoTime() - start); } } // match, split, replaceAll, replaceFirst
완벽 공략 18 - 가비지 컬렉션
- Mark, Sweep, Compact
ㆍMark : 오브젝트가 참조를 가지고 있는지 없는지 체크, 사라져도 되는 인스턴스인지 체크
ㆍSweep : 필요없는 오브젝트를 메모리 힙에서 날리는 것
ㆍCompact : 중간에 비워져있는 오브젝트 파편들을 없애고 모아주는 과정
- Young Generation (Eden, S0, S1), Old Generation
ㆍYoung Generation : 금방 사라지는 객체들을 관리
ㆍOld Generation : 오래 살아남는 객체들을 관리
- Minor GC, Full GC
ㆍMinor GC : Young Generation에서 일어나는 GC
ㆍFull GC : Young Generation과 Old Generation 동시에 일어나는 GC
- Throughput, Latency (Stop-The-World), Footprint - 3가지 관점에서 GC 종류들을 봐야한다.
ㆍThroughput : 애플리케이션 처리양, 내 서버의 리소스를 얼마만큼 사용하는가?
ㆍLatency : GC의 Compact하는 과정에 멈춤, 즉, GC만 일을 한다. GC를 하는 동안에는 요청처리가 되지 않는다.
Serial과 Parallel은 Latency 관점에서는 좋은 GC는 아니다.
ㆍFootprint : GC 알고리즘을 수행함으로써 얼마나 많은 메모리 공간을 필요로 하는가?
* 3가지 중 Latency가 가장 중요하다. 응답시간에 영향을 끼칠 수 있기 때문에..가장 짧은 Latency를 선택
- Serial, Parallel, CMS, G1, ZGC, Shenandoah
ㆍSerial
ㆍParallel : 자바8의 기본 GC, Serial GC에 비해 스레드를 더 쓰는 것
ㆍCMS : 자바9부터 deprecated가 됨, 13/14부터는 사라짐
ㆍG1 : 자바11의 기본 GC
ㆍZGC
ㆍShenandoah
- 참고) How to choose the best Java garbage collection?
가비지 컬렉션을 알기 위한 것들
1. 가비지 컬렉션 기본 개념
2. Optional
3. tools
'Java > 이펙티브 자바' 카테고리의 다른 글
아이템8. finalizer와 cleaner 사용을 피하라 (0) 2023.06.04 아이템7. 다 쓴 객체 참조를 해제하라. (0) 2023.06.01 아이템5. 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라 (0) 2022.11.01 아이템4. 인스턴스화를 막으려거든 private 생성자를 사용하라 (0) 2022.11.01 아이템3. 생성자나 열거 타입으로 싱글턴임을 보증하라 (0) 2022.10.31