제 홈페이지의 모든 글은 anti-nhn license에 따릅니다.



java String 풀(Pool)

다음의 케이스에 대해 a==b 가 true인지 false인지 알아 봅시다~.

<case 1>
String a = "하하";
String b = "하하";

String a = "머시기";와 같이 만들게 되면, 이 값은 jvm이 String 풀에 등록시키고 String b="머시기";와 같이 동일한 값이 다시 호출될 때는 풀에 등록된 값을 리턴해 줍니다. Singleton으로 처리된다고 이해하면 되겠습니다.(Singleton 은 여기여기를 읽어보시면 됩니다.) true!

<case 2>
String a = new String("하하");
String b = new String("하하");

new String으로 String 객체를 생성하기 때문에 일반적인 객체 생성과 같은 맥락입니다. ==는 메모리상의 주소만 비교하지 값을 비교하는 것이 아닙니다. false!

<case 3>
String a = reader.readLine(); // "하하"라는 String을 읽어왔다 칩시다..
String b = reader.readLine(); // 마찬가지..

3번의 경우도 2번의 경우와 유사합니다. 어떤 reader를 통해서 읽어온 값이기 때문에 String풀에 등록되지 않습니다.(물론, reader 안쪽에서 pool에 등록하는 로직을 집어넣으면, 결과가 달라질 수는 있습니다만, 일반적인 reader에서는 그런 로직이 들어있지 않습니다.) false!

<case 4>
String a= new String("하하").toString();
String b= new String("하하").toString();

String의 toString()이 어떻게 구현되었는가를 보면 됩니다. String의 toString()은 return this;가 끝입니다. 결국 case 2와 동일합니다. false!

<case 5>
String a= String.valueOf("하하");
String b= String.valueOf("하하");

이 경우는 String.valueOf(Object o)가 호출되는 것입니다. String의 valueOf(Object o)는 o.toString()을 리턴합니다. 즉 "하하".toString()이 됩니다. 여기서 1에서 보셨듯 "하하"는 String 풀에 등록되어있습니다. b의 "하하"는 a에서 등록된 풀에서 가져옵니다. 결국 같은 인스턴스가 되는 것입니다. true!

<case 6>
String a = "하" + "하";
String b = "하하";

자바 컴파일러가 (컴파일 옵션에 따라 변경되는 지는 모르겠습니다. 이 경우는 디폴트로 컴파일한 경우를 얘기하는 겁니다. sun jdk 1.5, 1.6의 경우입니다. ) String a = "하" + "하"; 라는 부분을 String a = "하하"; 로 변경시켜 버립니다. 디컴파일해보니까 그렇게 나오더군요. case 1과 같아집니다. true!

<case 7>
String a = "하";
String b = a+ "하";
a = a+"하";

7번의 경우도 6번의 경우와 비슷하지만, +로 연결된 부분에 따옴표로 감싸진 string이 아니라 변수가 들어가는 경우인데, 이 경우도 자바 컴파일러가 코드를 변경시킵니다. String b = new StringBuilder().append(a).append("하").toString(); 으로 코드가 바뀝니다. b 다음에 바꾸는 a의 경우도 마찬가집니다. 따라서 이 경우는 case 3과 유사한 코드가 됩니다. false!


6,7의 경우는 컴파일러가 소스코드를 "그대로" 컴파일하지 않기 때문에 나타나는 현상입니다.

StringBuilder가 StringBuffer보다 대략 2배 정도 빠르기 때문에(테스트해보니까 그렇더이다~~) StringBuffer로 append 하는 것보다는 그냥 String을 +로 연결하는 게 더 빠르다는 결론이 나오는군요.

http://www.odi.ch/prog/design/newbies.php 이 링크는 "자바 코드 이렇게 짜지마라!" 하는 내용으로 유명한 글입니다. 맨 위 부분이 +로 스트링 연결시키지 말라는 내용입니다. (이 글은 개인적으로 강추 때리는 글입니다. 언젠가 한글로 번역된 것도 본 적이 있는데, 구글형한테 물어보면 갈켜줄겁니다.) 컴파일러가 진화해버리면서 더 이상 유효하지 않겠군요.

단, 루프 안에서 + 처리하는 것은 성능이 떨어지는 듯합니다.

String h = "";
for (int i = 70; i < 80; i++) {
    h += (char) i;
}
이 코드는 컴파일하면 루프 안의 부분이

h = (new StringBuilder(java.lang.String.valueOf(h))).append((char)i).toString();

와 같이 바뀝니다. 즉, 매번 StringBuilder를 만들어내고, append 한 담에 다시 string으로 뽑아냅니다. 만약 루프 밖에서 StringBuilder를 만들어 내고, 루프 안에서 append하고 루프 끝나고 toString을 하면 성능이 그다지 안 떨어지겠지만, 최적화 과정이 그런 식으로 이루어지진 않는군요. 따라서 이런 부분은 코딩을 잘 잘하는 게 최선인 듯합니다.

String이 풀을 사용하는 것이 가능한 것은 String은 immutable이기 때문입니다. (immutable은 여기 참조)

다만, 2,3,4와 같이 읽어들인 String을 풀에 등록시켜서 써야 할 때가 있습니다. 동일한 user에 대해서 어떤 로직에 synch를 걸어야 할 경우가 있죠.(synchronized 분석은 여기서)

예를 들어, 한 번만 응모할 수 있는 이벤트가 있다고 칩시다. 일반적으로 디비에 이미 응모한 값이 있는지만 체크하지만 이는 완벽하지 않습니다. 브라우저를 두개 열어놓고 거의 동시에 응모해 버리면 통과될 수도 있습니다. 이런 경우 이벤트 등록 로직이 사용자 아이디에 대해 synchronized가 걸려야 합니다. 그러나, jsp나 servlet의 경우 request.getParameter로 값을 받아오면 설사 그 값이 같다 하더라도(즉, 같은 사용자라 하더라도) 인스턴스가 같은 것은 아닙니다.(case 3번과 유사하다고 보시면 됩니다.) 이럴 때는 String의 intern()을 사용하면 됩니다. String의 인스턴스 a,b에 대해서 a.equals(b)라면, a.intern() == b.intern()이 성립하며, 그 반대도 성립합니다.

synchronized(request.getParameter("user_id").intern()){
    // 이벤트 응모 로직
}

위의 경우 request.getParameter("user_id")가 호출되면, String 인스턴스가 하나 생기고 intern이 호출되면 풀에서 값을 가져옵니다.(물론 풀에 등록이 안 되어 있으면 등록부터 합니다.) 따라서 동일한 아이디일 경우 synchornized가 모니터를 잡게 되는 객체는 동일합니다.

by 삼실청년 | 2008/06/16 22:56 | 컴터질~ | 트랙백 | 덧글(5)

트랙백 주소 : http://iilii.egloos.com/tb/4427484
☞ 내 이글루에 이 글과 관련된 글 쓰기 (트랙백 보내기) [도움말]
Commented by 소내기 at 2008/07/01 11:12
예전에 읽었던 포스트였는데, Map관련된 포스트를 읽다가 다시 보았습니다. String pool에 대해 막연하게만 생각했는데, String에서 intern으로 Pool에 등록혹은 다시 꺼내어 synchornized하는게 상당히 매력적이네요. 이거 유용할것 같아요. Pool에서 꺼내디 동일한 값이 들어온다면 그것에 대해서만 synchornzed할수 있으니 무작정 걸어버리기도 했는데, 감사!
Commented by 서미향 at 2009/01/19 22:00
재작년 여름에 티맥스에서 로또 프로젝트 한다구
연구소 있을때 만났었는데
기억하실지 모르겠네요
저는 티맥스 그만 두고 회사 옮겼는데 우연히
지금 회사 동료가 황정철선임님 블로그에서 java String 풀(Pool)
보라구 알려줬는데 블로그 보니깐 황선임님 블로그가 맞는것같아서요
두서없이 적은것같네요 새해복 많이 받으세요
Commented by 삼실청년 at 2009/01/22 10:36
헉~~ 저인지 어떻게 아셨어요?-_-;;;
지식의 깊이를 드러내는 블로그인지라 틀린 거 있을까봐 쪽팔림에 정체를 숨기고 있었는데 뽀록났네요-_-;
미향씨 당근 기억나죠~ 회사 나가셨단 얘기도 들었습니다. 저번에 메신저로 저한테도 바이러스 한 번 뿌리시두만요ㅋㅋ
잘 지내시죠? 새해 복 많이 받으세요~~
Commented by 프랑크진우 at 2010/06/15 23:15
형 잘보고 가요~
짱!
Commented by 삼실청년 at 2010/06/25 18:58
넌 아직도 독일이냐?
얼렁 돌아와~
장가가야지ㅋㅋ

:         :

:

비공개 덧글

◀ 이전 페이지          다음 페이지 ▶