2022. 7. 21. 22:10ㆍJava
String
> String은 두 가지 방법으로 값을 할당합니다.
1. 리터럴로 값을 할당
2. new 키워드로 값을 할당
String s1 = "123456"; // 리터럴로 값 할당
String s2 = "123456"; // 리터럴로 값 할당
String s3 = new String("123456"); // new 키워드로 값 할당
String s4 = new String("123456"); // new 키워드로 값 할당
위와 같이 각각 s1, s2, s3, s4변수에 값을 할당하였다고 했을 때 메모리 영역에서는 아래와 같이 나타납니다.
1. 리터럴로 값을 할당하는 경우 힙영역 안 StringPool이라는 공간에 객체가 생성되고 같은 리터럴로 할당한 변수는 같은 공간을 가리킵니다.
2. new 키워드로 값을 할당한 경우 객체가 각각 생성되고 각자 다른 공간을 가리킵니다.
즉, 리터럴로 할당한 s1과 s2는 같은 객체를 가리키고(s1 == s2)
new 키워드로 할당한 s3와 s4는 다른 객체를 가리킵니다. (s3 != s4)
+ 리터럴: 그 자체로 값을 의미하는 것
int a = 3;
char b = 'A';
string abc = "ABC";
위의 코드에서 리터럴은 3, 'A', "ABC"입니다.
단점
> 문자열의 변경이 자주 일어날 시 힙메모리 공간의 부족 현상으로 이이어질 수 있습니다.
String str = new String("123456");
위처럼 String 인스턴스 str이 선언되었다고 합니다.
str은 힙 영역 안, String객체를 참조합니다.
이때, 아래와 같이 str의 값이 변한다고 가정합시다.
str = "안녕안녕";
str = "안녕안녕"
String은 기존의 "123456"의 값이 변경되는 것이 아닌
힙 영역에 새로운 String 객체가 생성되고 이를 str이 참조하는 식으로 진행됩니다.
따라서 기존의 str이 참조 하고 있던 객체는 아무도 참조하는 값이 없어 GC의 대상이 됩니다.
여기서 문제가 발생합니다.
str의 값이 계속 변한다고 가정합시다.
str = "나는 새로운 객체";
str = "사과";
메모리를 살펴보면 기존의 str이 참조했던 객체들이 임시 가비지가 되어 GC가 치워주기를 기다리게 됩니다.
즉, string은 불변객체입니다.
우리가 str = "사과"와 같이 str의 값을 저장하면, 기존의 인스턴스의 값을 변경하는 것이 아닌, 새로운 메모리 영역을 가리키게 됩니다. 즉, 문자열의 추가, 수정, 삭제 등의 연산이 빈번하게 일어날 수록 힙메모리에 임시 가비지가 생성되어 힙 메모리의 부족으로 이뤄질 수 있습니다.
StringBuilder vs StringBuffer
String은 불변객체였기 때문에 문자열의 변경시 매번 새로운 객체를 생성해주어야 했습니다.
반대로 StringBuilder와 StringBuffer는 가변객체입니다. 즉, 기존의 인스턴스 내에서 문자열을 변경하는 것이 가능합니다. 이는 StringBuilder와 StringBuffer가 AbstractStringBuilder라는 추상클래스를 상속받아 구현되었기 때문입니다.
StringBuilder와 StringBuffer는 append()메서드를 이용하여 문자열을 수정하게 됩니다.
AbstractStringBuilder에는 append()메서드가 구현되어 있고
StringBuilder와 StringBuffer는 이를 아래와 같이 override하여 사용합니다.
// StringBuilder
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
//StringBuffer
@Override
public synchronized StringBuffer append(String str) {
toStringCache = null;
super.append(str);
return this;
}
위에서 확인 할 것은 StringBuffer에 synchronized 키워드가 붙어 있다는 점입니다.
이 키워드 덕분에 StringBuffer는 동기화를 지원하여 멀티 스레드 환경에서도 안전하게 동작할 수 있습니다. 반면 StringBuilder는 동기화를 지원하지 않습니다.
다만, 이 키워드 때문에 성능이 다소 느려져 단일 쓰레드 환경에서는 StringBuilder의 성능이 더 우수합니다.
> synchronized 키워드 : 여러개의 스레드가 한 개의 자원에 접근할려고 할 때, 현재 데이터를 사용하고 있는 스레드를 제외하고 나머지 스레드들이 데이터에 접근할 수 없도록 막는 역할을 수행합니다.
> 스레드: 프로세스(process) 내에서 실제로 작업을 수행하는 주체를 의미합니다. (+ 추가적인 공부 필요)
> 프로세스(process): 실행 중인 프로그램(program)을 의미합니다.
'Java' 카테고리의 다른 글
GC (0) | 2022.08.01 |
---|---|
Overloading vs Overriding (0) | 2022.07.22 |
정렬(Sort) (0) | 2022.07.19 |
객체 지향 vs 절차 지향 프로그래밍 (0) | 2022.07.18 |
JVM (0) | 2022.07.18 |