티스토리 뷰
String 사용? 미사용?
String은 메모리에 어떻게 저장되는가? 객체의 값은 변경가능 한가?
-
String 객체 대부분은 원시(Primitive) 타입으로 취급하므로 new 키워드를 사용할 필요가 없다.
- new 키워드를 사용 안 하고 String 리터럴로 표현할 경우 String Constant Pool 영역(힙 영역이 감싸고 있음)에 존재한 후 해당 영역의 값을 가리키게 된다.
- new 키워드를 사용할 경우에는 heap영역에 String 객체를 생성하게 되는 것이다.
-
Java에서 String은 특별한 참조 자료형이다. 다른 객체들과 마찬가지로 new 생성자를 이용해서 인스턴스를 만들고 heap영역에 올라가지만, 다른 참조형과는 다르게 한번 객체가 생성되면 해당 값은 변하지가 않는다.
String str = new String("cat"); str = str+str; //1. str은 cat이라는 문자열을 참조 //2. str+str 연산 진행으로 인해 "catcat" 문자열 만들어짐 //3. 새로운 String 객체 생성 //4. 새로운 String 객체에 "catcat" 삽입 //5. 기존 str 객체는 새로운 str String 객체를 참조
-
이 처럼 String 연산은 계속적으로 객체를 만들어내기 때문에 비효율적이다.
String Constant pool
반복적으로 객체 생성되는 것을 어느정도(같은 값 일때만) 방지하기 위한 새로운 메모리 영역
String s1 = "Cat"; //String 리터럴 방식
String s2 = "Cat"; //String 리터럴 방식
//String Constant pool에 의해 str1과 str2는 같은 인스턴스를 참조하고 있다.
//Cat은 String Constant pool 메모리 영역에 한 부분만 차지하기 때문이다.
String s3 = new String("Cat"); //heap 영역에 할당
s1 == s2; //true
s1 == s3; //false
이 처럼 String 리터럴 추가 시 같은 값 (== true)일 때는 String Constant Pool 영역의 같은 위치에 담아두게 되는 것이다.
intern(인터닝이란)
String str1 = new String("ball");
String str2 = new String("ball");
String str3 = "ball";
//main
System.out.println(str1 == str1.intern()); //false
System.out.println(str3 == str1.intern()); //true
intern()
메소드를 사용하면 Heap영역에 있는 인스턴스를 String Constant pool영역으로 이동한다.- 위 결과를 다시한번 살펴보면,
System.out.println(str1 == str1.intern());
는false
가 나타난다.- 이유는
str1
은 이제 Heap영역이 아닌 String Constatn pool 영역에 있으므로 같은 인스턴스를 참조하지 않는다.
- 이유는
System.out.println(str3 == str1.intern());
는true
가 나타난다.- 위에서 본 바와 같이
str1.intern()
으로 인해str1
은 String Constatn pool로 이동하였으므로str3
과str1
은 같은 인스턴스를 참조하는 것이다.
- 위에서 본 바와 같이
- String 리터럴("")을 이용하여 String 객체를 생성하면 내부에서 intern(); 메소드를 호출한다.
- 인터닝을 사용하면 문자열 비교 시
equals()
보다 속도적인 부분이나 메모리 부분에서 효과를 얻을 수 있다.
String vs String Buffer vs String Builder
String
- String은 immutable(불변)으로서 한번 메모리에 생성되면 변하지 않는다.
- 위에서 살펴본 바와 같이 +연산 또는 concat을 통해 기존에 생성된 String 클래스의 변화를 주어도 해당 클래스는 변하는 것이 아니라 새로운 String 클래스를 생성 후 기존 String 클래스가 새로운 String 클래스를 참조하도록 하는 것이다.
- String이 객체는 문자열 연산이 많은 경우 새로운 String 객체를 생성하므로 성능이 우수하지 못하다.
- String은 짧은 문자열 연산에 사용하면 좋다
- String은 불변으로 인해 Thread-safe가 보장되는데 이유는, 컴파일러에 의해 바이트코드가 JVM에 로드될 시 JVM은 String Constant Pool에 동일한 문자열이 존재하는지 확인 후 존재할 시 재사용하고 없을 시에 새로운 값을 만들기 때문이다.
StringBuffer와 StringBuilder
-
StringBuffer와 StringBuilder 공통점
- String과는 다르게 mutable(변경가능)하다. 즉, 클래스는 한번만 만드므로, 문자열 연산이 많을 시 사용하면 성능이 좋다.
- String과는 다르게 문자열 연산으로 인해 객체 공간이 부족한 경우에는 버퍼 크기를 늘려 유연하게 동작한다.
- 서로 동일한 메소드를 가지고 있다.
-
StringBuffer와 StringBuilder 차이점
- StringBuffer는 메소드별로 Synchronized Keyword가 존재하여 멀티 스레드 환경에서도 동기화를 지원하나, StringBuilder는 동기화를 보장하지 않는다.
- 그로인해, 멀티 스레드 환경이라면 StringBuffer를 사용하고 단일 스레드 환경이라면 StringBuilder를 사용하는 것이 좋다. 단일 스레드에서 StringBuffer를 사용해도 되나 StringBuilder에 비해 성능이 좋지 않다.
- why 성능? StringBuilder는 동기화를 고려하지 않기 때문에 단일 스레드 환경에서는 StringBuffer에 비해 연산처리가 빠른 것이다.
결과적으로,
String은 불변객체이기 때문에 문자열 연산이 많은 프로그래밍이 필요할 시 연속적으로 객체를 생성하므로 성능은 떨어지나, 조회가 많은 환경이나 멀티 스레드 환경에서는 유리하다.
StringBuffer와 StringBuilder는 문자열 연산이 자주 발생할 때 추가적인 객체 생성 없이 문자열 변경이 가능하므로 성능적으로 유리히다.
StringBuffer는 스레드에 안전한 프로그램이 필요할 때나, 개발 중인 시스템이 스레드에 안전한지 모르는 경우에 사용하면 좋다. (Thread-safe)
StringBuilder는 스레드에 안전한지의 여부가 전혀 관계없는 프로그램을 개발할 때 사용하면 좋다.
'Java > Java 기초' 카테고리의 다른 글
[Java 기초] JVM 구조 (0) | 2020.01.24 |
---|---|
[Java 기초] 배열과 리스트의 관계 (0) | 2020.01.14 |
[Java 기초] 추상클래스와 인터페이스 (0) | 2020.01.05 |
[Java 기초] 접근제한자 (0) | 2020.01.04 |
[Java 기초] 상속과 구성 (2) | 2020.01.04 |
- Total
- Today
- Yesterday
- 점층적 생성 패턴
- 연관관계
- effectivejava
- 복사 팩토리
- java8
- 이펙티브자바
- 빌더 패턴
- 스프링부트
- jdk버전
- @Lazy
- springboot
- 생성자
- 팩토리 메소드 패턴
- 이펙티브 자바
- ifPresent
- 빈 순환 참조
- junit
- flatMap
- java
- JPA
- try catch finally
- Effective Java
- package-private
- Spring
- mustache
- 정적팩터리메서드
- 자바8
- 김영한
- 인프런
- try with resources
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |