programing

java.lang을 사용하는 것이 좋은 방법입니까?String.intern()?

minecode 2022. 8. 11. 21:41
반응형

java.lang을 사용하는 것이 좋은 방법입니까?String.intern()?

자바독에 대한 자세한 내용은 없습니다.(간단히 말하면:문자열의 표준 표현을 반환하여 내부 문자열을 비교합니다.==)

  • 언제 이 기능을 사용하면 편리합니까?String.equals()?
  • Javadoc에서 언급되지 않은 부작용, 즉 JIT 컴파일러에 의한 최적화 등이 있습니까?
  • 의 추가 용도가 있습니까?String.intern()?

이것은 문자열 비교와는 거의 관계가 없습니다.문자열 인터닝은 응용 프로그램에 동일한 내용을 가진 문자열이 여러 개 있는 경우 메모리를 절약하기 위한 입니다.사용방법String.intern()어플리케이션에는 장기적으로 인스턴스가1개밖에 없습니다.단, 일반적인 문자열 비교가 아닌 고속 참조 등가 비교를 실행할 수 있다는 단점이 있습니다(단, 이것은 1개의 인스턴스만 삽입하는 것을 잊어버리는 것으로 끊기 쉽기 때문에 일반적으로 권장되지 않습니다).

String.equals()를 위해 이 함수를 언제 사용할 수 있습니까?

참조로 문자열을 비교할 수 있으므로 속도가 필요할 때(==가 동등보다 빠름)

자바독에 언급되지 않은 부작용이 있나요?

주요 단점은 비교하는 모든 문자열을 실제로 intern()해야 한다는 것입니다.모든 문자열을 intern()하는 것을 잊어버리기 쉬우며, 그러면 혼동스러울 정도로 잘못된 결과를 얻을 수 있습니다.또, 모두를 위해서, 현의 내부화에 의존하고 있는 것을 명확하게 기록해 주세요.

문자열을 내부화하는 경우의 두 번째 단점은 intern() 메서드가 상대적으로 비싸다는 것입니다.(스트링이 이미 내부화되어 있는 경우에도) 상당한 양의 작업을 수행하기 위해 고유한 문자열 풀을 관리해야 합니다.따라서 코드 설계에 주의하여 예를 들어 intern()과 같이 입력 시 모든 적절한 문자열에 대해 걱정할 필요가 없도록 하십시오.

(JGuru에서)

세 번째 단점(Java 7 이하만): 내장된 문자열은 보통 매우 작은 PermGen 공간에 존재합니다.사용 가능한 힙 영역이 많은 Out Of Memory Error가 발생할 수 있습니다.

(Michael Borgwardt에서)

String.intern()확실히 최신 JVM에서 수집된 가비지입니다.
GC 액티비티에 의해 다음 NEVER 메모리 부족은 발생하지 않습니다.

// java -cp . -Xmx128m UserOfIntern

public class UserOfIntern {
    public static void main(String[] args) {
        Random random = new Random();
        System.out.println(random.nextLong());
        while (true) {
            String s = String.valueOf(random.nextLong());
            s = s.intern();
        }
    }
}

GCed 이외의 String.intern()에 대한 자세한 내용은 이쪽을 참조하십시오.

최근 Java 6, 7, 8에서의 String.intern() 구현에 관한 기사를 작성했습니다.Java 6, 7, 8 - 문자열 풀링에서의 String.intern.

자바에서의 문자열 풀링에 대한 현재 상황에 대한 충분한 정보를 담고 있기를 바랍니다.

한마디로:

  • 피하다String.intern()Java 6에서는 PermGen에 들어가 있기 때문에
  • 선호하다String.intern()Java 7 및 Java 8의 경우: 자체 객체 풀을 롤하는 것보다 메모리 사용량이 4~5배 적습니다.
  • 꼭 튜닝해 주세요-XX:StringTableSize(기본값이 너무 작을 수 있습니다.소수를 설정합니다.)

문자열을 ==와 비교하는 것이 equals()보다 훨씬 빠릅니다.

5 속도는 빨라지지만 String 비교는 일반적으로 애플리케이션 총 실행 시간의 극히 일부에 불과하기 때문에 전체적인 이득은 훨씬 적고 최종 이득은 몇 %로 희박해집니다.

String.intern()은 힙에서 문자열을 꺼내 PermGen에 넣습니다.

내부화된 문자열은 다른 스토리지 영역인 Permanent Generation(영구 생성)에 배치됩니다.영구 생성은 클래스, 메서드 및 기타 내부 JVM 개체와 같은 비사용자 개체용으로 예약된 JVM 영역입니다.이 영역의 크기는 한정되어 있으며,는 힙보다 훨씬 소중합니다.이 영역이 힙보다 작기 때문에 모든 공간을 사용하여 Out Of Memory를 얻을 가능성이 높아집니다.예외.

String.intern() 문자열이 가비지 수집됨

새 버전의 JVM에서는 내부화된 문자열도 개체에서 참조하지 않을 때 가비지 수집됩니다.

위의 3가지 점에 유의하면 String intern()이 도움이 되는 것은 문자열 비교가 많은 경우뿐이지만, 정확히 무엇을 하고 있는지 모르는 경우에는 내부 문자열을 사용하지 않는 것이 좋습니다.

String.equals()를 위해 이 함수를 언제 사용할 수 있습니까?

서로 다른 일을 한다는 걸 고려하면, 아마 절대 아닐 거예요.

퍼포먼스상의 이유로 스트링을 인터닝 하는 것은, 레퍼런스 균등화를 위해서 비교할 수 있도록 하기 위해서, 스트링에 대한 참조를 잠시 보류하고 있는 경우에만 유효합니다.사용자 입력 또는 IO에서 나오는 스트링은 인터닝 되지 않습니다.

즉, 어플리케이션에서 외부 소스로부터 입력을 받아 의미적 값(식별자)을 가진 오브젝트로 처리하지만 그 오브젝트는 원시 데이터와 구별할 수 없는 타입을 가지고 있으며 프로그래머가 그것을 어떻게 사용해야 하는지에 대한 다른 규칙을 가지고 있습니다.

대부분의 경우, 이 기능을 사용하여UserId(스레드 세이프 범용 인터닝 메커니즘을 작성하는 것은 용이하며) 타입은 오버로드가 아닌 오픈 열거형으로 동작합니다.java.lang.String사용자 ID일 경우 참조 시멘틱스와 함께 입력합니다.

이렇게 하면 특정 문자열이 삽입되었는지 여부를 혼동하지 않고 필요한 추가 동작을 open enum에 캡슐화할 수 있습니다.

어떤 장점도 모르고, 만약 어떤 장점이 있다면, equals() 자체가 내부적으로 (하지만) 사용할 것이라고 생각할 것입니다.

인턴() 신화를 깨다

Daniel Brückner가 전적으로 옳다.문자열 인터닝은 메모리(히프)를 절약하기 위한 것입니다.현재 당사의 시스템에는 특정 데이터를 저장하기 위한 거대한 해시맵이 있습니다.시스템이 확장됨에 따라 해시맵은 (테스트한 바와 같이) 메모리 부족에 대응할 수 있을 정도로 커집니다.해시맵 내의 모든 오브젝트를 중복된 문자열에 삽입함으로써 힙 공간을 대폭 절약할 수 있습니다.

또한 Java 7에서는 내장된 문자열이 PermGen에 존재하지 않고 힙에 존재합니다.따라서 크기에 대해 걱정할 필요가 없으며 쓰레기가 수집됩니다.

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

Javadoc에서 언급되지 않은 부작용, 즉 JIT 컴파일러에 의한 최적화 등이 있습니까?

JIT 레벨은 모르지만 스트링 풀에 대한 직접적인 바이트 코드 지원이 있습니다.이것은 전용 바이트 코드를 사용하여 마법처럼 효율적으로 구현됩니다.CONSTANT_String_info구조(일반적인 표현을 가진 대부분의 다른 개체와 달리).

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 구조를 가리킵니다.이것은 문자열을 나타내는 데 필요한 유일한 데이터입니다.

위의 JVMS 인용문에는 Utf8이 동일한 것을 가리킬 때마다 동일한 인스턴스가 로딩됩니다.ldc.

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

  • static final String s = "abc"상수속성을 통해 상수 테이블을 가리킵니다.
  • 비최종 필드에는 해당 속성이 없지만 다음과 같이 초기화할 수 있습니다.ldc

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

문자열의 다중 비교에서 병목 현상이 발생하는 경우에만 동등하지 않고 intern과 ==-비교를 검토하겠습니다.intern()은 무료가 아니기 때문에 이것은 적은 수의 비교에 도움이 될 가능성이 거의 없습니다.적극적으로 문자열을 삽입하면 intern()에 대한 호출이 점점 느려지고 있음을 알 수 있습니다.

메모리 리크의 일종은, 다음의 사용으로 발생할 수 있습니다.subString()소스 문자열에 비해 결과가 작고 개체의 수명이 긴 경우.

통상적인 해결책은new String( s.subString(...))그러나 잠재력/잠재력 결과를 저장하는 클래스가 있는 경우subString(...)발신자를 제어할 수 없기 때문에, 그 유저의 데이터를 보존하는 것을 고려할 수 있습니다.intern()String 인수 중 하나를 지정합니다.이로 인해 잠재적인 큰 버퍼가 해방됩니다.

문자열 인터닝은 다음과 같은 경우에 유용합니다.equals()메서드가 자주 호출되는 이유는equals()method는 메서드의 시작 부분에서 객체가 동일한지 여부를 빠르게 확인합니다.

if (this == anObject) {
    return true;
}

이 문제는 보통 에서 검색 시 발생합니다.Collection단, 다른 코드도 문자열 등호도 체크할 수 있습니다.

인터닝에는 비용이 듭니다만, 몇개의 코드의 마이크로벤치마크를 실행해 본 결과, 인터닝 프로세스가 실행 시간을 10배로 증가시켰습니다.

일반적으로 코드 내의 문자열이 자동으로 삽입되므로 코드 외부에 저장되어 있는 키를 읽을 때 인터닝을 수행하는 것이 가장 좋습니다.이는 보통 최초 사용자의 패널티를 방지하기 위해 어플리케이션 초기화 단계에서 발생합니다.

키 검색에 사용할 수 있는 사용자 입력을 처리하는 것도 가능합니다.이는 보통 요청 프로세서에서 발생하므로 삽입된 문자열은 전달되어야 합니다.

그것 말고는, 일반적으로는 아무런 이득도 주지 않기 때문에, 나머지 코드에 인터닝을 하는 것은 큰 의미가 없습니다.

유지 보수에 애쓸 가치가 없는 것에 투표하겠습니다.

코드가 아닌 이상 대부분의 경우 서브스트링으로 많은 작업을 수행할 필요가 없으며 성능도 향상되지 않습니다.이 경우 String 클래스는 원래 문자열과 오프셋을 사용하여 메모리를 절약합니다.코드가 기판을 많이 사용한다면 메모리 요구량이 폭발적으로 증가할 것입니다.

http://kohlerm.blogspot.co.uk/2009/01/is-javalangstringintern-really-evil.html

라고 단언하다String.equals()사용하다"=="비교하다String이전의 오브젝트

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

문자열의 길이를 비교한 다음 내용을 비교합니다.

(그런데 판매 카탈로그의 제품 코드 문자열은 모두 같은 길이로 되어 있습니다.BIC0417은 자전거 타는 사람의 안전모, TIG0003은 살아있는 성인 수컷 호랑이입니다.이것들 중 하나를 주문하려면 아마도 모든 종류의 라이선스가 필요할 것입니다.그리고 안전모도 동시에 주문하는 것이 좋을지도 모릅니다.)

즉, String을 자신의 String으로 대체함으로써 이점을 얻을 수 있을 것 같습니다.intern()"=="를 사용하지 않고도 안전성과 가독성 및 표준 컴플라이언스를 얻을 수 있습니다.equals()당신의 프로그래밍에서.그리고 제가 말하고자 하는 것의 대부분은 그것이 사실인지에 달려있습니다.

하지만 그렇다String.equals()다른 오브젝트가 아닌 String을 전달했는지 테스트합니다."=="말할 자격은 없지만, 그럴 자격은 없을 것 같아요.equals()연산은 String to String이 되기 때문에 테스트는 거의 항상 통과됩니다.실제로, 내부에서는 "=="에 우선순위를 부여합니다.String.equals()는 String을 같은 실제 객체와 자주 비교한다는 확신을 의미합니다.

다음 행이 "거짓"의 결과로 나타난다는 사실에 아무도 놀라지 않기를 바랍니다.

    Integer i = 1;
    System.out.println("1".equals(i));

But if you change i to i.toString() in the second line, of course it's true.

Venues where you might hope for a benefit from interning include Set and Map, obviously. I hope that interned strings have their hashcodes cached... I think that would be a requirement. And I hope I haven't just given away an idea that could earn me a million dollars. :-)

As for memory, it's also obvious that that is an important limit if your volume of Strings is large, or if you want the memory used by your program code to be very small. If your volume of -distinct- Strings is very large, then it may be time to consider using dedicated database program code to manage them, and a separate database server. Likewise, if you can improve a small program (that needs to run in 10000 instances simultaneously) by having it not store its Strings itself at all.

It feels wasteful to create a new String and then discard it straight away for its intern() substitute, but there isn't a clear alternative, except for keeping the duplicate String. So really the execution cost is of searching for your string in the intern pool and then allowing the garbage collector to dispose of the original. And if it's a string literal then it comes intern-ed already anyway.

I am wondering whether intern() can be abused by malicious program code to detect whether some String and their object references already exist in the intern() pool, and therefore exist elsewhere in the Java session, when that shouldn't be known. But that would only be possible when the program code is already being used in a trusting way, I guess. Still, it is something to consider about the third-party libraries that you include in your program to store and remember your ATM PIN numbers!

The real reason to use intern is not the above. You get to use it after you get out-of-memory error. Lots of the string in a typical program are String.substring() of other big string [think of taking out a user-name from a 100K xml file. The java implementation is that , the substring holds a reference to the original string and the start+end in that huge string. (The thought behind it is a reuse of the same big string)

After 1000 big files , from which you only save 1000 short names , you will keep in memory the whole 1000 files! Solution: in this scenario just use smallsubstring.intern()

I am using intern to save memory, I hold a large amount of String data in memory and moving to use intern() saved a massive amount of memory. Unfortunately although it use alot less memory the memory it does use is stored in PermGen memory not Heap and it is difficult to explain to customers how to increase the allocation of this type of memory.

So is there an alternative to intern() for reducing memory consumption, (the == versus equals performance benefits is not a aissue for me)

Let's face it: the main use-case scenario is when you read a stream of data (either through an input stream, or from a JDBC ResultSet) and there is a myriad of little Strings that are repeated all throughout.

Here is a little trick that gives you some control over what kind of mechanism you'd like to use to internalize Strings and other immutables, and an example implementation:

/**
 * Extends the notion of String.intern() to different mechanisms and
 * different types. For example, an implementation can use an
 * LRUCache<T,?>, or a WeakHashMap.
 */
public interface Internalizer<T> {
    public T get(T obj);
}
public static class LRUInternalizer<T> implements Internalizer<T> {
    private final LRUCache<T, T> cache;
    public LRUInternalizer(int size) {
        cache = new LRUCache<T, T>(size) {
            private static final long serialVersionUID = 1L;
            @Override
            protected T retrieve(T key) {
                return key;
            }
        };
    }
    @Override
    public T get(T obj) {
        return cache.get(obj);
    }
}
public class PermGenInternalizer implements Internalizer<String> {
    @Override
    public String get(String obj) {
        return obj.intern();
    }
}

I use that often when I read fields from streams or from ResultSets. Note: LRUCache is a simple cache based on LinkedHashMap<K,V>. It automatically calls the user-supplied retrieve() method for all cache misses.

The way to use this is to create one LRUInternalizer before your read (or reads), use it to internalize Strings and other small immutable objects, then free it. For example:

Internalizer<String> internalizer = new LRUInternalizer(2048);
// ... get some object "input" that stream fields
for (String s : input.nextField()) {
    s = internalizer.get(s);
    // store s...
}

I am using it in order to cache the contents of approximately 36000 codes which link to associated names. I intern the strings in the cache because many of the codes point to the same string.

By interning the strings in my cache, I am ensuring that codes that point to the same string actually point to the same memory, thereby saving me RAM space.

If the interned strings were actually garbage collected, it would not work for me at all. This would basically negate the purpose of interning. Mine won't be garbage collected because I am holding a reference to each and every string in the cache.

The cost of interning a string is much more than the time saved in a single stringA.equals(B) comparison. Only use it (for performance reasons) when you are repeatedly using the same unchanged string variables. For example if you regularly iterate over a stable list of strings to update some maps keyed on the same string field you can get a nice saving.

I would suggest using string interning to tweak performance when you are optimising specific parts of your code.

Also remember that String are immutable and don't make the silly mistake of

String a = SOME_RANDOM_VALUE
a.intern()

remember to do

String a = SOME_RANDOM_VALUE.intern()

If you are looking for an unlimited replacement for String.intern, also garbage collected, the following is working well for me.

private static WeakHashMap<String, WeakReference<String>> internStrings = new WeakHashMap<>();
public static String internalize(String k) {
    synchronized (internStrings) {
        WeakReference<String> weakReference = internStrings.get(k);
        String v = weakReference != null ? weakReference.get() : null;
        if (v == null) {
            v = k;
            internStrings.put(v, new WeakReference<String>(v));
        }
        return v;
    }
}

Of course, if you can roughly estimate how many different strings there will be, then simply use String.intern() with -XX:StringTableSize=highEnoughValue.

ReferenceURL : https://stackoverflow.com/questions/1091045/is-it-good-practice-to-use-java-lang-string-intern

반응형