String Class
String 클래스의 구조
- String은 문자열을 내부적으로 byte[]로 저장한다. JDK 9 이전에는 char[], JDK 9부터는 메모리 효율성을 위해 byte[]로 저장하게 되었다
// String.class (Java 8)
public final class String implements ... {
private final char[] value;
...
// String.class (Java 11)
public final class String implements ... {
private final byte[] value;
...
- String은 final한 클래스로서 상속(확장)이 불가능하고, 문자열을 저장하는 배열 또한 final로 저장되어있어 변경이 불가능하다. 따라서 String에 새로운 문자열을 할당하는것은, 새로운 String 객체를 만들어서 할당하는것이다
- 이로 인해 문자열 연산시에 객체를 새로 만드는 Overhead가 발생한다
- 객체가 불변하므로 멀티 쓰레드 환경에서 동기화를 신경 쓸 필요가 없고, 조회 연산에 매우 큰 장점을 가지고 있다
String 객체 생성 방식
- Literal로 선언 (큰따옴표 ""로 선언)
- String은 Heap 메모리 영역에 String만을 위한 전용 메모리 공간인, String Constant Pool이 존재한다
- String을 리터럴로 선언하면, JVM은 SCP에서 같은 "값"이 있는지 확인하게 되고, 같은 값이 있다면 새로 선언한 String이더라도 기존에 존재하는 주소값을 가지게 된다
- new로 선언
- new로 String을 선언하게 되면, 리터럴과 다르게 JVM의 Heap 영역에 곧바로 저장이 된다
- 이렇게 되면, "같은 값"을 가지더라도 서로 다른 메모리 주소를 가지게 된다
- String class의 intern() 메서드를 사용하여, new로 생성하여 힙 영역에 저장된 String을 SCP로 저장할 수 있다
String str1 = new String("ABC").intern();
String str2 = new String("ABC").intern();
str1 == str2 // 주소 비교 결과는 true
출처: https://creatordev.tistory.com/81 [Creator Developer:티스토리]
String이 Immutable한 이유
- 메모리 관리
- String을 Literal로 선언하게 되면, String이 객체지만 같은 값을 가질 때 동일한 메모리 주소를 갖게 된다. 따라서 중복된 값들을 동일한 메모리 주소를 사용하게 함으로써 메모리 공간을 절약할 수 있다
- 이렇게 같은 값을 가질 때 같은 메모리 영역을 가지므로, 하나의 메모리 주소에서 수정이 일어나게되면 이를 바라보는 모든 값들에게 동시성의 문제가 발생할 수 있다. 따라서 String은 Immutable하게 설게되었다
- 보안
- String은 인증을 위한 사용자 ID, 비밀번호, 네트워크 연결을 위한 url 등, Java에서 광범위하게 사용되고 민감한 정보들을 다루고 있다
- 따라서 Immutable하므로써 데이터의 무결성을 보장할 수 있기에 보안적인 측면에서 중요하다
- 동기화
- String은 Immutable하기 때문에 Thread-Safe하다
- Thread-safe: 다른 스레드가 String에 접근하는 동안 변경될 일이 없어서 원자성이 보장되고, 동기화를 걱정할 필요가 없다
- 따라서 멀티 쓰레드 환경에서 여러 쓰레드가 String 객체에 접근하더라도, 프로그램 실행에 문제가 없게 된다
- 해시코드 캐싱을 통한 성능 향상
- Hash 구현체들은 명령어를 수행할 때, 데이터를 bucket에 채우면서 hashCode() 메소드를 매우 자주 호출하게 된다
- 하지만 String은 불변성이 보장되므로, 값이 변하지 않아서 최초로 딱 한번 hashCode() 메소드를 호출하여 전역 변수인 hash에 값을 할당하고, 이후로는 hash를 호출하여 사용하는 캐시 기능이 구현되어 있다
- 따라서 String 객체를 사용하는 Hash 구현체 컬렉션의 성능을 향상시킬 수 있다
StringBuilder & StringBuffer
StringBuilder와 StringBuffer는 모두 String의 문제점을 보완해주는 역할을 하고, 같은 기능을 한다. 공통점은 무엇이고 어떠한 차이점 때문에 구분해서 사용하는지 확인해보자
공통점
- mutable한 객체이다
- new 연산을 통해서 객체를 선언한다
- 문자열 연산시 새로 객체를 만들지 않고, 크기를 변경시킨다. 따라서 문자열 연산이 많은 환경에서 사용하면 좋다
- 사용하는 메서드가 동일하다
차이점
- StringBuffer는 내부에서 synchronized를 통해서 thread-safe를 보장하여, mutable의 단점을 보완했다
- StringBuilder는 thread-safe하지는 않지만, 속도가 더 빠른 장점이 있다
- 따라서 문자열 연산이 많은 multi-thread 환경에서는 StringBuffer를, 문자열 연산이 많은 single-thread 환경에서는 StringBuilder를 사용하면 좋다
- 다만, 2억번의 연산과정에서 두 클래스의 성능차이는 300ms 정도이므로, thread-safe한 StringBuffer가 추천된다
복습 Question
- String을 리터럴로 선언할 때, 어떤 방식으로 메모리 효율성을 높일까?
- String이 Immutable한 이유는 무엇일까?
- StringBuffer와 StringBuilder는 어떤 환경에서 쓰는게 좋을까?
Reference
- 자바 String byte[] : https://velog.io/@roro/Java-%EC%9E%90%EB%B0%94-String-byte-Compact-Strings
- String 클래스에 대해서 알아보자ㅣ : https://velog.io/@gates/JAVA-String-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%97%90-%EB%8C%80%ED%95%B4%EC%84%9C-%EC%95%8C%EC%95%84%EB%B3%B4%EC%9E%90
- Java String 클래스 정리 : https://velog.io/@donglee99/JAVA-String-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%A0%95%EB%A6%AC
- String Constant Pool이란? : https://starkying.tistory.com/entry/what-is-java-string-pool
- 자바 String 클래스에 대해서 : https://syundev.tistory.com/285
- String 클래스 깊숙히 이해하기 : https://creatordev.tistory.com/81