일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- hashcode
- stream
- MSA
- Test
- 조합
- select_type
- SQL
- jwt
- 필드 주입
- 생성자 주입
- DI
- StringBuilder
- 테스트 코드
- DDL
- 인덱스
- java
- AOP
- docker
- redis
- jpa
- KEVISS
- 열 속성
- VUE
- cache
- 재정의
- lambda
- equals
- Spring
- 바이너리 카운팅
- static
- Today
- Total
백엔드 개발자 블로그
String, StringBuffer, StringBuilder 본문
String, StringBuffer, StringBuilder 차이를 알아보자
String
- String 인스턴스는 String Constant Pool 영역에 저장됩니다.
- Java 8 이전 : Permgen 영역에 할당되어 Method area(상수형 데이터 저장되는 곳)에 위치합니다.
- Java 8 이후 : Permgen 영역이 제거되고 Metaspace 영역으로 변경되면서 Heap 영역으로 이동하여 GC가 관리될 수 있도록 개선되었습니다.
new String()과 리터러("")의 차이
- new String()은 Heap 메모리 영역에 저장된다.
- ""는 Heap안에 있는 String Constant Pool 영역에 저장된다.
String 클래스의 구현 코드를 살펴보자
String 인스턴스는 immutable 합니다
String은 final 클래스로 선언되어 있으며 클래스내의 문자열도 final로 선언되어 있기 때문이다.
/**
* Strings are objects which represent immutable arrays of characters.
*
* @author OTI
* @version initial
*
* @see StringBuffer
*/
public final class String implements Serializable, Comparable<String>, CharSequence
{
...
private final char[] value;
private final int count;
private int hashCode;
...
/**
* Creates a string that is a copy of another string
*
* @param string
* the String to copy
*/
public String(String string) {
value = string.value;
count = string.count;
hashCode = string.hashCode;
}
}
문제점 : 문자열을 자주 조합할 경우
문자열을 조합할 경우, 새로운 인스턴스가 생성됩니다.
value가 final이니 인스턴스의 value를 수정할 수 없기 때문입니다.
새로운 인스턴스는 힙 메모리에 할당되므로 빈번한 문자열 조합은 가비지 컬렉터의 부하가 발생할 수 있습니다.
/*
* Creates a string that is s1 + s2.
*/
private String(String s1, String s2) {
...
value = new char[concatlen];
count = concatlen;
System.arraycopy(s1.value, 0, value, 0, s1len);
System.arraycopy(s2.value, 0, value, s1len, s2len);
}
String Optimization
Java 1.5 이상부터는 String 인스턴스에 문자열을 '+' 할 경우에 컴파일러가 StringBuilder로 변환해주는 것을 확인할 수 있습니다.
그렇다면 아래와 같이 코드를 컴파일 할 경우에 어떻게 최적화 될지 확인해 봅시다.
public static void main(String[] args) {
// case 1. 두 인스턴스 문자열 조합
String str1 = "test";
String str2 = "test";
String str3 = str1 + str2;
// case 2. 문자열 인스턴스에 int 추가
String str4 = "";
str4 += 0;
str4 += 1;
str4 += 2;
// case 3. 문자열 인스턴스에 for loop를 돌면서 문자열 추가
String str5 = "123";
for(int i = 0; i < 100; ++i) {
str5 = str5 + "456";
}
}
다음은 JAD로 컴파일한 파일을 디컴파일한 결과입니다.
String 인스턴스들을 조합할 경우 StringBuilder로 변환하여 append()를 호출하는 것을 확인할 수 있습니다.
그러나 루프문에서는 루프 수 만큼 StringBuilder 인스턴스가 생성되었기 때문에 주의해야합니다.
public static void main(String args[])
{
// case 1. 두 인스턴스 문자열 조합
String str1 = "test";
String str2 = "test";
String str3 = (new StringBuilder()).append(str1).append(str2).toString();
// case 2. 문자열 인스턴스에 int 추가
String str4 = "";
str4 = (new StringBuilder()).append(str4).append(0).toString();
str4 = (new StringBuilder()).append(str4).append(1).toString();
str4 = (new StringBuilder()).append(str4).append(2).toString();
// case 3. 문자열 인스턴스에 for loop를 돌면서 문자열 추가
String str5 = "123";
for(int i = 0; i < 100; i++)
str5 = (new StringBuilder()).append(str5).append("456").toString();
}
정리
String은 immutable한 특징 때문에 문자열을 자주 조합할 시 힙 메모리에 자주 할당되어 가비지 컬렉터의 부하가 발생할 수 있습니다.
Java 5 이상부터는 String 인스턴스에 새로운 문자열을 조합할 경우 컴파일러가 최적화하여 StringBuilder로 변환해주지만 StringBuilder 또한 새로 생성되는 인스턴스이기 때문에 성능을 고려하며 코드를 작성해야 합니다.
StringBuilder
- StringBuilder는 힙 메모리에 생성됩니다.
- 가변적이고, Thread Safe 하지 않습니다.
- Thread safe를 고려하지 않으므로 성능 상 가장 우수합니다.
- 싱글 쓰레드 로직이나 쓰레드간 String을 공유하지 않는다면 StringBuilder를 사용합시다.
public final class StringBuilder extends AbstractStringBuilder implements Serializable, CharSequence, Appendable {
...
private transient char[] value;
...
}
StringBuffer
- StringBuffer 또한 힙 메모리에 생성됩니다.
- 가변적이지만 Thread Safe 합니다.
Thread safe를 어떻게 구현하였을까?
StringBuffer의 경우에는 append 메소드에 synchronized 를 붙여주어 해당 로직에 lock을 걸어 동기화를 보장합니다. synchronized 키워드는 가장 안전하게 쓰레드 동기화를 보장해주지만 lock을 거는 만큼 성능상 이슈가 없는지 고려해야합니다.
/**
* Adds the character array to the end of this StringBuffer.
*
* @param chars the character array
* @return this StringBuffer
*
* @exception NullPointerException when chars is null
*/
public synchronized StringBuffer append (char[] chars) {
int currentLength = lengthInternalUnsynchronized();
int currentCapacity = capacityInternal();
...
}
총정리
String : 불변
StringBuilder : 가변, 문자열 조합 시 성능 가장 우수함, 동기화 지원 안함
StringBuffer : 가변, 문자열 조합 시 두번째로 우수함, 동기화 지원
참고
'Java' 카테고리의 다른 글
스택 오버 플로우(SOF) (0) | 2024.04.18 |
---|---|
JDK 버전 고려해보기 (0) | 2024.04.17 |
[Effective Java] Item15. 클래스와 멤버의 접근 권한을 최소화하라 (0) | 2024.04.08 |
[Effective Java] Item14. Comparable을 구현할지 고려하라 (0) | 2024.04.07 |
[Effective Java] Item13. clone 재정의는 주의해서 진행하라 (0) | 2024.04.05 |