hong's android

[Java] java.lang.String 클래스에 대해서 본문

Develop/Java

[Java] java.lang.String 클래스에 대해서

_hong 2022. 12. 20. 17:58

String은 불변이다.

String은 변하지 않는다. 계속 값을 더하게 되면 값이 변경되어서 변경되는 것이라고 착각할 수 있지만 내부적으로 기존 객체는 쓰레기가 되고 계속 새로운 객체를 만들어서 참조를 변경해 준다.

 

Java string은 왜 불변일까?

 

1. 메모리를 절약할 수 있는 String Constant pool을 사용하기 위해

만약 변환될 수 있는 상태가 되면 Constant pool에 참조하고 있는 두 string이 있다고 했을 때 그중 하나의 값을 변경했는데 똑같은 참조를 하고 있는 상태가 된다. 

 

2. thread safe 하기 위해

멀티 스레드가 공유 자원을 사용할 때 문제가 생긴다.

두 개의 스레드가 있다고 생각하자. 두 번째 스레드가 새로운 값을 할당하기 위해 새로운 객체를 생성한다. 첫 번째 스레드는 기존 string 참조를 가지고 있기 때문에 변경되지 않는다. 하지만 string이 변경되면 두 스레드 모두 변경된 string을 갖게 된다.

 

3. 보안 때문에

저장되어야 하는 중요한 정보(비밀번호, 네트워크 포트번호, url 등) 은 대부분 string으로 저장됨. 그렇기에

함부로 변환할 수 있으면 안 된다.

 


String은 값을 literal로 할당하는 경우 String pool을 사용해서 재사용된다.

String 리터럴로 생성을 하면 따로 메모리를 할당받지 않고 Constant pool에 저장되어 계속 재사용된다. 자바 7 이후부터 PermGen(고정된 사이즈로 인한 oom) 공간이 아니라 heap 영역에 배치되었다. 그렇기에 gc도 수행된다.

(New 연산자 string 생성했을 땐 constant pool 사용하지 않고 새로운 메모리 할당)

String Pool은 HashTable 구조를 가지고 있다. String을 해싱하고 그것을 key로 하여 값을 찾는다.

 

 

 

https://bangu4.tistory.com/192

 

 

String은 불변이기 때문에 문자열을 추가, 수정, 삭제하는 경우 새로운 객체를 계속 할당해서 복사해 주어서 메모리 효율이 좋지 않다.

그런 단점을 보완하기 위해 Stringbuilder, StringBuffer 가 등장했다. Stringbuilder, StringBuffer은 문자열을 더하더라도 새로운 객체를 생성하지 않는다. 

 

*Jdk 5 이상부터 string의 더하기 연산을 할 때 stringbuilder는 자동으로 변환된다. 하지만 For 루프에서 불가능 변환되지 않는다. 

 

StringBuilder, StringBuffer가 String 클래스의 덧셈보다 성능이 좋은 이유

String을 사용할 때 String에 값을 할당하면 불변하다 그렇기 때문에 덧셈을 하는 경우 새로운 메모리를 할당해서 덧셈하려고 하는 두 literal을 읽은 다음 새로운 메모리에 할당해줘야 한다.

 

StringBuilder, StringBuffer를 사용할 때

하지만 StringBuilder, StringBuffer는 기존 배열의 크기를 확인한 후 만약 배열의 크기가 충분하다면 string을 그대로 삽입한다. 충분하지 않은 경우는 공간을 두 배 늘리고 두 문자열의 복사가 이루어진다.

두 문자열을 복사하는 과정이 필요할 때만 이뤄지고 새로운 메모리를 할당하지 않는다. 그렇기 때문에 성능상 효율적이다. 

(내부적으로 문자열 값을 저장하는 char형 배열  Value,

현재 문자열 크기의 값을 가지는 int형의 count 이용해서 구현이 된다)

 

예를 들어,

 val result = "abc" + "second string over ten"  같은 경우 자동으로 result = new StringBuilder(). append(“abc”). append(“second string over ten”). toString() 이 생성된다.

 

위 코드의 작동 원리 

1. 맨 처음 value엔 기본 16 크기인 배열이 선언되고 count는 0이다.

2. 추가할 string인 abc가 배열 크기 16보다 작으므로 value에 문자열이 복사된다.

3. 문자열 count는 3으로 초기화된다.

4. 두 번째 문자열 second string over ten은 value의 여유공간보다 더 크기가 크므로 그대로 문자열을 복사하지 못한다. 그렇기 때문에 배열의 크기를 두 배 늘리고 기존 abc와 second string over ten을 합쳐준다.

 

String,  stringbuilder, string buffer의 공통점

-> Charsequence 인터페이스 (문자열을 다루기 위한 표준규격) 구현 

 

stringbuilder, stringbuffer의 차이점

stringbuffer는 synchronized를 사용해서 멀티 스레드 환경에서 thread-safe 하다. 하지만 synchronized를 사용하기 때문에 성능이 안 좋아질 수밖에 없고 단일 스레드 환경인 경우에 굳이 사용할 필요는 없다. (*synchronized가 성능이 안 좋은 이유는 나중에 정리)

 

1. 멀티 스레드 환경에서 문자열의 추가, 수정, 삭제가 빈번히 일어나게 되면 stringbuffer

2. 단일 스레드 환경에서  문자열의 추가, 수정, 삭제가 빈번히 일어나게 되면 stringbuilder

3. 문자열이 변하지 않는다면 string 클래스를 사용하는 것이 메모리 상 효율적이다.

 

 

'Develop > Java' 카테고리의 다른 글

[Java] Map 인터페이스  (0) 2022.12.31
[Java] 자바와 Jvm에 대해서  (0) 2022.12.26
[Java] DeadLock  (0) 2022.12.20
[Java] Abstract class VS Interface  (0) 2022.12.20
[Java] Arraylist의 길이 확장 방법  (1) 2022.12.20