DevLog ๐ถ
[Java] ๋ฌธ์์ด - String pool๊ณผ String concatenation ๋ณธ๋ฌธ
์ฐํ ์ฝ์์ ๋ฌธ์์ด ๊ด๋ จ ์์ ์ ๋ฃ๋ค๊ฐ, ์ด๊ฒ์ ๊ฒ ๊ถ๊ธํด์ ์ฐพ์๋ดค๋๋ฐ ๋ธ๋ก๊ทธ ๊ธ๋ก ์์ฑํ๋ฉด ์ข์ ๊ฒ ๊ฐ์์ ์ด๋ ๊ฒ ์์ฑํ๋ค ๐ค
์์์ ํ๋ฆ๋๋ก ๊ถ๊ธํ ์ ๋ค์ ์ฐพ์๋ณธ ๊ฑฐ์ฌ์ ์๋นํ ๋์๊ฐ ์์ ๊ฒ ๊ฐ๋ค ๐ฅฒ
โ๏ธ String๊ณผ String pool
String์ ๋ฌธ์์ด์ ์ ์ฅํ๊ณ ์ฒ๋ฆฌํ๋ ํด๋์ค์ด๋ค. ๋ถ๋ณ ํด๋์ค์ด๊ธฐ ๋๋ฌธ์, ํ ๋ฒ ์์ฑ๋๋ฉด ๋ณ๊ฒฝ์ด ๋ถ๊ฐ๋ฅํ๋ค. ๋ํ, final ํด๋์ค๋ก ๋์ด ์๊ธฐ ๋๋ฌธ์ ์์์ด ๋ถ๊ฐ๋ฅํ๋ค.
UTF-16 ์ธ์ฝ๋ฉ์ ํ์ฉํ์ฌ, ์ธ์ฝ๋ฉ๋ ๋ฌธ์์ด์ ์ ์ฅํ๊ธฐ ๋๋ฌธ์ ๊ฐ ๋ฌธ์๋ฅผ 2๋ฐ์ดํธ (=16๋นํธ) ์ ๋์ฝ๋ ๋ฌธ์๋ก ํํํ๋ค.
public String(int[] codePoints, int offset, int count) {
checkBoundsOffCount(offset, count, codePoints.length);
if (count == 0) {
this.value = "".value;
this.coder = "".coder;
return;
}
if (COMPACT_STRINGS) {
byte[] val = StringLatin1.toBytes(codePoints, offset, count);
if (val != null) {
this.coder = LATIN1;
this.value = val;
return;
}
}
this.coder = UTF16;
this.value = StringUTF16.toBytes(codePoints, offset, count);
}
(๋ด๋ถ ๊ตฌํ์ฒด๋ฅผ ๋ณด๋ฉด, ์๋์์ this.coder = UTF16์ผ๋ก ์ค์ ํด๋ ๊ฒ์ ๋ณผ ์ ์๋ค.)
๋ฌธ์์ด์ ์ฒ๋ฆฌํ๊ธฐ ์ํด, 'String'์ ์ ๋ง ๋ง์ด ์ฌ์ฉ๋๋ ํด๋์ค์ด๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์, ํญ์ ๊ฐ์ฒด๋ฅผ ์์ฑํด์ ์ฌ์ฉํ๊ธฐ์๋ ์ด์ฉ๋ฉด ๋ญ๋น์ผ ์ ์๋ค. ์ด๋ฅผ ์ต์ ํํ๊ธฐ ์ํด์ ์๋ฐ์์๋ ๋ด๋ถ์ ์ผ๋ก 'String pool'์ ํ์ฉํ์ฌ ์ค๋ณต๋ ๋ฌธ์์ด์ ๊ณต์ ํ๊ฒ ๋๋ค.
์ฌ๊ธฐ์, String pool์ด๋ ๋ฌด์์ผ๊น? JVM์์ String ๊ฐ์ฒด๋ฅผ ๋ฉ๋ชจ๋ฆฌ์์ ๊ด๋ฆฌํ๊ธฐ ์ํ ์ผ์ข ์ '์ ์ฅ์'์ด๋ค. String pool์ ๋ํด์ ์์๋ณด๊ธฐ ์ ์, ๋จผ์ String์ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์๊ฐํด๋ด์ผ ํ๋ค.
String์ ์์ฑํ๊ธฐ ์ํด์๋, new, ํน์ ๋ฆฌํฐ๋ด์ ์ฌ์ฉํ์ฌ ์์ฑํ ์ ์๋ค.
String hello = new String("hello"); // new ์ฌ์ฉํ๊ธฐ
String hi = "Hi"; // ๋ฆฌํฐ๋ด ์ฌ์ฉํ๊ธฐ
๋์ ๋ฌด์จ ์ฐจ์ด๊ฐ ์์๊น? ์ฌ์ค new๋ฅผ ํตํด์ ์์ฑํ๋ ๊ฑด ๊ฑฐ์ ๋ณธ ์ ์ด ์๋ค. (๊ถ์ฅํ์ง๋ ์๋๋ค.)
๋จผ์ , 'new'๋ฅผ ํตํด ์์ฑํ String ๊ฐ์ฒด๋ heap ์์ญ์ ์ ์ฅ๋๊ณ , ๋ฆฌํฐ๋ด๋ก ์์ฑํ ๊ฐ์ฒด๋ 'String pool'์ ์ ์ฅ๋๋ค.
๊ทธ๋ฆผ์ผ๋ก ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
new ์ฐ์ฐ์๋ฅผ ํ์ฉํ๋ฉด ์๋ก์ด ๊ฐ์ฒด๋ฅผ ์์ฑํ์ฌ 'heap' ์์ญ์ ์ ์ฅํ๊ฒ ๋๋ฉฐ, ๋ฆฌํฐ๋ด์ ์ฌ์ฉํ๋ฉด String pool์ ์์๊ฒ ์์ด๋ ๊ฒ์ ๋ณผ ์ ์๋ค. ์ด๋, ๊ฐ์ฅ ์ค์ํ ๊ฒ์ ๋์ผํ ๋ฌธ์์ด์ด ํ ๋ฒ ๋ ๋ค์ด์์ ๊ฒฝ์ฐ์ด๋ค. String pool์ Heap ๋ด๋ถ์ ์กด์ฌํ๋ฉฐ, JVM ์ด ์์๋ ๋ ์์ฑ๋๊ณ , ํด๋์ค ํ์ผ์ด ๋ก๋๋ ๋ ์ด๊ธฐํ๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ํ๋ก๊ทธ๋จ์ด ์คํ๋ ๋๋ง๋ค ์๋ก์ด String pool์ด ์์ฑ๋๋ ๊ฒ์ด ์๋, JVM์ด ์์๋ ๋ ํ ๋ฒ ์์ฑ๋๊ณ ๊ทธ ์ดํ๋ก๋ ๊ณ์ํด์ ์ฌ์ฌ์ฉ๋๋ค๋ ํน์ง์ด ์๋ค.
String hello = new String("hello");
String hello2 = new String("hello");
String hi = "Hi";
String hi2 = "Hi";
์ด์ ๊ฐ์ด ๋ฌธ์์ด์ด ์กด์ฌํ ๋, ์์ ๊ตฌ์กฐ๋ ์ด๋ป๊ฒ ๋ณํ๊ฒ ๋ ๊น?
new๋ฅผ ํตํด์ ์์ฑํ๊ฒ ๋๋ฉด, ์ด๋ฏธ ์กด์ฌํ๋ ๊ฐ์ด๋๋ผ๋ ๋ฌด์กฐ๊ฑด ํ์ ์๋ก์ด ๊ณต๊ฐ์ ํ ๋นํ๊ฒ ๋์ง๋ง, ๋ฆฌํฐ๋ด์ ํ์ฉํ๊ฒ ๋๋ฉด ์ด๋ฏธ ์กด์ฌํ๋ ๊ฐ์ด ์์ ๋ ํด๋น ๊ฐ์ ๊ฐ๋ฆฌํค๋๋ก ํ๋ค๋ ๊ฒ์ด๋ค. ์ฆ, ์๋ก ๊ณต์ ํ๋ ์ํ๊ฐ ๋๋ค. ์ฐธ๊ณ ๋ก, ๋ด๋ถ์ ์ผ๋ก String pool์ HashMap์ ์ฌ์ฉํ์ฌ ๊ตฌํํ๊ณ ์๊ธฐ ๋๋ฌธ์ (๋น์ฐํ ConcurrentHashMap์ด๋ค. ๋์์ฑ ๋ณด์ฅ!)
String hello = new String("hello");
String hello2 = new String("hello");
String hi = "Hi";
String hi2 = "Hi";
System.out.println(hello == hello2); // false
System.out.println(hello.equals(hello2)); // true
System.out.println(hi == hi2); // true
System.out.println(hi.equals(hi2)); // true
์ค์ ๋ก ๋น๊ต ์ฐ์ฐ์ ํด๋ณด๋ฉด, ๊ฐ์ฒด๋ฅผ ๋น๊ตํ๋ ==์ ๊ฒฝ์ฐ hello์ hello2์ ๋ํด false๋ฅผ ๋ฐํํ๊ณ , hi์ hi2์ ๋ํด์ true๋ฅผ ๋ฐํํ๋ ๊ฒ์ ๋ณผ ์ ์๋ค.
โ๏ธ String pool์ ์ด๋์ ์ ์ฅ๋์์๊น?
Java6 ์ด์ ๋ฒ์ ๊น์ง ๋ฌธ์์ด์ PermGen(Permanent Generation) ์์ญ์ ์์นํ์๋ค. PermGen ์์ญ์ ํด๋์ค๋ ๋ฉํ ๋ฐ์ดํฐ, String pool ๊ฐ์ด ๊ณ ์ ์ ์ผ๋ก ์์ฑ๋๋ ๊ฐ์ฒด๋ค์ด ์ ์ฅ๋๋ ์์ญ์ด๋ค. ์ฆ, JVM์ด ์คํ๋๋๋ผ๋ ๋ณํ์ง ์๋ ๋ฉํ๋ฐ์ดํฐ๋ค์ ์ ์ฅํ๋ ๊ณต๊ฐ์ด๋ผ๊ณ ๋ณด๋ฉด ๋๋ค. ๋ด๋ถ์ ์ผ๋ก PermGen์ ์ด๊ธฐ ์ฌ์ด์ฆ์ธ PermSize์ ์ต๋ ์ฌ์ด์ฆ์ธ MaxPermSize๋ฅผ ์กฐ์ ํ ์ ์์ผ๋ฉฐ, ์ผ๋ฐ์ ์ผ๋ก ๊ณ ์ ๋ ํฌ๊ธฐ๋ก ํ ๋น๋์๊ธฐ ๋๋ฌธ์ OOM (Out Of Memory)์ด ๋ฐ์ํ ์ ์์๋ค. ๋ํ, GC๋ Full GC๋ง ์ํํ์๊ธฐ ๋๋ฌธ์ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฑ๋ฅ์ ์ผ๋ก๋ ๊ทธ๋ ๊ฒ ์ข์ง ์์๋ค.
์๋ฐ์ GC (Garbage Collector)์ ๋ํด์ ๊ฐ๋ตํ๊ฒ ์ง๊ณ ๋์ด๊ฐ๋ณด์.
Young Generation์์๋ ์๋กญ๊ฒ ์์ฑ๋ ๊ฐ์ฒด๋ค์ด ์ ์ฅ๋๊ณ , Old Generation์์๋ Young generation์์ ์ด์๋จ์ ๊ฐ์ฒด๋ค์ด ์ ์ฅ๋๋ค. (Young์์ Eden์ ๊ฐ์ฒด๊ฐ ์์ฑ๋์๋ง์ ์ ์ฅ๋๋ ๊ณณ์ด๊ณ , Survivor๋ ํ ๋ฒ Minor GC๋ฅผ ๊ฒช์ ๊ฐ์ฒด๋ค์ด ์ ์ฅ๋๋ ๊ณณ์ด๋ค.) Old์์๋ง GC๊ฐ ์ผ์ด๋๋ ๊ฒ์ Major GC, Young์์๋ง ์ผ์ด๋๋ ๊ฒ์ Minor GC๋ผ๊ณ ํ๋ค. Full GC์ ๊ฒฝ์ฐ ๋ ์์ญ ๋ชจ๋์ ๋ํด์ ์ฌ์ฉ๋์ง ์๋ ๊ฐ์ฒด๋ค์ ๋ํด ํด์ ํ๋ ์์ ์ด ๋ค์ด๊ฐ๊ธฐ ๋๋ฌธ์ ์ฑ๋ฅ์ ์ง์ฅ์ ์ค ์ ์๋ค.
๊ทธ๋ฌ๋, Java8๋ถํฐ๋ PermGen ์์ญ์ด Metaspace ์์ญ์ผ๋ก ๋์ฒด๋์์ผ๋ฉฐ, ๊ธฐ์กด์ Heap์์ ๊ด๋ฆฌ๋๋ PermGen ์์ญ๊ณผ ๋ฌ๋ฆฌ Native Memory์ ํ ๋น๋๋ค. Native Memory ์์ญ์ JVM ์ธ๋ถ์์ ํ ๋น๋๊ณ ๊ด๋ฆฌ๋๊ธฐ ๋๋ฌธ์, JVM์ด ์คํ๋๊ณ ์๋ OS์ ๋ฉ๋ชจ๋ฆฌ ์์ญ์ ์ฌ์ฉํ๋ฉฐ, GC์ ์ํด ๊ด๋ฆฌ๋์ง ์๋๋ค๋ ํน์ง์ด ์กด์ฌํ๋ค. Heap์ ์ ์ฅ๋์ง ์๊ธฐ ๋๋ฌธ์ GC๊ฐ ๊ด๋ฆฌํ์ง ์์ง๋ง, Heap์ ์ ์ฅ๋์ง ์์์ผ๋ก์ ์ฉ๋์ ๋ํ ์ ํ์ด ์๋ค๋ ๊ฒ์ด ํน์ง์ด๋ค. (๋ฌผ๋ก OS์ ๋ฉ๋ชจ๋ฆฌ์๋ ๊ตญํ๋์ด ์๋ค ๐ฅฒ)
์ฆ, Metaspace์์๋ ํด๋์ค ๋ก๋ฉ ์ PermGen ์์ญ๊ณผ ๋ฌ๋ฆฌ ๋์ ์ผ๋ก ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ ๋นํ ์ ์์ผ๋ฉฐ, ๊ธฐ๋ณธ์ ์ผ๋ก ์์คํ ์ ๋ฉ๋ชจ๋ฆฌ ํฌ๊ธฐ์ ๋ฐ๋ผ์ ๋์ ์ผ๋ก ํ ๋น๋์ง๋ง -XX:MetaspaceSize์ -XX:MaxMetaspaceSize ์ต์ ์ ํตํด์ ์๋์ผ๋ก ์กฐ์ ๋ ๊ฐ๋ฅํ๋ค. ๊ทธ๋ฌ๋, String pool์ ๊ฒฝ์ฐ permGen์ด ์๋ Heap์ผ๋ก ์ด๋ํ์ผ๋ฉฐ, Heap์์ ๊ด๋ฆฌ๋๊ธฐ ๋๋ฌธ์ GC๊ฐ ์ํ๋๋ค.
๐ฌ metaspace ์์ญ vs method ์์ญ
๊ฐ์ธ์ ์ผ๋ก ๊ณต๋ถํ ๋ ๊ถ๊ธํด์ ์ฐพ์๋ณธ ๋ด์ฉ์ด๋ค. JVM์์ ๋ฉ์๋ ์์ญ (๋ค๋ฅธ ๊ณณ์์ ํด๋์ค ์์ญ, ํน์ ์คํํฑ ์์ญ์ด๋ผ๊ณ ๋ ํ๋ ๊ฒ ๊ฐ๋ค. ์ด๋ฆ์ด ์ฐธ ๋ค์ํ๋ค)์ ํด๋์ค ํ์ผ์ ๋ฐ์ดํธ ์ฝ๋๊ฐ ๋ก๋๋๋ ๊ณณ์ผ๋ก, ์ด์ ์ PermGen์์ ๊ด๋ฆฌ๋๋ ๊ฒ๋ค์ ์ด๊ณณ์์ ๊ด๋ฆฌํ๊ฒ ๋์๋ค. ์ด๋ ์ข ํผ๋์ด ์์๋๋ฐ, '๊ทธ๋ฌ๋ฉด metaspace ์์ญ์ method ์์ญ์ ๋์ผํ ๊ฒ์ธ๊ฐ?'๋ผ๋ ์๋ฌธ์ด ๋ค์๋ค.
์ฐพ์๋ณธ ๊ฒฐ๊ณผ, Java8 ์ด์ ๊น์ง๋ ์๋ก ๊ฐ์ ๊ฐ๋ ์ด์์ผ๋ PermGem ์์ญ์ ์์นํด ์๋ ์์ญ์ด native area๋ก ์ด๋ํ๋ฉด์ 'metaspace'๋ผ๋ ์ด๋ฆ์ผ๋ก ๋ณ๊ฒฝ๋์๋ค. (์ด์ ๊น์ง๋ permGen์์ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ ๋ถ ๊ด๋ฆฌํ์์) Java8 ์ดํ๋ถํฐ๋ ํด๋์ค์ ๋ฉํ ๋ฐ์ดํฐ ์ผ๋ถ๋ฅผ native์, ์ผ๋ถ๋ heap์ ์ ์ฅํ๋ฉด์ native memory์ ์ ์ฅ๋๋ ๋ถ๋ถ์ 'metaspace', heap ์์ญ์ ์ ์ฅ๋๋ ๋ถ๋ถ์ 'class metadata' ์์ญ์ด๋ผ๊ณ (JDK ๋ด๋ถ์์ 'klass metadata'๋ผ๊ณ ํ๋ ๊ฒ ๊ฐ๋ค) ํ๋ฉด์ ์ฝ๊ฐ์ ๋ค๋ฅธ ๊ฐ๋ ์ผ๋ก ์ฌ์ฉํ๋ค๊ณ ํ๋ค.
์๋ฌดํผ... JVM์ ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ์๋ฐ, heap๊ณผ native memory ๋ถ๋ถ์ ์ ๋๋ก ์๊ฐํ์ง ๋ชปํด์ ์ฐฉ์ค๊ฐ ์ผ์ด๋ฌ๋ ๊ฒ ๊ฐ๋ค.
- Method area: JVM์ ํด๋์ค ์ ๋ณด๋ฅผ ์ ์ฅํ๋ค. ํด๋์ค๋ง๋ค ๋ณ๋๋ก ์์ฑ๋๋ฉฐ, ํด๋์ค ๋ก๋๊ฐ ํด๋์ค ํ์ผ์ ์ฝ์ด์์ ํด๋์ค ์ ๋ณด๋ฅผ ์ ์ฅํ๋ค. ํด๋์ค ์ด๋ฆ, ๋ฉ์๋ ๋ณ์์ ์ด๋ฆ, ํ์ , ์ ๊ทผ ์ ์ด์๋ค์ด ์ ์ฅ๋๋ค. = java8 ์ด์ ์๋ permGen area. ์ดํ์๋ metaspace๋ผ๊ณ ์๊ฐํด๋ ์ข์ ๊ฒ ๊ฐ๋ค.
- Native Method Stack: ๋ฉ์๋ ์์ญ์ ์ฌ์ฉํ ๋, Java Native Interface๋ฅผ ์ฌ์ฉํด์ ํธ์ถ๋๋ ์ฝ๋๊ฐ ์ ์ฅ๋๋ ์์ญ์ด๋ค. (๊ทธ๋ฅ ์ด๋ฆ์ด ๋ฉ์๋ ์์ญ์ด๋ ๋น์ทํด์ ์ ์ด๋ )
์ด์ผ๊ธฐ๊ฐ ๊ธธ์ด์ก๋ค. ์๋ฌดํผ, String pool์ ๊ฒฝ์ฐ heap์ผ๋ก ์ฎ๊ฒจ์ก๊ธฐ ๋๋ฌธ์ GC๊ฐ ๊ด๋ฆฌํ ์ ์์์ผ๋ฉฐ, ํฌ๊ธฐ๊ฐ ๊ณ ์ ๋์๋ permGen์์๋ ๋ฐ์ดํฐ๊ฐ ๋ง์์ก์ ๋ OOM์ด ๋ฐ์ํ ์ ์์์ผ๋, GC ๊ด๋ฆฌ๋ก ์ธํด์ ๋ ๋ง์ ๋ฐ์ดํฐ๋ฅผ ๋ณด๊ดํ ์ ์๊ฒ ๋์๋ค!
โ๏ธ ๋ฌธ์์ด ์ฐ๊ฒฐํ๊ธฐ - String concatenation
String์ ์์ ๋งํ ๊ฒ์ฒ๋ผ ๋ถ๋ณํ๋ค. ๊ทธ๋์ String๊ณผ String์ ๋ํ๊ฒ ๋๋ฉด, ์๋ก์ด String ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ฒ ๋๋ค. String์ ๋ํ๊ฒ ๋๋ฉด ๋ฉ๋ชจ๋ฆฌ ํ ๋น๊ณผ ํด์ ๊ฐ ๊ณ์ ๋ฐ์ํ๊ฒ ๋๋ฉฐ ๋ญ๋น๊ฐ ๋ ์ ์๊ธฐ์, ๋ณดํต ๋ฌธ์์ด์ ๋ํ ๋ ๋ด๋ถ์ ์ผ๋ก StringBuilder๋ฅผ ์ฌ์ฉํ๋ค. StringBuilder๋ฅผ ์ฌ์ฉํ๊ฒ ๋๋ฉด ๊ธฐ์กด ๋ฐ์ดํฐ์ ๊ณ์ ๋ฐ์ดํฐ๋ฅผ ๋ํ๋ ๋ฐฉ์์ด๊ธฐ ๋๋ฌธ์ ์๋๊ฐ ๋ ๋นจ๋ผ์ง๊ฒ ๋๋ค.
์ฐ์ , ์ง์ StringBuilder๋ฅผ ํตํด์ ๋ฌธ์์ด์ ๋ํด๋ณด์.
StringBuilder str1 = new StringBuilder("hi");
str1.append(" java");
๋ฐ์ดํธ ์ฝ๋๋ฅผ ๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ๋ค.
L0
LINENUMBER 5 L0
NEW java/lang/StringBuilder
DUP
LDC "hi"
INVOKESPECIAL java/lang/StringBuilder.<init> (Ljava/lang/String;)V
ASTORE 1
L1
LINENUMBER 6 L1
ALOAD 1
LDC " java"
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
POP
L2
LINENUMBER 7 L2
RETURN
L3
LOCALVARIABLE args [Ljava/lang/String; L0 L3 0
LOCALVARIABLE str1 Ljava/lang/StringBuilder; L1 L3 1
MAXSTACK = 3
MAXLOCALS = 2
ํฌ๊ฒ ๋ณผ ๊ฒ์ ์๊ณ , invokespecial์ ํตํด์ StringBuilder๋ฅผ ํธ์ถํ๋ค๋ ์ ๊ณผ, L0์์ ์ ์ฅํ 1๋ฒ ์ธ๋ฑ์ค์ hi๋ฅผ L1์์ ๋ก๋ํ์ฌ, invokevirtual์ ํตํด append๋ฅผ ํธ์ถํ์ฌ ์ธ์๋ก ์ ๋ฌํ๋ค๋ ์ ์ด๋ค. (pop์ stringbuilder์์ appendํ ์ดํ ๋ฐํ๋ ๊ฒฐ๊ณผ๊ฐ์ ์ฌ์ฉํ์ง ์์์ ์คํ์์ ์ ๊ฑฐํ ๊ฒ์ด๋ค.)
๊ทธ๋ ๋ค๋ฉด, + ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํด์ ์งํํ๋ฉด ์ด๋ป๊ฒ ๋ ๊น?
Java 9 ์ด์ ์์๋ ๋ฌธ์์ด ์ฐ๊ฒฐ์ +์ ์ฌ์ฉํ๋ฉด, ์ปดํ์ผ๋ฌ๋ StringBuilder ํด๋์ค๋ฅผ ์ฌ์ฉํ ์ฝ๋๋ก ๋ณํ์ ์งํํด์ฃผ์๋ค. +์ ์ฌ์ฉํ ๋ฌธ์์ด ์ฐ๊ฒฐ์ ๋ด๋ถ์ ์ผ๋ก StringBuilder ๊ฐ์ฒด๋ฅผ ์์ฑํ๊ณ , append๋ฅผ ํตํด ๋ฌธ์์ด์ ๊ณ์ํด์ ์ถ๊ฐํด์ฃผ์ด toString()์ ํตํด ํด๋น ๋ฌธ์์ด์ ๋ฐํํ๋ ํํ๋ก ์งํํ์๋ค. ๋ฌผ๋ก , ๋ ์ด์ ๋ฒ์ (JDK 1.4)์์๋ +๊ฐ ๊ณ์ ๋จ์์์๋ค. (์ง์ ํ์ธํ๊ณ ์ถ์์ผ๋, ๋ ๋ฎ์ ๋ฒ์ ์ ๋ค์ด๋ฐ์ง๋ฅผ ๋ชปํ๋ค... ๐ฅฒ ๋ค๋ฅธ ๋ถ์ด ์์ฑํ ๊ฒ์๊ธ ์ฐธ๊ณ !)
๊ทธ๋ฌ๋, Java 9 ์ดํ๋ถํฐ๋ (JEP 280) InvokeDynamic์ ํธ์ถํ๋ ์์ผ๋ก ๋ณ๊ฒฝ๋์๋ค! invokeDynamic์ด๋, methodHandle์ ํธ์ถํ ๋ ์ฌ์ฉ๋๋ ๊ธฐ์ ๋ก, ๋์ ์ผ๋ก ๋ฉ์๋ ํธ์ถ์ ์ฒ๋ฆฌํ๋ค. ํธ์ถํ ๋ฉ์๋๊ฐ ๋์ ์ผ๋ก ๊ฒฐ์ ๋ ๋ ์ ์ฉํ๋ฉฐ, ์ปดํ์ผ๋ฌ์์ ์ ์ ์ผ๋ก ๊ฒฐ์ ๋ ์ ์๊ธฐ ๋๋ฌธ์ ๋ฐํ์์์ ํธ์ถ ๋์์ ๊ฒฐ์ ํ๊ณ , ํด๋น ๋ฉ์๋๋ฅผ ํธ์ถํ๊ฒ ๋๋ค. ๋ํ! Java 8 ์ดํ๋ถํฐ ๋์ ๋ ๋๋ค์์ด invokeDynamic์ ํ์ฉํ์ฌ ๊ตฌํ๋์์ผ๋ฉฐ, ํจ์ํ ์ธํฐํ์ด์ค ์ฒ๋ฆฌ๋ ๋ ์ ์ฉํ๊ฒ ํด์ค๋ค. (๋๋ค์์ด ํธ์ถ๋๋ฉด, invokedynamic์ ํตํด ํด๋น ๋๋ค์์ ์ฒ๋ฆฌํ ๋ฉ์๋๋ฅผ ๋์ ์ผ๋ก ์ฐ๊ฒฐํ๋ ์์ผ๋ก ๊ตฌํ๋์ด ์๋ค ๐ฆ)
String hi = "hi";
String java = " java";
String hello = hi + java;
์๋ฌดํผ, ์ ์ฝ๋์ ๋ฐ์ดํธ ์ฝ๋๋ฅผ ์ดํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ด ๋์จ๋ค.
L0
LINENUMBER 5 L0
LDC "hi"
ASTORE 1
L1
LINENUMBER 6 L1
LDC " java"
ASTORE 2
L2
LINENUMBER 8 L2
ALOAD 1
ALOAD 2
INVOKEDYNAMIC makeConcatWithConstants(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; [
// handle kind 0x6 : INVOKESTATIC
java/lang/invoke/StringConcatFactory.makeConcatWithConstants(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
// arguments:
"\u0001\u0001"
]
ASTORE 3
L3
LINENUMBER 9 L3
RETURN
L4
LOCALVARIABLE args [Ljava/lang/String; L0 L4 0
LOCALVARIABLE hi Ljava/lang/String; L1 L4 1
LOCALVARIABLE java Ljava/lang/String; L2 L4 2
LOCALVARIABLE hello Ljava/lang/String; L3 L4 3
MAXSTACK = 2
MAXLOCALS = 4
๋ฐ์ดํธ ์ฝ๋๋ฅผ ์กฐ๊ธ์ฉ ์ดํด๋ณด์.
L0
LINENUMBER 5 L0
LDC "hi"
ASTORE 1
LineNumber๋ ์ฝ๋์์ ๋ช ๋ฒ์งธ ์ค์ ์์นํ๋์ง ๋งํ๋ ๊ฒ์ด๋ค. LDC๋ ์ด๋ค ๋ฌธ์์ด์ ๋ก๋ํ๋์ง ๋ํ๋ด๋ฉฐ, ์ฌ๊ธฐ์๋ "hi"๋ผ๋ ๋ฌธ์์ด์ ๋ก๋ํ์ฌ ASTORE๋ฅผ ํตํด ์คํ์ ์๋ ๊ฐ์ฅ ์์ ์๋ ๊ฐ์ ๋ก์ปฌ ๋ณ์ ๋ฐฐ์ด์ ์ธ๋ฑ์ค 1๋ฒ์ ์ ์ฅํ์๋ค.
์ฆ, L0๊ณผ L1์์๋ "hi"์ " java"๋ผ๋ ๋ฌธ์์ด์ ๊ฐ๊ฐ์ ๋ณ์์๊ฒ ํ ๋นํ๋ค๊ณ ๋ณด๋ฉด ๋๋ค.
L2
LINENUMBER 8 L2
ALOAD 1
ALOAD 2
INVOKEDYNAMIC makeConcatWithConstants(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; [
// handle kind 0x6 : INVOKESTATIC
java/lang/invoke/StringConcatFactory.makeConcatWithConstants(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
// arguments:
"\u0001\u0001"
]
ASTORE 3
์ดํ, L2๋ฅผ ๋ณด๋ฉด 1, 2๋ฒ์ ๋ณ์๋ฅผ ALOAD๋ฅผ ํตํด ๊ฐ ๋ณ์์ ๊ฐ์ ์คํ์ ๋ฃ๊ณ , INVOKEDYNAMIC์ ํตํด 'makeConcatWithConstants'๋ฉ์๋๋ฅผ ํธ์ถํ ๊ฒ์ ํ์ธํ ์ ์๋ค. ํด๋น ๋ฉ์๋๋ฅผ ํตํด์ +์ ํตํด ๊ฐ์ ์ฐ๊ฒฐํ๋ ๊ฒ๊ณผ ๋์ผํ ์์ ์ ์งํํ๊ณ , ๋ฆฌํด๋ฐ์๋ค๊ณ ์๊ฐํ๋ฉด ๋๋ค. ๋ํ, ์ธ์๋ก ์ ๋ฌ๋๋ ์์๊ฐ๋ค์ ๋๊ฒจ์ฃผ๋๋ฐ, ์ฌ๊ธฐ์ ์ฌ์ฉ๋๋ "\u0001\u0001"์ ๊ฒฝ์ฐ String pool์ ์ ์ฅ๋์ด ์๋ ๊ฐ์ ์ฌ์ฉํ ๊ฒ์ด๋ค. ๊ทธ๋ฆฌ๊ณ , ๋ฐ์ ๊ฐ์ 3๋ฒ ์ธ๋ฑ์ค์ ๋ฃ์ด์ฃผ์๋ค.
L4
LOCALVARIABLE args [Ljava/lang/String; L0 L4 0
LOCALVARIABLE hi Ljava/lang/String; L1 L4 1
LOCALVARIABLE java Ljava/lang/String; L2 L4 2
LOCALVARIABLE hello Ljava/lang/String; L3 L4 3
MAXSTACK = 2
MAXLOCALS = 4
์ดํ, LOCALVARIABLE์ ํตํด ๋ณ์ ์ด๋ฆ๊ณผ, ํด๋น ๋ณ์์ ์ ํจ ๋ฒ์๋ฅผ ๋ํ๋ด๊ฒ ๋๋ฉฐ, ๋ง์ง๋ง์ ๋ณด๋ฉด 'hello'๋ผ๋ ๋ณ์์ 3๋ฒ ์ธ๋ฑ์ค์ ๊ฐ = ์ฆ, concat์ ์งํํ ๊ฐ์ ํ ๋นํด์ฃผ๋ ๊ฒ์ ํ์ธํ ์ ์๋ค. maxstack์ ์ฌ์ฉ๋์๋ ์คํ์ ์ต๋ ํฌ๊ธฐ๋ฅผ, maxlocals๋ ์ฌ์ฉ๋์๋ ๋ก์ปฌ ๋ณ์์ ์ต๋ ๊ฐ์๋ฅผ ์๋ฏธํ๋ค.
์๋ฌดํผ, invokeDynamic์ ํ์ฉํ ๋๋ถ์ ์ปดํ์ผ๋ฌ๋ StringBuilder๋ฅผ ์ฌ์ฉํ ํ์์์ด ๋์ ์ผ๋ก ๋ฉ์๋๋ฅผ ์ฃผ์ ๋ฐ์์ ์ฒ๋ฆฌํ ์ ์๊ฒ ๋์๋ค. StringBuilder์์ append()๋ ๋ด๋ถ ๋ฌธ์์ด ๋ฒํผ (char[])๋ฅผ ์์ฑํ๋ฉฐ ๋ฌธ์์ด์ ์ ์ฅํ๋ ๊ตฌ์กฐ์ด๊ธฐ ๋๋ฌธ์ ๊ฒฐ๊ตญ ๋ฉ๋ชจ๋ฆฌ ์๋น๊ฐ ๋ฐ์ํ๋๋ฐ (StringBuilder์ ์ด๊ธฐ ์ฌ์ด์ฆ๋ 16์ผ๋ก ๊ณ ์ ์ด๋ผ, ๋ฌธ์์ด ๊ธธ์ด๊ฐ ๊ธธ์ด์ง๋ฉด ๊ณ์ ๋ณต์ฌ ์์ ์ด ์ผ์ด๋์ ๋นํจ์จ์ ์ด๋ค), ์ด๋ฌํ ์ธก๋ฉด์์ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ค์ผ ์ ์์ผ๋ฉฐ, ๋ฐ์ดํธ ์ฝ๋์ ๋ณ๊ฒฝ์ด ํ์ํ์ง ์๊ธฐ ๋๋ฌธ์ ๋ ํจ์จ์ ์ผ๋ก ์คํํ ์ ์๋ค๋ ์ ์ด ์ฅ์ ์ด๋ค. (๋ฐ์ดํธ์ฝ๋์ ์ต์ ํ ์ธก๋ฉด์์ ์ข์ ๊ฒ ๊ฐ๋ค!)
-> ์ด ๋ถ๋ถ์ ์ดํฉํฐ๋ธ ์๋ฐ ๊ธ์ ์ ๋ฆฌํ ๋ ๋ค์ ํ ๋ฒ ์ธ๊ธํ๊ฒ ๋ค ๐
String์ ๋ํด ์์ฑํ๋ ค๊ณ ํ์ผ๋, ์๊พธ ๋ชจ๋ฅด๋ ๊ฐ๋ ์ด ์๊ฒจ์ ๋ง์ด ํค๋งจ ๋๋์ด๋ค ;ใ ;
์์ ๋ ์ด๋ ต๋ค๊ณ ๋๊ผ๋ ๊ฑธ ์ต๋ํ ์ฝ๊ฒ ํ์๋๋ฐ ๋ค๋ฅธ๋ถ๋ค์ด ๋ด๋ ์ดํด๊ฐ ๋์ผ๋ฉด ์ข๊ฒ ๋ค.
์์ผ๋ก๋ ํ์ดํ ... ๐ช