이전글
자바(Java) String의 불변성(Immutable)
어떤 언어든 프로그래밍을 하다 보면 가장 많이 사용하고 꼭 있는 자료형이 문자열(String)일것입니다. 사람은 010100 과 같은 숫자보다 "book" 같은 문자에 더욱 익숙하기 때문입니다. 또한 문자열은 "1234"와 같이 숫자를 표현할 수 있고 "j20dsf908"와 같이 특정 인코딩된 데이터또한 표현이 가능하며, 모든것들을 표현할 수 있습니다. 예를들어 png같은 이미지 파일도 Base64 인코딩 하여 ASCII 형식의 문자열로 표현이 가능하지요.
이처럼 수많은 데이터를 문자열로 표현이 가능하기에 어떤 언어에서든 존재하고 가장 많이 사용하는 자료형이 문자열 자료형일 것입니다. 그래서인지 문자열(String) 타입은 참조타입임에도 불구하고 직접 new 연산자를 통해 객체를 생성하여 사용하는것이 아닌 문자열 리터럴 형태로 사용하는것이 허용됩니다.
String str = new String("Hello"); //이렇게도 사용 가능하지만
String str2 = "Hello"; //이렇게 리터럴을 할당할수도 있다 |
cs |
String의 불변성(immutable)
이미 아실분은 아시겠지만 String 즉 문자열 객체는 최초에 한 번 생성되면 절대로 그 값이 변하지 않습니다.
String str = "문자열";
str = "문자열2"; |
cs |
위와 같이 String객체가 생성된 이후 "문자열"을 "문자열2"로 바꾼다고 해도 실제 내부적으로는 최초에 생성된 String 객체의 값이 변경된것이 아닌 새로운 String 객체가 생성되어 그 참조가 str 변수에 할당된 것입니다. 즉 이 상태에서는 최초에 생성된 "문자열" 과 "문자열2" 두개의 객체가 heap 에 생성되어 있는 상태입니다.
String 객체가 불변객체(Immutable)인 이유
이쯤 되면 왜 String은 불변성을 가지게 설계되었는지 궁금할 수 있습니다. 앞서 설명했듯이 String은 가장 많이 사용되는 데이터 타입 중 하나입니다. 그렇다는 말은 Java 어플리케이션에서 String 타입의 객체들이 가장 많은 메모리를 차지한다는 말이기도 합니다.
Java 언어의 창시자인 제임스 고슬링 형님(?)께서는 문자열 객체는 재사용 될 가능성이 높기에 같은 값일 경우 어플리케이션 당 하나의 String 객체만을 생성해두어 JVM의 힙(heap)을 절약하고자 했습니다.
1. String 객체의 캐싱기능
앞서 설명했지만 String이 불변객체로 설계된 이유는 메모리 절약을 위한 캐싱 기능을 위해서입니다.
예를 들어 Google 의 웹서버가 Java 언어로 만들어져 있다고 가정해보도록 하죠. 여기서 "Google"이라는 문자열이 굉장히 많이 쓰일것입니다. 구글은 사용자가 1초에 몇백만 ~ 몇천만의 HTTP request를 받게 되는 세계적인 웹서비스입니다.
만약 "Google"이라는 값을 가지는 문자열 객체를 사용자의 요청을 처리할때마다 한개씩 생성한다면 어떻게 될까요? 힙에 "Google"이라는 값을 가진 문자열 객체가 사용자의 요청을 처리하는 수만큼 생성되어 있을것입니다.
이번에는 다음과 같은 상황을 보도록 하죠. "Hello Wrold" 라는 문자열을 500번 출력하고 있습니다. 만약 String의 불변성이 없었다면 이 코드는 "Hello World"라는 값을 갖는 500개의 String 객체를 생성하게 됩니다. 그러나 불변성의 성질을 갖는덕분에 실제로는 "Hello World"라는 문자열 객체는 단 하나만 생성이 됩니다. 그러나 참조값을 갖는 참조변수인 String str 변수 자체는 스택상에서 500번 생성되었다가 사라질 것입니다.
for(int i = 0; i < 500; i++) {
String str = "Hello World";
System.out.println(str);
} |
cs |
그리고 캐싱의 진정한 목적은 메모리 절약도 있겠지만 속도 향상에 있습니다. 자주 쓰이는 값은 최대한 CPU와 가까운 데이터일수록 처리가 빠릅니다. 보조기억장치보다는 메모리에 메모리보다는 캐시메모리나 레지스터에 있을수록 곧바로 연산을 하기가 쉬운것이죠.
Java에서 이 String 객체들은 Heap의 String Pool 이라는 특별한 공간이 있어 이곳에 저장됩니다. 그리고 앞선 코드의 for문에서처럼 참조하려는 문자열이 String Pool에 존재하는 경우 새로 생성하지 않고 Pool에 있는 객체를 사용하도록 합니다. 따라서 객체를 새로 생성되는 오버헤드가 발생하지 않기때문에 특정 문자열값을 재사용하는 빈도가 높을수록 상당한 성능 향상을 기대할 수 있습니다.
참고로 이글을 작성하면서 알아본 바에 의하면 문자열 리터럴("Hello"와 같이 값 자체를 할당하는 경우)을 사용하는 경우와 new String() 과 같이 생성자를 사용하여 생성하는 경우 내부 동작의 차이가 있다고 합니다. 이부분은 나중에 알아보도록 합니다.
2. 보안기능
String은 불변성을 가졌기 때문에 중요한 데이터를 문자열로 다루는 경우 강제로 해당 참조에 대한 문자열 값을 바꾸는것이 불가능합니다. 따라서 보안에 유리합니다.
3. 스레드 안전성
String 객체가 변경될 수 없다는것은 여러 스레드에서 동시에 특정 String객체를 참조한다고 하더라도 안전하다는것을 뜻합니다. 만약 어플리케이션 여러곳에서 특정 문자열값을 참조하고 있을때 그 값 자체는 절대 변하지 않으므로(그 누구도 수정할 수 없으므로) 안전하다고 할 수 있습니다.
물론 String str = "Hello World" 와 같은 상황에서 str에 새로운 문자열 값을 할당할수는 있겠지만 내부적으로는 String 객체의 값을 변경한 것이 아닙니다. 즉 힙공간에 존재하는 "Hello World" 자체는 변하지 않고 그대로 살아 있는 것입니다.
참고문서
'자바[Java]' 카테고리의 다른 글
[JAVA] Java I/O(입출력) [2] 자바 입출력 패키지(java.io) 구조 (1) | 2019.07.11 |
---|---|
[Java] String의 더하기 연산으로 인한 문제점과 StringBuffer, StringBuilder의 특성과 차이점에 대해서 (0) | 2019.06.22 |
[Logback] 로그백(logback) 스프링(Spring)에서 사용 하는 방법 (1) | 2019.04.04 |
[Logback] 로그백(logback) 다운로드 및 사용해보기 (0) | 2019.04.03 |
[Logback] Logback이란? log4J의 후속작 로그백(Logback) 살펴보기 및 비교 (0) | 2019.04.02 |