Heim  >  Artikel  >  Java  >  So verwenden Sie die interne Methode in Java String

So verwenden Sie die interne Methode in Java String

WBOY
WBOYnach vorne
2023-05-05 17:28:131203Durchsuche

    Einführung in den Konstantenpool

    Es gibt 8 Grundtypen und einen speziellen Typ String in der JAVA-Sprache. Um sie schneller zu machen und im Betrieb Speicher zu sparen, bieten diese Typen ein Konzept eines konstanten Pools (im Methodenbereich). Der Konstantenpool ähnelt einem Cache, der auf der JAVA-Systemebene bereitgestellt wird. Die acht Grundtypen von Konstantenpools werden alle vom System koordiniert, und der Konstantenpool vom Typ String ist etwas Besonderes.

    Es gibt zwei Hauptmöglichkeiten, den String-Konstantenpool zu verwenden:

    String-Objekte, die direkt mit doppelten Anführungszeichen deklariert werden, werden direkt im Konstantenpool gespeichert.

    Wenn das String-Objekt nicht in doppelten Anführungszeichen deklariert ist, können Sie es mit der von String bereitgestellten internen Methode in den Konstantenpool einfügen.

    Einführung in die interne Methode (JDK7)

    Prototyp: öffentlicher nativer String intern();

    Beschreibung:

    Von Fragen Sie ab, ob die aktuelle Zeichenfolge im Zeichenfolgenkonstantenpool vorhanden ist (beurteilt nach Gleichheit).

    • Wenn vorhanden, wird die Zeichenfolgenreferenz im Konstantenpool zurückgegeben.

    • Wenn es nicht vorhanden ist, speichern Sie die String-Objektreferenz im Konstantenpool und geben Sie dann die String-Objektreferenz zurück.

    Rückgabewert: Alle geben Verweise auf den String-Konstantenpool zurück, der der String-Variablen entspricht.

    Beispiel

    package com.example;
     
    public class Demo {
        public static void main(String argv[]) {
            String s = "test";
            System.out.println(s == s.intern());
        }
    }

    JDK6 und davor: Ausgabe falsch

    JDK7 und danach: Ausgabe wahr

    Prinzip (JDK6 und JDK7)

    Der Ursprung der Zeichenfolge im Konstantenpool

    JDK6 und frühere Aufrufe String.intern()

    #🎜 🎜 #
    • Wenn einer im Konstantenpool vorhanden ist, geben Sie einen Verweis auf die Zeichenfolge im Konstantenpool zurück.

    • Wenn keiner vorhanden ist Kopieren Sie es im Konstantenpool. Eine Kopie des Objekts wird im Konstantenpool abgelegt (permanente Generierung). Der Rückgabewert ist ein Verweis auf die entsprechende Zeichenfolgeninstanz im Konstantenpool (permanente Generierung).

    JDK7 und späterer Aufruf von String.intern()

    • Wenn es einen im Konstantenpool gibt, Die Konstante wird zurückgegeben. Die Referenz dieser Zeichenfolge im Pool

    • Wenn es keine Referenz im Konstantenpool gibt, kopieren Sie eine Referenz und fügen Sie sie in den Konstantenpool ein ( Heap); (JDK1.7 wird String Der Konstantenpool wurde aus dem Perm-Bereich in den Java Heap-Bereich verschoben)

    Routinetest

    #🎜🎜 #Routine 1:

    package org.example.a;
     
    public class Demo {
        public static void main(String argv[]) {
            String s1 = new String("1");
            s1.intern();
            String s2 = "1";
            System.out.println(s1 == s2);
     
            String s3 = new String("1") + new String("1");
            s3.intern();
            String s4 = "11";
            System.out.println(s3 == s4);
        }
    }
    #🎜🎜 #RESULT

    jdk6:false false

    jdk7:false true
    jdk8:false true



    Routine 2:

    package org.example.a;
     
    public class Demo {
        public static void main(String argv[]) {
            String s1 = new String("1");
            s1.intern();
            String s2 = "1";
            System.out.println(s1 == s2);
     
            String s3 = new String("1") + new String("1");
            String s4 = "11";
            s3.intern();
            System.out.println(s3 == s4);
        }
    }
    Im zweiten Teil des obigen Codes gibt es einen Austausch.

    Ergebnis

    jdk6:falsch falsch

    jdk7:falsch falsch
    jdk8:falsch falsch

    # 🎜🎜#Routineanalyse

    Im Bild unten: Die grüne Linie stellt die Inhaltsverweisung des String-Objekts dar. Die rote Linie stellt die Adressrichtung dar.

    jdk1.6

    Analyse von Routine 1 und Routine 2

    Wie oben gezeigt . Lassen Sie uns zunächst über die Situation in jdk6 sprechen. In jdk6 sind alle oben genannten Ausdrucke falsch, da der konstante Pool in jdk6 im Perm-Bereich platziert ist und der Perm-Bereich vollständig vom normalen JAVA-Heap-Bereich getrennt ist. Wie oben erwähnt, wird die Zeichenfolge, wenn sie mit Anführungszeichen deklariert wird, direkt im String-Konstantenpool generiert und das neue String-Objekt im JAVA-Heap-Bereich platziert. Daher ist der Vergleich der Objektadresse eines JAVA-Heap-Bereichs mit der Objektadresse des String-Konstantenpools definitiv unterschiedlich, selbst wenn die Methode String.intern aufgerufen wird, besteht keine Beziehung.

    jdk1.7So verwenden Sie die interne Methode in Java String

    In Jdk6 und früheren Versionen wird der String-Konstantenpool im Perm-Bereich des Heaps platziert . Es speichert hauptsächlich Informationen zu geladenen Klassen, Konstantenpools, Methodenfragmenten usw. Sobald intern im Konstantenpool ausgiebig verwendet wird, tritt ein Speicherfehler von java.lang.OutOfMemoryError:PermGen auf. In der jdk7-Version wurde der String-Konstantenpool aus dem Perm-Bereich in den normalen Java-Heap-Bereich verschoben. Der Hauptgrund, warum es verschoben werden muss, ist, dass der Perm-Bereich zu klein ist. Den Nachrichten zufolge hat jdk8 den Perm-Bereich natürlich direkt aufgehoben und einen neuen Metabereich eingerichtet. Es sollte sein, dass JDK-Entwickler der Meinung sind, dass der Perm-Bereich nicht mehr für die aktuelle Entwicklung von JAVA geeignet ist. Der String-Konstantenpool wurde in den JAVA-Heap-Bereich verschoben. Erklären Sie nun, warum es die oben gedruckten Ergebnisse gibt.

    Analyse von Routine 1

    1.String s1 = neu String("1");

    So verwenden Sie die interne Methode in Java String

    Analyse: Diese Codezeile generiert 2 Objekte ("1" im Konstantenpool und das String-Objekt in JavaHeap). s.intern(); Dieser Satz bedeutet, dass das s1-Objekt zur Suche in den Konstantenpool gegangen ist und festgestellt hat, dass sich „1“ bereits im Konstantenpool befindet.

    Zu diesem Zeitpunkt zeigt s1 auf das String-Objekt im Java-Heap.

    2.String s2 = „1“;

    Analyse: Diese Codezeile generiert einen Verweis auf s2, der auf „1“ zeigt der konstante Pool „Object“. Das Ergebnis ist, dass die Referenzadressen von s1 und s2 unterschiedlich sind.

    3.String s3 = neuer String("1") + neuer String("1");

    分析:这行代码生成了2个对象(字符串常量池中的“1” 和 Java Heap中的 s3 引用指向的对象“11”(中间还有2个匿名的new String("1")我们不讨论它)。
    此时s3 是Java Heap中的字符串对象的引用,对象内容是”11″,此时常量池中是没有 “11”对象的。

    4.s3.intern();

    分析:这行代码将 s3中的"11"字符串放入String 常量池中,因为此时常量池中不存在"11"字符串,因此常规做法是跟 jdk6 图中表示的那样,在常量池中生成一个"11"的对象,关键点是 jdk7 中常量池不在Perm区域,而是在堆中了。常量池中不需再存储一份对象了,可以直接存储堆中的引用。这份引用指向s3引用的对象。 也就是说引用地址是相同的。

    此时,s3是Java Heap中的字符串对象的引用,对象内容是”11″,此时常量池中是有 “11”对象,它保存的就是s3引用地址。

    5.String s4 = "11"; 

    这行代码”11″是显式声明的,因此会直接去常量池中创建,创建时发现已经有这个对象了。

    此时:s4 == 常量池的“11”对象引用 == s3引用对象的引用

    例程2的分析

    So verwenden Sie die interne Methode in Java String

    String s1 = new String("1");

    s1.intern();

    String s2 = "1";

    分析:s1.intern();,这一句往后放也不会有什么影响了,因为对象池中在执行第一句代码String s = new String("1");的时候已经生成“1”对象了。下边的s2声明都是直接从常量池中取地址引用的。 s1 和 s2 的引用地址是不会相等的。

    String s3 = new String("1") + new String("1");

    分析:这行代码生成了2个对象(字符串常量池中的“1” 和 Java Heap中的 s3 引用指向的对象“11”(中间还有2个匿名的new String("1")我们不讨论它)。

    此时s3 是Java Heap中的字符串对象的引用,对象内容是”11″,此时常量池中是没有 “11”对象的。

    String s4 = "11";

    分析:声明 s4 的时候常量池中是不存在“11”对象的,执行完后,s4是常量池里“11“对象的引用。

    s3.intern(); 

    分析:此时常量池中“11”对象已经存在了,不会有任何操作,s3仍然是堆中String对象的引用。因此 s3 != s4

    应用实例

    package org.example.a;
     
    import java.util.Random;
     
    public class Demo {
        static final  int MAX = 1000 * 10000;
        static final String[] arr = new String[MAX];
        public static void main(String argv[]) {
            Integer[] DB_DATA = new Integer[10];
            Random random = new Random(10 * 10000);
            for(int i = 0; i < DB_DATA.length; i++){
                DB_DATA[i] = random.nextInt();
            }
     
            long t = System.currentTimeMillis();
            for(int i = 0; i < MAX; i++){
                //arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length]));
                arr[i] = new String(String.valueOf(DB_DATA[i % DB_DATA.length])).intern();
            }
     
            System.out.println((System.currentTimeMillis() -t) + "ms");
            System.gc();
        }
    }

    上述代码是一个演示代码,其中有两条语句不一样,一条是使用 intern,一条是未使用 intern。

    运行的参数是:-Xmx2g -Xms2g -Xmn1500M

    不用intern

    2160ms

    So verwenden Sie die interne Methode in Java String

    使用intern

    826ms

    So verwenden Sie die interne Methode in Java String

    通过上述结果,我们发现不使用 intern 的代码生成了1000w 个字符串,占用了大约640m 空间。 使用了 intern 的代码生成了1345个字符串,占用总空间 133k 左右。其实通过观察程序中只是用到了10个字符串,所以准确计算后应该是正好相差100w 倍。虽然例子有些极端,但确实能准确反应出 intern 使用后产生的巨大空间节省。

    细心的同学会发现使用了 intern 方法后时间上有了一些增长。这是因为程序中每次都是用了 new String 后, 然后又进行 intern 操作的耗时时间,这一点如果在内存空间充足的情况下确实是无法避免的,但我们平时使用时,内存空间肯定不是无限大的,不使用 intern占用空间导致 jvm 垃圾回收的时间是要远远大于这点时间的。 毕竟这里使用了1000w次intern 才多出来1秒钟多的时间。

    Das obige ist der detaillierte Inhalt vonSo verwenden Sie die interne Methode in Java String. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

    Stellungnahme:
    Dieser Artikel ist reproduziert unter:yisu.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen