itsource

Java 문자열 풀은 무엇이며 "s"는 새로운 문자열과 어떻게 다릅니까?

mycopycode 2022. 9. 16. 23:10
반응형

Java 문자열 풀은 무엇이며 "s"는 새로운 문자열과 어떻게 다릅니까?

String Pool은 무슨 뜻입니까?그리고 다음 선언의 차이점은 무엇입니까?

String s = "hello";
String s = new String("hello");

JVM에 의한 이 두 문자열의 저장에는 차이가 있습니까?

문자열 풀은 문자열 인터닝 개념에 대한 JVM의 특별한 구현입니다.

컴퓨터 과학에서 문자열 인터닝은 각 개별 문자열 값의 복사본을 하나만 저장하는 방식이며, 이는 불변해야 합니다.문자열을 인터닝하면 일부 문자열 처리 태스크의 시간 또는 공간 효율이 향상되지만 문자열 작성 또는 삽입 시 시간이 더 소요됩니다.고유한 값은 문자열 내부 풀에 저장됩니다.

기본적으로 문자열 내부 풀을 사용하면 실행 시 풀에 불변의 문자열을 보존하여 메모리를 절약할 수 있습니다.이것에 의해, 애플리케이션의 영역은, 공통 문자열의 인스턴스를 복수 작성하는 대신에, 그 인스턴스를 재이용할 수 있습니다.

흥미로운 측면 참고로서 문자열 인터닝은 플라이웨이트 설계 패턴의 한 예입니다.

Flyweight는 소프트웨어 설계 패턴입니다.플라이웨이트는 다른 유사한 개체와 가능한 한 많은 데이터를 공유함으로써 메모리 사용을 최소화하는 개체입니다.단순한 반복 표현으로 허용할 수 없는 양의 메모리가 사용될 경우 개체를 대량으로 사용하는 방법입니다.

문자열 풀에서는 문자열 상수를 재사용할 수 있습니다.이것은 Java의 문자열은 불변하기 때문에 가능합니다.Java 코드의 모든 장소에서 동일한 문자열 상수를 반복할 경우 시스템에 해당 문자열의 복사본이 하나만 있을 수 있습니다. 이것이 이 메커니즘의 장점 중 하나입니다.

「 」를 사용하는 String s = "string constant";문자열 풀에 있는 복사본을 얻을 수 있습니다. 을 을(를) 사용할 수 있습니다.String s = new String("string constant");이치노

JLS

Andrew에 의해 언급되었듯이 이 개념은 JLS에 의해 "interning"이라고 불립니다.

JLS 7 3.10.5로부터의 관련 패스워드:

또한 문자열 리터럴은 항상 클래스 String의 동일한 인스턴스를 참조합니다.이는 문자열 리터럴(또는 일반적으로는 상수 표현의 값인 문자열('15.28))이 String.intern 메서드를 사용하여 고유한 인스턴스를 공유하기 위해 "interned"되기 때문입니다.

예 3.10.5-1.문자열 리터럴

컴파일 유닛(77.3)으로 구성된 프로그램:

package testPackage;
class Test {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.print((hello == "Hello") + " ");
        System.out.print((Other.hello == hello) + " ");
        System.out.print((other.Other.hello == hello) + " ");
        System.out.print((hello == ("Hel"+"lo")) + " ");
        System.out.print((hello == ("Hel"+lo)) + " ");
        System.out.println(hello == ("Hel"+lo).intern());
    }
}
class Other { static String hello = "Hello"; }

컴파일 유닛:

package other;
public class Other { public static String hello = "Hello"; }

는 다음과 같은 출력을 생성합니다.

true true true true false true

JVMS

JVMS 7 5.1의 특징:

문자열 리터럴은 클래스 String 인스턴스를 참조하는 것으로 클래스 또는 인터페이스의 바이너리 표현 중 CONST_String_info 구조('4.4.3)에서 파생됩니다.CONST_String_info 구조체는 문자열 리터럴을 구성하는 Unicode 코드 포인트의 시퀀스를 제공합니다.

Java 프로그래밍 언어에서는 동일한 문자열 리터럴(같은 코드포인트 시퀀스를 포함하는 리터럴)이 클래스 문자열의 동일한 인스턴스(JLS ls 3.10.5)를 참조해야 합니다.또한 String.intern 메서드가 임의의 문자열로 호출된 경우 그 문자열이 리터럴로 표시되었을 때 반환되는 동일한 클래스인스턴스에 대한 참조가 됩니다.따라서 다음 식에는 true 값을 지정해야 합니다.

("a" + "b" + "c").intern() == "abc"

문자열 리터럴을 도출하기 위해 Java Virtual Machine은 CONTANT_String_info 구조에 의해 지정된 코드 포인트의 시퀀스를 조사합니다.

  • 메서드 String.intern이 CONST_String_info 구조에 의해 지정된 것과 동일한 Unicode 코드 포인트 시퀀스를 포함하는 클래스 String 인스턴스에서 이전에 호출된 경우 문자열 리터럴 파생 결과는 클래스 String의 동일한 인스턴스를 참조합니다.

  • 그렇지 않으면 CONST_String_info 구조에 의해 지정된 Unicode 코드포인트 시퀀스를 포함하는 클래스 String의 새로운 인스턴스가 생성됩니다.이 클래스인스턴스에 대한 참조는 문자열 리터럴파생 결과입니다.마지막으로 새로운 String 인스턴스의 intern 메서드가 호출됩니다.

바이트 코드

OpenJDK 7에서의 바이트 코드 실장을 살펴보는 것도 도움이 됩니다.

디컴파일 할 경우:

public class StringPool {
    public static void main(String[] args) {
        String a = "abc";
        String b = "abc";
        String c = new String("abc");
        System.out.println(a);
        System.out.println(b);
        System.out.println(a == c);
    }
}

상수 풀에 있습니다.

#2 = String             #32   // abc
[...]
#32 = Utf8               abc

그리고.main:

 0: ldc           #2          // String abc
 2: astore_1
 3: ldc           #2          // String abc
 5: astore_2
 6: new           #3          // class java/lang/String
 9: dup
10: ldc           #2          // String abc
12: invokespecial #4          // Method java/lang/String."<init>":(Ljava/lang/String;)V
15: astore_3
16: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
19: aload_1
20: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
23: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #6          // Method java/io/PrintStream.println:(Ljava/lang/String;)V
30: getstatic     #5          // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_1
34: aload_3
35: if_acmpne     42
38: iconst_1
39: goto          43
42: iconst_0
43: invokevirtual #7          // Method java/io/PrintStream.println:(Z)V

주의:

  • 0그리고.3: 동일ldc #2상수가 로드됩니다(리터럴).
  • 12: 새로운 문자열인스턴스가 생성됩니다.#2인수로)
  • 35:a그리고.c일반 객체와 비교됩니다.if_acmpne

상수 문자열의 표현은 바이트 코드에서 매우 매직합니다.

  • 일반 개체와 달리 전용 CONTANT_String_info 구조를 가집니다(예:new String)
  • 구조체는 데이터를 포함하는 CONST_Utf8_info 구조를 가리킵니다.이것은 문자열을 나타내는 데 필요한 유일한 데이터입니다.

을 가리킬 가 Utf8에 의해 .ldc.

필드에 대해서도 비슷한 테스트를 실시했습니다.

  • static final String s = "abc"상수속성을 통해 상수 테이블을 가리킵니다.
  • 는 이러한 가지고 않지만 be종필필음음음음음음음음음음음음음음음음음 non non non non non non non non non non non non로 초기화할 수 .ldc

결론: 문자열 풀에 대한 직접 바이트 코드가 지원되며 메모리 표현은 효율적입니다.

보너스: 직접 바이트 코드를 지원하지 않는 Integer 풀과 비교합니다(즉, no).CONSTANT_String_info★★★★★★★★★★★★★★★★★★」

스트링 오브젝트는 기본적으로 스트링 리터럴 주위에 래퍼입니다.불필요한 개체 생성을 방지하기 위해 고유한 문자열 개체가 풀링되며, JVM은 문자열 리터럴을 내부적으로 풀링하기로 결정할 수 있습니다.컴파일러가 이를 지원한다면 여러 번 참조되는 문자열 상수에 대한 직접 바이트 코드도 지원됩니다.

을 쓸 는 '어울리다'라고 말해요.String str = "abc";풀 내의 오브젝트가 사용됩니다.「 」를 사용하고 String str = new String("abc");새로운 오브젝트가 생성되지만 기존 문자열 리터럴은 (컴파일 시) JVM 레벨 또는 바이트 코드레벨로 재사용할 수 있습니다.

하고 For를 사용하여 할 수 .==연산자를 사용하여 객체의 동일성을 확인합니다.예제에서는 음음 in in in in 。string.value is is is is の is is 。String는 사용되는 문자열 리터럴을 유지합니다.비공개이기 때문에 반영을 통해 접근해야 합니다.

public class InternTest {
    public static void main(String[] args) {
        String rehi = "rehi";
        String rehi2 = "rehi";
        String rehi2a = "not rehi";
        String rehi3 = new String("rehi");
        String rehi3a = new String("not rehi");
        String rehi4 = new String(rehi);
        String rehi5 = new String(rehi2);
        String rehi6 = new String(rehi2a);

        String[] arr  = new String[] { rehi, rehi2, rehi2a, rehi3, rehi3a, rehi4, rehi5, rehi6 };
        String[] arr2 = new String[] { "rehi", "rehi (2)", "not rehi", "new String(\"rehi\")", "new String(\"not rehi\")", "new String(rehi)", "new String(rehi (2))", "new String(not rehi)" };

        Field f;
        try {
            f = String.class.getDeclaredField("value");
            f.setAccessible(true);
        } catch (NoSuchFieldException | SecurityException e) {
            throw new IllegalStateException(e);
        }

        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length; j++) {
                System.out.println("i: " +arr2[i]+", j: " +arr2[j]);
                System.out.println("i==j: " + (arr[i] == arr[j]));
                System.out.println("i equals j: " + (arr[i].equals(arr[j])));
                try {
                    System.out.println("i.value==j.value: " + (f.get(arr[i]) == f.get(arr[j])));
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    throw new IllegalStateException(e);
                }
                System.out.println("========");
            }
        }
    }
}

출력:

i: rehi, j: rehi
i==j: true
i equals j: true
i.value==j.value: true
========
i: rehi, j: rehi (2)
i==j: true
i equals j: true
i.value==j.value: true
========
i: rehi, j: not rehi
i==j: false
i equals j: false
i.value==j.value: false
========
i: rehi, j: new String("rehi")
i==j: false
i equals j: true
i.value==j.value: true
========
i: rehi, j: new String("not rehi")
i==j: false
i equals j: false
i.value==j.value: false
========
i: rehi, j: new String(rehi)
i==j: false
i equals j: true
i.value==j.value: true
========
i: rehi, j: new String(rehi (2))
i==j: false
i equals j: true
i.value==j.value: true
========
i: rehi, j: new String(not rehi)
i==j: false
i equals j: false
i.value==j.value: false
========
i: rehi (2), j: rehi
i==j: true
i equals j: true
i.value==j.value: true
========
i: rehi (2), j: rehi (2)
i==j: true
i equals j: true
i.value==j.value: true
========
i: rehi (2), j: not rehi
i==j: false
i equals j: false
i.value==j.value: false
========
i: rehi (2), j: new String("rehi")
i==j: false
i equals j: true
i.value==j.value: true
========
i: rehi (2), j: new String("not rehi")
i==j: false
i equals j: false
i.value==j.value: false
========
i: rehi (2), j: new String(rehi)
i==j: false
i equals j: true
i.value==j.value: true
========
i: rehi (2), j: new String(rehi (2))
i==j: false
i equals j: true
i.value==j.value: true
========
i: rehi (2), j: new String(not rehi)
i==j: false
i equals j: false
i.value==j.value: false
========
i: not rehi, j: rehi
i==j: false
i equals j: false
i.value==j.value: false
========
i: not rehi, j: rehi (2)
i==j: false
i equals j: false
i.value==j.value: false
========
i: not rehi, j: not rehi
i==j: true
i equals j: true
i.value==j.value: true
========
i: not rehi, j: new String("rehi")
i==j: false
i equals j: false
i.value==j.value: false
========
i: not rehi, j: new String("not rehi")
i==j: false
i equals j: true
i.value==j.value: true
========
i: not rehi, j: new String(rehi)
i==j: false
i equals j: false
i.value==j.value: false
========
i: not rehi, j: new String(rehi (2))
i==j: false
i equals j: false
i.value==j.value: false
========
i: not rehi, j: new String(not rehi)
i==j: false
i equals j: true
i.value==j.value: true
========
i: new String("rehi"), j: rehi
i==j: false
i equals j: true
i.value==j.value: true
========
i: new String("rehi"), j: rehi (2)
i==j: false
i equals j: true
i.value==j.value: true
========
i: new String("rehi"), j: not rehi
i==j: false
i equals j: false
i.value==j.value: false
========
i: new String("rehi"), j: new String("rehi")
i==j: true
i equals j: true
i.value==j.value: true
========
i: new String("rehi"), j: new String("not rehi")
i==j: false
i equals j: false
i.value==j.value: false
========
i: new String("rehi"), j: new String(rehi)
i==j: false
i equals j: true
i.value==j.value: true
========
i: new String("rehi"), j: new String(rehi (2))
i==j: false
i equals j: true
i.value==j.value: true
========
i: new String("rehi"), j: new String(not rehi)
i==j: false
i equals j: false
i.value==j.value: false
========
i: new String("not rehi"), j: rehi
i==j: false
i equals j: false
i.value==j.value: false
========
i: new String("not rehi"), j: rehi (2)
i==j: false
i equals j: false
i.value==j.value: false
========
i: new String("not rehi"), j: not rehi
i==j: false
i equals j: true
i.value==j.value: true
========
i: new String("not rehi"), j: new String("rehi")
i==j: false
i equals j: false
i.value==j.value: false
========
i: new String("not rehi"), j: new String("not rehi")
i==j: true
i equals j: true
i.value==j.value: true
========
i: new String("not rehi"), j: new String(rehi)
i==j: false
i equals j: false
i.value==j.value: false
========
i: new String("not rehi"), j: new String(rehi (2))
i==j: false
i equals j: false
i.value==j.value: false
========
i: new String("not rehi"), j: new String(not rehi)
i==j: false
i equals j: true
i.value==j.value: true
========
i: new String(rehi), j: rehi
i==j: false
i equals j: true
i.value==j.value: true
========
i: new String(rehi), j: rehi (2)
i==j: false
i equals j: true
i.value==j.value: true
========
i: new String(rehi), j: not rehi
i==j: false
i equals j: false
i.value==j.value: false
========
i: new String(rehi), j: new String("rehi")
i==j: false
i equals j: true
i.value==j.value: true
========
i: new String(rehi), j: new String("not rehi")
i==j: false
i equals j: false
i.value==j.value: false
========
i: new String(rehi), j: new String(rehi)
i==j: true
i equals j: true
i.value==j.value: true
========
i: new String(rehi), j: new String(rehi (2))
i==j: false
i equals j: true
i.value==j.value: true
========
i: new String(rehi), j: new String(not rehi)
i==j: false
i equals j: false
i.value==j.value: false
========
i: new String(rehi (2)), j: rehi
i==j: false
i equals j: true
i.value==j.value: true
========
i: new String(rehi (2)), j: rehi (2)
i==j: false
i equals j: true
i.value==j.value: true
========
i: new String(rehi (2)), j: not rehi
i==j: false
i equals j: false
i.value==j.value: false
========
i: new String(rehi (2)), j: new String("rehi")
i==j: false
i equals j: true
i.value==j.value: true
========
i: new String(rehi (2)), j: new String("not rehi")
i==j: false
i equals j: false
i.value==j.value: false
========
i: new String(rehi (2)), j: new String(rehi)
i==j: false
i equals j: true
i.value==j.value: true
========
i: new String(rehi (2)), j: new String(rehi (2))
i==j: true
i equals j: true
i.value==j.value: true
========
i: new String(rehi (2)), j: new String(not rehi)
i==j: false
i equals j: false
i.value==j.value: false
========
i: new String(not rehi), j: rehi
i==j: false
i equals j: false
i.value==j.value: false
========
i: new String(not rehi), j: rehi (2)
i==j: false
i equals j: false
i.value==j.value: false
========
i: new String(not rehi), j: not rehi
i==j: false
i equals j: true
i.value==j.value: true
========
i: new String(not rehi), j: new String("rehi")
i==j: false
i equals j: false
i.value==j.value: false
========
i: new String(not rehi), j: new String("not rehi")
i==j: false
i equals j: true
i.value==j.value: true
========
i: new String(not rehi), j: new String(rehi)
i==j: false
i equals j: false
i.value==j.value: false
========
i: new String(not rehi), j: new String(rehi (2))
i==j: false
i equals j: false
i.value==j.value: false
========
i: new String(not rehi), j: new String(not rehi)
i==j: true
i equals j: true
i.value==j.value: true
========

아무도 그 질문에 직접 대답하지 않았지만 대부분의 답변에는 많은 찬성이 있다는 것이 곤혹스럽다.

즉, 첫 번째 엔트리는 String Pool에 엔트리를 작성하고, 두 번째 엔트리는 String 개체를 새로 만듭니다(비용이 많이 듭니다).

두 개체 모두 힙에 있습니다.둘 다에 대한 참조는 스레드의 스택에 있습니다.

http://www.journaldev.com/797/what-is-java-string-pool에서는 를 실현하는 방법을 명확하게 설명합니다.

언급URL : https://stackoverflow.com/questions/2486191/what-is-the-java-string-pool-and-how-is-s-different-from-new-strings

반응형