티스토리 뷰
Java에서 문자열은 Heap과 SCP(String Constant Pool)에 적재됩니다.
Heap은 runtime때 new로 생성된 문자열이 적재되며
SCP는 runtime때 생성된 literal로 생성된 문자열이 적재됩니다.
(Heap에는 다른 정보도 함께 저장됩니다.)
아래 그림은 할당방식에 따른 적재공간을 표현합니다.
String s1 = "a";
위와 같이 literal 한 방식으로 할당할 경우
"a"는 String Constant Pool에 적재된 후 s1식별자에
String Constant Pool에 적재된 "a"의 주소가 반환됩니다.
String s2 = new String("a");
위와 같이 new 키워드를 사용한 경우는
Heap 영역에 "a"를 할당한 후
Heap에 적재된 "a"의 주소가 반환됩니다.
Stack(Java Virtual Machine Stacks)
위에서 stack영역을 그려봤는데
thread(stack area)영역에서 생성될 경우
동일하게 Heap or SCP영역에 적재 됩니다.
전역 공간에서 선언된 문자열 또한 동일하게 Heap or SCP로 갑니다.
SCP의 특징은 문자열이 중복 값이 없습니다.
10명이 동일한 "A"라는 문자열을 적재할 시
최초 1명의 문자열만 SCP영역에 적재되며
나머지 9명은 해당 문자열을 공유하여 사용하게 됩니다.
그래서 SCP영역의 문자열은 같은 문자열일 경우 같은 주소 값을 가집니다.
Heap의 특징은 문자열을 기준으로 다루지 않고
주소값을 기준으로 다룹니다.
10명일 동일한 "A"라는 문자열을 적재할 시
10개의 "A"문자열이 적재되며 10개의 문자열 모두
각기 다른 주소 값을 가지게 됩니다.
그래서 Heap영역의 문자열은 같은 문자열일 경우에도 주소가 다릅니다.
String의 문자열 비교
String SCPStr = "abc";
String SCPStr2 = "abc";
System.out.println(SCPStr == SCPStr2); // TRUE
String heapStr = new String("abc");
String heapStr2 = new String("abc");
System.out.println(heapStr == heapStr2); // FALSE
String byToString = SCPStr.toString(); // java 버전마다 상이
System.out.println(SCPStr == byToString); // TRUE
String combineStr = SCPStr + "d";
String anotherStr = "abcd";
System.out.println(combineStr == anotherStr); // FALSE
첫 번째 케이스는 둘 다 literal방식으로 생성되어
Pool에 들어갑니다.
SCPStr2 할당은 "abc"를 SCP에 적재하려고 하나 이미 존재하여
이미 존재하는 문자열의 주소 값만 반환합니다.
고로 TRUE입니다.
두 번째 케이스는 둘 다 new 키워드를 사용하여
Heap영역에 적재되어 같은 문자열 이어도 다른 주소 값에 적재됩니다.
주소 값이 다르니 당연히 FALSE입니다.
세 번째 케이스는
기존 문자열에 toString()을 호출해서 비교합니다.
몇 주 전에 toString()의 코드를 보는데 new Keyword를 사용하는 것을 보고
예제에 넣었으나 막상 돌리고 보니 openJdk1.8에서는 this를 사용하였습니다.
버전마다 구현 방식이 상이한 것 같습니다. 사용상에 주의 필요
네 번째 케이스는
이미 식별자에 할당된 주소를 이용하여 '+' operator를 사용합니다.
TRUE가 나올 것이 기대되지만 아쉽게도 FALSE입니다.
1.8 기준 내부적으로 StringBuilder가 사용됩니다.
이렇게 '=='를 사용하니 문자열 작성방식에 따라 구분이 힘들어집니다.
그래서 주소 값을 비교하는 '=='보다는 String에서는
시퀀스를 비교하는 equals를 사용하는 것이 정신건강에 좋다 생각됩니다.
String '+'는 내부적으로 어떻게 변할까?
정말 간단한 로직을 가진 코드가 있습니다.
이 코드를 컴파일한 뒤 디어셈블러 하면 런타임 때 다른 방식으로 동작하는 것이 확인됩니다.
Java 11( 10 이후 )
Java 8( 9 이전 )
Java8의 7, 27번 줄의 "<init>":() V가
인자와 반환 값 둘 다 없는 메서드(생성자)를 표현합니다.
Java 8에서는 '+'의 개수만큼 StringBuilder instance가 생성됩니다.
String a = "fff";
a = new StringBuilder(a). append("123");
a = new StringBuilder(a). append("af");
이렇게 변경해버린다고 보면 됩니다.
(여러 줄로 개행하여 식별자를 참조하는 '+'를 사용할 경우입니다. , "aa" + "bb"+ "cc"는 다름)
그래서 8에서는 많은 문자열을 '+'연산할 일이 생길 경우
StrungBuilder를 사용하여 append() 메서드를 사용을 권장합니다.
'Java & Kotlin' 카테고리의 다른 글
Java inheritance, abstract class, interface (0) | 2022.09.03 |
---|---|
Java modifier (0) | 2022.08.24 |
Java virtual Machine (0) | 2022.08.15 |
Java Lambda Expression (0) | 2022.07.26 |
Java Arithmetic Operator (0) | 2022.07.25 |
- Total
- Today
- Yesterday
- jre
- JDK
- boot
- JDK8
- java8
- Spring
- 백준 제로
- 자바
- 문제
- 스택
- jre11
- 자사서비스
- 백준 제로 자바
- 백엔드
- 프로그래머
- 알고리즘
- JPA
- boot 일대다
- jdk11
- 스타트업
- springboot
- 다대일
- jre8
- jvm
- 관계설정
- 코딩테스트
- 개발자채용
- mappedby
- ㅃ
- 백준
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |