DevLog ๐Ÿ˜ถ

[Java] ๋ฌธ์ž์—ด - String pool๊ณผ String concatenation ๋ณธ๋ฌธ

โœ๏ธ/Java

[Java] ๋ฌธ์ž์—ด - String pool๊ณผ String concatenation

dolmeng2 2023. 3. 5. 02:07

์šฐํ…Œ์ฝ”์—์„œ ๋ฌธ์ž์—ด ๊ด€๋ จ ์ˆ˜์—…์„ ๋“ฃ๋‹ค๊ฐ€, ์ด๊ฒƒ์ €๊ฒƒ ๊ถ๊ธˆํ•ด์„œ ์ฐพ์•„๋ดค๋Š”๋ฐ ๋ธ”๋กœ๊ทธ ๊ธ€๋กœ ์ž‘์„ฑํ•˜๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์•„์„œ ์ด๋ ‡๊ฒŒ ์ž‘์„ฑํ•œ๋‹ค ๐Ÿค—

์˜์‹์˜ ํ๋ฆ„๋Œ€๋กœ ๊ถ๊ธˆํ•œ ์ ๋“ค์„ ์ฐพ์•„๋ณธ ๊ฑฐ์—ฌ์„œ ์ƒ๋‹นํžˆ ๋‘์„œ๊ฐ€ ์—†์„ ๊ฒƒ ๊ฐ™๋‹ค ๐Ÿฅฒ


 

โœ”๏ธ 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์— ๋Œ€ํ•ด ์ž‘์„ฑํ•˜๋ ค๊ณ  ํ–ˆ์œผ๋‚˜, ์ž๊พธ ๋ชจ๋ฅด๋Š” ๊ฐœ๋…์ด ์ƒ๊ฒจ์„œ ๋งŽ์ด ํ—ค๋งจ ๋Š๋‚Œ์ด๋‹ค ;ใ……;

์ˆ˜์—… ๋•Œ ์–ด๋ ต๋‹ค๊ณ  ๋Š๊ผˆ๋˜ ๊ฑธ ์ตœ๋Œ€ํ•œ ์‰ฝ๊ฒŒ ํ’€์—ˆ๋Š”๋ฐ ๋‹ค๋ฅธ๋ถ„๋“ค์ด ๋ด๋„ ์ดํ•ด๊ฐ€ ๋์œผ๋ฉด ์ข‹๊ฒ ๋‹ค.

์•ž์œผ๋กœ๋„ ํŒŒ์ดํŒ…... ๐Ÿ’ช

Comments