programing

Java 6 및 Java 7에서 다르게 작동하는 intern ()

projobs 2021. 1. 15. 07:29
반응형

Java 6 및 Java 7에서 다르게 작동하는 intern ()


class Test {
    public static void main(String...args) {
        String s1 = "Good";
        s1 = s1 + "morning";
        System.out.println(s1.intern());
        String s2 = "Goodmorning";
        if (s1 == s2) {
            System.out.println("both are equal");
        }
    }
}

이 코드는 자바 6 자바 6 자바 7에 다른 출력을 생성하는 s1==s2상태로 되돌아 false앤드 자바 7 s1==s2복귀 true. 왜?

이 프로그램이 Java 6 및 Java 7에서 다른 출력을 생성하는 이유는 무엇입니까?


JDK7은 이전과 다른 방식으로 인턴을 처리하는 것 같습니다.
빌드 1.7.0-b147로 테스트했고 "둘 다 같음"을 얻었지만 1,6.0_24로 실행할 때 (동일한 바이트 코드) 메시지가 표시되지 않습니다.
또한 String b2 =...소스 코드에서 해당 줄의 위치에 따라 다릅니다 . 다음 코드도 메시지를 출력하지 않습니다.

class Test {
   public static void main(String... args) {
      String s1 = "Good";
      s1 = s1 + "morning";

      String s2 = "Goodmorning";
      System.out.println(s1.intern());  //just changed here s1.intern() and the if condition runs true   

      if(s1 == s2) {
         System.out.println("both are equal");
      } //now it works.
   }
}

intern문자열 풀에서 문자열을 찾지 못한 후 실제 인스턴스 s1을 풀에 삽입하는 것처럼 보입니다 . JVM은 s2가 생성 될 때 해당 풀을 사용하므로 s1과 동일한 참조를 다시 가져옵니다. 반면에 s2가 먼저 생성되면 해당 참조가 풀에 저장됩니다.
이는 Java 힙의 영구 생성에서 인턴 된 문자열을 이동 한 결과 일 수 있습니다.

여기에서 찾을 수 있습니다 : JDK 7에서 해결 된 중요한 RFE

JDK 7에서 인턴 된 문자열은 더 이상 Java 힙의 영구 생성에 할당되지 않고 대신 응용 프로그램에서 만든 다른 개체와 함께 Java 힙의 주요 부분 (젊은 세대 및 이전 세대라고 함)에 할당됩니다. . 이 변경으로 인해 기본 Java 힙에 더 많은 데이터가 있고 영구 생성에 더 적은 데이터가 있으므로 힙 크기를 조정해야 할 수 있습니다. 대부분의 응용 프로그램은 이러한 변경으로 인해 힙 사용량에서 상대적으로 작은 차이 만 볼 수 있지만 많은 클래스를로드하거나 String.intern () 메서드를 많이 사용하는 큰 응용 프로그램에서는 더 큰 차이를 볼 수 있습니다.

그것이 버그인지 그리고 어떤 버전인지 확실하지 않습니다. JLS 3.10.5는

계산 된 문자열을 명시 적으로 삽입 한 결과는 동일한 내용을 가진 기존 리터럴 문자열과 동일한 문자열입니다.

그래서 질문은 어떻게 기존의 해석, 컴파일 시간 또는 실행 시간입니다. "Goodmorning"이 이미 존재하는지 여부입니다.
나는 그것이 7 이전에 구현 된 방식을 선호합니다 ...


예제에서 불필요한 세부 사항을 생략하겠습니다.

class Test {
    public static void main(String... args) {
        String s1 = "Good";
        s1 = s1 + "morning";
        System.out.println(s1 == s1.intern()); // Prints true for jdk7, false - for jdk6.
    }
}

String#intern블랙 박스로 생각해 봅시다 . 몇 가지 테스트 사례를 실행 한 결과 구현이 다음과 같다고 결론을 내릴 수 있습니다.

Java 6 :
this에 다음과 같은 개체가 포함되어 있으면 해당 개체에 대한 참조를 반환하고, 그렇지 않으면 새 문자열 (같음)을 만들고 this, 풀에 넣고, 생성 된 인스턴스에 대한 참조를 반환합니다.

Java 7 :
this에 다음과 같은 객체가 포함되어 있으면 해당 객체에 대한 참조를 반환하고 그렇지 않으면 this풀에 넣고 this.

Java 6이나 Java 7 모두 메소드 계약을 위반하지 않습니다 .

새로운 인턴 메소드 동작이이 버그 수정의 결과 인 것 같습니다 : http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6962931 .


==참조를 비교합니다. intern 메소드는 동일한 값을 가진 문자열이 동일한 참조를 갖도록합니다.

String.intern 메소드에 대한 javadoc은 다음을 설명합니다.

공용 문자열 intern ()

문자열 객체에 대한 정식 표현을 반환합니다.

처음에 비어있는 문자열 풀은 String 클래스에 의해 비공개로 유지됩니다.

When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.

It follows that for any two strings s and t, s.intern() == t.intern() is true if and only if s.equals(t) is true.

All literal strings and string-valued constant expressions are interned. String literals are defined in §3.10.5 of the Java Language Specification

Returns: a string that has the same contents as this string, but is guaranteed to be from a pool of unique strings.

So without interning the compiler looks at the constants in the java code and builds its constant pool from that. There is a different pool maintained by the String class, and interning checks the string passed in against the pool and makes sure the reference is unique (so that == will work).


In jdk6: String s1="Good"; creates a String object "Good" in constant pool.

s1=s1+"morning"; creates another String object "morning" in constant pool but this time actually JVM do: s1=new StringBuffer().append(s1).append("morning").toString();.

Now as the new operator creates an object in heap therefore the reference in s1 is of heap not constant pool and the String s2="Goodmorning"; creates a String object "Goodmorning" in constant pool whose reference is stored in s2.

Therefore, if(s1==s2) condition is false.

But what happens in jdk7?


FIRST CASE:

In the first code snipped you are actually adding three Strings in the Pool of Strings. 1. s1 = "Good"
2. s1 = "Goodmorning" (after concatenating) 3. s2 = "Goodmorining"

While doing if(s1==s2), the objects are same but reference as different hence it is false.

SECOND CASE:

In this case you are using s1.intern(), which implies that if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.

  1. s1 = "Good"
  2. s1 = "Goodmorning" (after concatenating)
  3. For String s2="Goodmorning", new String is not added to the pool and you get reference of existing one for s2. Hence if(s1==s2) returns true.

You need to use s1.equals(s2). Using == with String objects compares the object references themselves.

Edit: When I run your second code snippet, I do not get "both are equal" printed out.

Edit2: Clarified that references are compared when you use '=='.


there are mainly 4 ways to compare string:

  1. "== operator": it just compares the reference variable of the string object. So it might give you unexpected results depending upon how you have created the string i.e. using String class's constructor or simply by using double quote as both get memory differently(in heap and pool respectively).
  2. "equals(Object) method": this is method of object class and is OVERLOADED by string class. It compares whole string and IS CASE SENSITIVE.
  3. "equalsIgnoreCase(String) method": this is method of string class and compares whole string and IS NOT CASE SENSITIVE.
  4. "compares(String) method": compare both strings character by character and return their difference if the returned value is 0, this means strings are equal.

Whenever you are comparing between two String, don't use == and use eqauls() becaue you are comparing objects not references:

string1.equals(string2);

The result code dependents runtime:

class Test {
     public static void main(String... args) {
        String s1 = "Good";
        s1 = s1 + "morning";
        System.out.println(s1 == s1.intern()); // Prints true for jdk7, false - for jdk6.
    }
}

If you write like this:

class Test {
     public static void main(String... args) {
        String s = "GoodMorning";
        String s1 = "Good";
        s1 = s1 + "morning";
        System.out.println(s1 == s1.intern()); // Prints false for both jdk7 and jdk6.
    }
}

the reason is ' ldc #N ' (Load string from constant pool) and String.intern() both will use StringTable in hotspot JVM. For detail I wrote a pool english article: http://aprilsoft.cn/blog/post/307.html

ReferenceURL : https://stackoverflow.com/questions/7065337/intern-behaving-differently-in-java-6-and-java-7

반응형