Maison  >  Article  >  Java  >  Comment utiliser la méthode interne dans Java String

Comment utiliser la méthode interne dans Java String

WBOY
WBOYavant
2023-05-05 17:28:131146parcourir

    Introduction au pool de constantes

    Il existe 8 types de base et un type spécial String dans le langage JAVA. Afin de les rendre plus rapides et d'économiser de la mémoire pendant le fonctionnement, ces types proposent un concept de pool constant (dans la zone méthode). Le pool constant est similaire à un cache fourni par le niveau système JAVA. Les huit types de base de pools constants sont tous coordonnés par le système, et le pool constant de type String est spécial.

    Il existe deux manières principales d'utiliser le pool de constantes String :

    Les objets String déclarés directement à l'aide de guillemets doubles seront directement stockés dans le pool de constantes.

    Si l'objet String n'est pas déclaré avec des guillemets doubles, vous pouvez utiliser la méthode interne fournie par String pour le mettre dans le pool de constantes.

    Introduction à la méthode Intern (JDK7)

    Prototype : public native String intern();

    Description :

    Requête si la chaîne actuelle existe à partir du pool de constantes de chaîne (jugée par égaux).

    • Si présent, renvoie la référence de chaîne dans le pool constant.

    • S'il n'existe pas, stockez la référence de l'objet String dans le pool de constantes, puis renvoyez la référence de l'objet String.

    Valeur de retour : toutes renvoient des références au pool de constantes de chaîne correspondant à la variable String.

    Exemple

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

    JDK6 et avant : sortie false

    JDK7 et après : sortie vrai

    Principe (JDK6 et JDK7)

    L'origine de la chaîne dans le pool constant

    JDK6 et avant appelle String.intern ( )

    • S'il y en a un dans le pool constant, renvoie la référence à la chaîne dans le pool constant

    • S'il n'y en a pas dans le pool constant, copiez une copie de l'objet et placez-la dans le pool constant (génération permanente) ; la valeur de retour est Une référence à l'instance de chaîne correspondante dans le pool constant (génération permanente).

    JDK7 et versions ultérieures appellent String.intern()

    • S'il y en a une dans le pool constant, renvoie la référence de la chaîne dans le pool constant

    • S'il n'y en a pas dans le pool constant, copiez une référence et placez-la dans le pool de constantes (tas) ; (JDK1.7 a déplacé le pool de constantes String de la zone Perm vers la zone Java Heap)

      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");
            s3.intern();
            String s4 = "11";
            System.out.println(s3 == s4);
        }
    }

    Il y a un échange dans la deuxième partie du code ci-dessus.

    Résultat



    jdk6 : false false

    jdk7 : false false
    jdk8 : false false

    Analyse de routine

    Dans l'image ci-dessous : la ligne verte représente le pointeur de contenu de l'objet String. La ligne rouge représente le pointage d'adresse.

    jdk1.6


    Analyse de la routine 1 et de la routine 2

    comme indiqué sur l'image ci-dessus. Parlons d'abord de la situation dans jdk6. Dans jdk6, toutes les impressions ci-dessus sont fausses, car le pool constant dans jdk6 est placé dans la zone Perm et la zone Perm est complètement séparée de la zone JAVA Heap normale. Comme mentionné ci-dessus, si la chaîne est déclarée à l'aide de guillemets, elle sera générée directement dans le pool de constantes de chaîne et le nouvel objet String est placé dans la zone JAVA Heap. Par conséquent, comparer l'adresse d'objet d'une zone de tas JAVA avec l'adresse d'objet du pool de constantes de chaîne est définitivement différente, même si la méthode String.intern est appelée, elle n'a aucune relation.

    jdk1.7

    Dans Jdk6 et les versions précédentes, le pool constant de chaînes est placé dans la zone Perm du tas. La zone Perm est une zone de type statique qui stocke principalement des informations sur les classes chargées, les pools constants, et méthodes. La taille par défaut des fragments et autres contenus est de seulement 4 min. Une fois que intern est largement utilisé dans le pool de constantes, une erreur d'espace java.lang.OutOfMemoryError:PermGen se produit. Dans la version jdk7, le pool de constantes de chaîne a été déplacé de la zone Perm vers la zone Java Heap normale. La principale raison pour laquelle il doit être déplacé est que la zone de Perm est trop petite. Bien sûr, selon les informations, jdk8 a directement annulé la zone de Perm et a créé une nouvelle méta-zone. Il faudrait que les développeurs de jdk pensent que la région de Perm n'est plus adaptée au développement actuel de JAVA. Le pool de constantes de chaîne a été déplacé vers la zone JAVA Heap. Expliquez maintenant pourquoi les résultats sont imprimés ci-dessus.

    Analyse de la routine 1Comment utiliser la méthode interne dans Java String

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

    Analyse : Cette ligne de code génère 2 objets ("1" et "1" dans la constante pool String dans JavaHeap). s.intern(); Cette phrase signifie que l'objet s1 est allé dans le pool constant pour rechercher et a constaté que "1" était déjà dans le pool constant.

    À ce stade, s1 pointe vers l'objet chaîne dans Java Heap.

    Comment utiliser la méthode interne dans Java String

    2.String s2 = "1";

    Analyse : Cette ligne de code génère une référence à s2 pointant vers l'objet "1" dans le pool constant. Le résultat est que les adresses de référence de s1 et s2 sont différentes.

    3.String s3 = new String("1") + new 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的分析

    Comment utiliser la méthode interne dans 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

    Comment utiliser la méthode interne dans Java String

    使用intern

    826ms

    Comment utiliser la méthode interne dans Java String

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

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

    Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

    Déclaration:
    Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer