Rumah  >  Artikel  >  Java  >  Cara menggunakan kaedah pelatih dalam Java String

Cara menggunakan kaedah pelatih dalam Java String

WBOY
WBOYke hadapan
2023-05-05 17:28:131205semak imbas

    Pengenalan kepada kolam berterusan

    Terdapat 8 jenis asas dan String jenis khas dalam bahasa JAVA. Untuk menjadikannya lebih pantas dan menjimatkan memori semasa operasi, jenis ini menyediakan konsep kumpulan malar (dalam kawasan kaedah). Kolam malar adalah serupa dengan cache yang disediakan oleh tahap sistem JAVA. Lapan jenis asas kolam pemalar semuanya diselaraskan oleh sistem, dan kolam pemalar jenis String adalah istimewa.

    Terdapat dua cara utama untuk menggunakan kolam pemalar String:

    Objek rentetan yang diisytiharkan terus menggunakan petikan berganda akan disimpan terus dalam kolam pemalar.

    Jika objek String tidak diisytiharkan dengan petikan berganda, anda boleh menggunakan kaedah pelatih yang disediakan oleh String untuk memasukkannya ke dalam kumpulan tetap.

    Pengenalan kaedah Intern (JDK7)

    Prototaip: public native String intern();

    Penerangan:

    Soal watak semasa daripada kolam pemalar rentetan Sama ada rentetan itu wujud (dinilai oleh sama).

    • Jika ada, mengembalikan rujukan rentetan dalam kolam malar.

    • Jika ia tidak wujud, simpan rujukan objek String dalam kolam malar, dan kemudian kembalikan rujukan objek String.

    Nilai pulangan: Semua rujukan pulangan kepada kumpulan pemalar rentetan sepadan dengan pembolehubah Rentetan.

    Contoh

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

    JDK6 dan sebelum: output palsu

    JDK7 dan selepas: output benar

    Prinsip (JDK6 dan JDK7)

    Asal usul rentetan dalam kolam malar

    JDK6 dan sebelum dipanggil String.intern()

    • Jika ada satu dalam kolam malar , pemalar dikembalikan Rujukan rentetan ini dalam kolam

    • Jika tiada rujukan dalam kolam malar, salin salinan objek dan letakkan dalam kolam malar (kekal penjanaan); nilai pulangan ialah kumpulan malar (generasi kekal) Rujukan kepada contoh rentetan yang sepadan dalam .

    JDK7 dan kemudian panggil String.intern()

    • Jika ada satu dalam kolam malar, kembalikan rujukan kepada rentetan dalam kolam malar

    • Jika tiada kolam malar, salin rujukan dan letakkan dalam kolam malar (timbunan); (JDK1.7 mengalihkan kolam malar String dari kawasan Perm ke Kawasan Java Heap)

    Ujian rutin

    Rutin 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);
        }
    }

    Keputusan

    jdk6: palsu palsu
    jdk7: false true
    jdk8: false true

    Rutin 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);
        }
    }

    Bahagian kedua kod di atas mempunyai pertukaran.

    Keputusan

    jdk6: false false
    jdk7: false false
    jdk8: false false

    Analisis rutin

    Dalam gambar di bawah: garis hijau mewakili arah kandungan objek String. Garis merah mewakili alamat yang menunjuk.

    jdk1.6

    Analisis rutin 1 dan rutin 2

    Cara menggunakan kaedah pelatih dalam Java String

    Seperti yang ditunjukkan dalam gambar di atas. Mula-mula mari kita bincangkan tentang situasi dalam jdk6 Dalam jdk6, semua cetakan di atas adalah palsu, kerana kolam berterusan dalam jdk6 diletakkan di kawasan Perm, dan kawasan Perm dipisahkan sepenuhnya daripada kawasan Java Heap biasa. Seperti yang dinyatakan di atas, jika rentetan diisytiharkan menggunakan tanda petikan, ia akan dijana terus dalam kolam pemalar rentetan, dan objek Rentetan baharu diletakkan di kawasan JAVA Heap. Oleh itu, membandingkan alamat objek kawasan JAVA Heap dengan alamat objek kumpulan pemalar rentetan pasti berbeza, walaupun kaedah String.intern dipanggil, ia tidak mempunyai hubungan.

    jdk1.7

    Dalam Jdk6 dan versi sebelumnya, kumpulan rentetan berterusan diletakkan di kawasan Perm timbunan adalah kawasan seperti statik yang kebanyakannya menyimpan beberapa beban. Saiz lalai maklumat kelas, kumpulan malar, serpihan kaedah, dsb. hanya 4m Sebaik sahaja sejumlah besar pelatih digunakan dalam kumpulan malar, ralat ruang java.lang.OutOfMemoryError:PermGen akan berlaku. Dalam versi jdk7, kolam pemalar rentetan telah dialihkan dari kawasan Perm ke kawasan Java Heap biasa. Sebab utama mengapa ia perlu dipindahkan adalah kerana kawasan Perm terlalu kecil, sudah tentu, menurut berita, jdk8 telah membatalkan secara langsung kawasan Perm dan menubuhkan kawasan meta baru. Sepatutnya pemaju jdk berpendapat kawasan Perm sudah tidak sesuai untuk pembangunan JAVA sekarang. Kolam pemalar rentetan telah dialihkan ke kawasan JAVA Heap Sekarang terangkan mengapa terdapat hasil cetakan di atas.

    Analisis Rutin 1

    Cara menggunakan kaedah pelatih dalam Java String

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

    Analisis: Baris kod ini menjana 2 objek ("1" dalam kolam malar dan objek rentetan dalam JavaHeap). s.intern(); Ayat ini bermakna objek s1 pergi ke kolam malar untuk mencari dan mendapati bahawa "1" sudah berada dalam kolam malar.

    Pada masa ini, s1 menunjuk ke objek rentetan dalam Java Heap.

    2.String s2 = "1";

    Analisis: Baris kod ini menjana rujukan kepada s2 yang menunjuk ke objek "1" dalam kolam malar. Hasilnya ialah alamat rujukan s1 dan s2 adalah berbeza.

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

    Cara menggunakan kaedah pelatih dalam 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

    Cara menggunakan kaedah pelatih dalam Java String

    使用intern

    826ms

    Cara menggunakan kaedah pelatih dalam Java String

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

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

    Atas ialah kandungan terperinci Cara menggunakan kaedah pelatih dalam Java String. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

    Kenyataan:
    Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam