programing

Java String interning이란 무엇입니까?

firstcheck 2022. 7. 9. 09:35
반응형

Java String interning이란 무엇입니까?

Java에서의 String Interning이란 무엇이며 언제 사용해야 하며 그 이유는 무엇입니까?

http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#intern()

기본적으로 일련의 문자열에서 String.intern()을 실행하면 동일한 내용을 가진 모든 문자열이 동일한 메모리를 공유합니다.따라서 'john'이 1000번 나타나는 이름 목록이 있는 경우, 인터닝을 통해 'john' 한 명만 실제로 할당됩니다.

이것은, 프로그램의 메모리 요건을 삭감하는데 도움이 됩니다.그러나 캐시는 JVM에 의해 영구 메모리 풀에서 유지되며, 일반적으로 힙에 비해 크기가 제한되므로 중복되는 값이 너무 많지 않은 경우에는 intern을 사용하지 마십시오.


intern() 사용 시 메모리 제약에 대한 자세한 내용

한편으로 문자열 중복을 내부화하는 것으로 제거할 수 있습니다.문제는 내부 문자열이 클래스, 메서드 및 기타 내부 JVM 개체와 같은 비사용자 개체용으로 예약된 JVM 영역인 영구 생성으로 이동한다는 것입니다.이 영역의 크기는 제한되어 있으며 일반적으로 힙보다 훨씬 작습니다.String 상에서 intern()을 호출하면 힙에서 영구 생성으로 이동하기 때문에 PermGen 공간이 부족해질 위험이 있습니다.

-- 송신원: http://www.codeinstructions.com/2009/01/busting-javalangstringintern-myths.html


JDK7(즉, HotSpot에서는)부터 뭔가 바뀌었습니다.

JDK 7에서는 내장 문자열은 Java 힙의 영구 세대에 할당되지 않고 어플리케이션에 의해 작성된 다른 오브젝트와 함께 Java 힙의 주요 부분(신세대 및 구세대로 알려져 있음)에 할당됩니다.이 변경으로 메인 Java 힙에 상주하는 데이터가 많아지고 영구 생성 데이터가 줄어들기 때문에 힙 크기를 조정할 필요가 있을 수 있습니다.대부분의 응용 프로그램은 이 변경으로 인해 힙 사용률에서 비교적 작은 차이만 볼 수 있지만, 많은 클래스를 로드하거나 String.intern() 메서드를 많이 사용하는 큰 응용 프로그램에서는 더 큰 차이를 볼 수 있습니다.

-- Java SE 7의 기능과 확장 기능

업데이트: 내장된 문자열은 Java 7 이후 메인 힙에 저장됩니다.http://www.oracle.com/technetwork/java/javase/jdk7-relnotes-418459.html#jdk7changes

아래 코드를 실행하면 왜 동등하게 되는지와 같은 "귀중한 인터뷰" 질문이 몇 가지 있습니다.

String s1 = "testString";
String s2 = "testString";
if(s1 == s2) System.out.println("equals!");

하고 싶을 는 스트링을 사용하는 것이 .equals()위의 출력은 동일합니다.왜냐하면testString컴파일러에 의해 이미 인턴이 되어 있습니다.앞의 답변에서 보듯이 인턴 방식을 사용하여 문자열을 직접 인턴할 수 있습니다.

JLS

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.1에 따르면 인터닝은 전용 기능을 사용하여 마법처럼 효율적으로 구현됩니다.CONSTANT_String_info구조(일반적인 표현을 가진 대부분의 다른 개체와 달리):

문자열 리터럴은 클래스 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를 사용합니다).#2argument(인수)로)
  • 35a ★★★★★★★★★★★★★★★★★」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★★★★★★★★★★★★★★★★★★」

Java 8 이상용 업데이트Java 8에서는 PermGen(영구 생성) 공간이 삭제되고 Meta Space로 대체됩니다.문자열 풀 메모리가 JVM의 힙으로 이동합니다.

Java 7과 비교하여 힙의 String 풀 크기가 증가합니다.따라서 내부화된 문자열을 위한 공간은 더 많지만 애플리케이션 전체에 대한 메모리는 더 적습니다.

2개의 할 때' 2개의 오브젝트(레퍼런스)를 비교할 때 .==사물의 할 때 합니다.equals'이렇게 하다'는 것을 말합니다.

다음 코드를 확인합니다.

String value1 = "70";
String value2 = "70";
String value3 = new Integer(70).toString();

결과:

value1 == value2> »

value1 == value3> false(false)

value1.equals(value3)> »

value1 == value3.intern()> »

'어울리지 않다'를사용해야 .equals현악기 해서 and게 and and and and and and and andintern()도움이 됩니다.

문자열은 객체이며 Java의 모든 객체는 항상 힙 공간에만 저장되므로 모든 문자열은 힙 공간에 저장됩니다.단, Java는 새로운 키워드를 사용하지 않고 작성된 문자열을 "string pool"이라고 불리는 힙 공간의 특수 영역에 보관합니다.Java는 new 키워드를 사용하여 작성된 문자열을 일반 힙 공간에 유지합니다.

문자열 풀의 목적은 고유한 문자열 세트를 유지하는 것입니다.new 키워드를 사용하지 않고 새로운 문자열을 작성하면 동일한 문자열이 문자열 풀에 이미 존재하는지 여부를 확인합니다.이 경우 Java는 동일한 String 객체에 대한 참조를 반환하고 그렇지 않으면 문자열 풀에 새로운 String 객체를 생성하여 참조를 반환합니다.따라서, 예를 들어, 코드에 「hello」라고 하는 문자열을 2회 사용하면, 같은 문자열을 참조할 수 있습니다.다음 코드와 같이 == 연산자를 사용하여 두 개의 서로 다른 기준 변수를 비교하여 이 이론을 실제로 테스트할 수 있습니다.

String str1 = "hello";
String str2 = "hello";
System.out.println(str1 == str2); //prints true

String str3 = new String("hello");
String str4 = new String("hello");

System.out.println(str1 == str3); //prints false
System.out.println(str3 == str4); //prints false 

== 연산자는 단순히 두 개의 참조가 동일한 개체를 가리키는지 확인하고, 가리키면 true를 반환합니다.위의 코드에서 str2는 이전에 작성된 동일한 String 객체에 대한 참조를 가져옵니다.단, str3str4는 전혀 다른2개의 String 객체에 대한 참조를 가져옵니다.그렇기 때문에 str1 == str2는 true를 반환하지만 str1 == str3str3 == str4는 false를 반환합니다.실제로 새로운 String("hello")실행하면 프로그램 내의 임의의 장소에서 "hello"라는 문자열이 처음 사용되는 경우 1개 대신 2개의 String 객체가 생성됩니다.하나는 따옴표로 둘러싸인 문자열이 사용되고 다른 하나는 새로운 키워드가 사용되고 있기 때문에 일반 힙 공간에 생성됩니다.

문자열 풀링은 동일한 값을 포함하는 여러 String 개체가 생성되지 않도록 함으로써 프로그램 메모리를 절약하는 Java의 방법입니다.String의 intern 메서드를 사용하면 새로운 키워드를 사용하여 작성된 문자열의 문자열을 문자열 풀에서 가져올 수 있습니다.이것은 문자열 객체의 "interning"이라고 불립니다.예를들면,

String str1 = "hello";
String str2 = new String("hello");
String str3 = str2.intern(); //get an interned string obj

System.out.println(str1 == str2); //prints false
System.out.println(str1 == str3); //prints true

OCP Java SE 11 프로그래머, Deshmukh

문자열 인터닝은 컴파일러에 의한 최적화 기술입니다.1개의 컴파일 유닛에 2개의 동일한 문자열 리터럴이 있는 경우 생성되는 코드는 어셈블리 내의 해당 리터럴(큰따옴표로 둘러싸인 문자)의 모든 인스턴스에 대해 작성된 문자열 오브젝트가1개뿐임을 보증합니다.

저는 C# 출신이기 때문에 예를 들어 설명하겠습니다.

object obj = "Int32";
string str1 = "Int32";
string str2 = typeof(int).Name;

다음 비교 결과:

Console.WriteLine(obj == str1); // true
Console.WriteLine(str1 == str2); // true    
Console.WriteLine(obj == str2); // false !?

참고 1: 객체는 참조별로 비교됩니다.

2: type of(int).이름은 반사 방식으로 평가되므로 컴파일 시 평가되지 않습니다.여기에서는 컴파일 시에 비교합니다.

결과 분석: 1) 둘 다 같은 리터럴을 포함하므로 생성된 코드에는 "Int32"를 참조하는 개체가 하나만 있기 때문에 true입니다. 1을 참조해 주세요.

2) 두 값의 내용이 동일한지 확인하기 때문에 true입니다.

3) str2와 obj는 같은 리터럴을 가지고 있지 않기 때문에 FALSE입니다. 2를 참조해 주세요.

Java interning() method basically makes sure that if String object is present in SCP, If yes then it returns that object and if not then creates that objects in SCP and return its references

for eg: String s1=new String("abc");
        String s2="abc";
        String s3="abc";

s1==s2// false, because 1 object of s1 is stored in heap and other in scp(but this objects doesn't have explicit reference) and s2 in scp
s2==s3// true

now if we do intern on s1
s1=s1.intern() 

//JVM checks if there is any string in the pool with value “abc” is present? Since there is a string object in the pool with value “abc”, its reference is returned.
Notice that we are calling s1 = s1.intern(), so the s1 is now referring to the string pool object having value “abc”.
At this point, all the three string objects are referring to the same object in the string pool. Hence s1==s2 is returning true now.

힙 오브젝트 참조를 사용함으로써 대응하는 SCP 오브젝트 참조를 원하는 경우 intern() 메서드를 사용해야 합니다.

:

class InternDemo
{
public static void main(String[] args)
{
String s1=new String("smith");
String s2=s1.intern();
String s3="smith";
System.out.println(s2==s3);//true
}
}

내부 흐름도

언급URL : https://stackoverflow.com/questions/10578984/what-is-java-string-interning

반응형