Java --- JVM之StringTable

目录

一、String的基本特性

 二、String的内存分配

2.1、String内存分布图

三、字符串拼接操作 

 3.1、字符串拼接操作底层原理

3.2、拼接操作与append操作效率对比 

四、intern()方法 

4.1、intern()效率

五、StringTable的垃圾回收

一、String的基本特性

1、String字符串,使用一对""引起表示

2、String声明为Final的,不可被继承

3、String实现了Serializable接口:表示字符串是支持序列化的。实现Comparable接口:表示String可以比较大小。

4、String字符串在JDK8及以前内部定义了final char[] value用于存储字符串数据。jdk9时改为byte[]。

5、String代表不可变的字符串序列。①、当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值。②、当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。③、当调用String的replace()方法修改指定字符或字符串时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。

6、通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中。

参考代码:

public class StringTest {
    @Test
    public void test1(){
        String s1 = "a";
        String s2 = "a";
        s2 = "b";
        System.out.println(s1 == s2);//false
        System.out.println(s1);//a
        System.out.println(s2);//b
    }
    @Test
    public void test2(){
        String s1 = "a";
        String s2 = "a";
        s2 += "b";
        System.out.println(s1);//a
        System.out.println(s2);//ab
    }
    @Test
    public void test3(){
        String s1 = "a";
        String s2 = s1.replace('a','b');
        System.out.println(s1);//a
        System.out.println(s2);//b
    }
}

面试题:

public class StringTest02 {
    String s1 = new String("hello");
    char[] chars = {'t','o','m'};

    public void change(String s1,char chars[]){
        s1 = "test ok";
        chars[0] = 'a';
    }

    public static void main(String[] args) {
        StringTest02 str = new StringTest02();
        str.change(str.s1,str.chars);
        System.out.println(str.s1);//”hello“
        System.out.println(str.chars);//”aom“
    }
}

 7、字符串常量池是不会存储相同内容的字符串。

①、String的String Pool是一个固定大小的Hashtable,默认值大小长度是1009.如果放进String Pool的String非常多,就会造成Hash冲突严重,从而导致链表会很长,而链表长了后直接会造成的影响就是调用String.intern时性能会大幅度下降。

参考代码:

public class StringTest04 {
    public static void main(String[] args) {
        try {
            FileWriter fileWriter = new FileWriter("hello.txt");
            for (int i = 0; i < 100000; i++) {
                int length = (int) (Math.random() * (10) + 1);
                fileWriter.write(getString(length) + "\n");
            }
            fileWriter.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    public static String getString(int length){
        String str = "";
        for (int i = 0; i < length; i++) {
            int num = (int) (Math.random() * (90 - 65 + 1) + 65) + (int) (Math.random() * 2) * 32;
            str += (char)num;

        }
        return str;
    }
}
public class StringTest03 {
    public static void main(String[] args)  {
        //参数设置:-XX:StringTableSize=1009
//        System.out.println("String参数设置开始");
//        try {
//            Thread.sleep(100000);
//        } catch (InterruptedException e) {
//            throw new RuntimeException(e);
//        }
        BufferedReader bufferedReader = null;
        try {
             bufferedReader = new BufferedReader(new FileReader("hello.txt"));
            long start = System.currentTimeMillis();
            String data;
            while ((data = bufferedReader.readLine()) != null){
                data.intern();
            }
            long end = System.currentTimeMillis();
            System.out.println("花费的时间为:" + (end - start));//参数1009.时间91ms。参数1000009,时间35ms
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            if (bufferedReader != null){
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

}

②、使用-XX:StringTableSize可设置StringTable的长度。

③、在jdk6中的StringTable的是固定的,就是1009的长度,所以如果常量池中的字符串过多就会导致效率下降很快。StringTableSize设置没有要求。

④、在jdk7中,StringTable的默认长度是60013,jdk8及以后1009是设置的最小值。

以jdk8测试:

 二、String的内存分配

1、Java语言中有八大基本数据类型和特殊的String类型,这些类型为了使它们在运行过程中速度更快,更节省内存,都提供了一种常量池概念。

2、常量池类似一个Java系统级别提供缓存。8种基本数据类型的常量池都是系统协调的,String类型的常量池比较特殊。

      ①、直接使用双引号声明出来的String对象会直接存储在常量池中。

      ②、不使用双引号声明的String对象,可以使用String提供的intern()方法。

3、Java6及以前,字符串常量池存放在永久代中。

4、Java7中将字符串常量池的位置调整到Java堆中

     ①、所有的字符串都保存在堆中,和其他普通对象一样,这样可以在进行调优应用时仅需要调整堆大小就可以了。

     ②、字符串常量池概念原本使用的比较多,但改动后可以重新考虑在Java7中使用String.intern()

5、Java8元空间,字符串常量在堆。

2.1、String内存分布图

三、字符串拼接操作 

1、常量与常量的拼接结果是在常量池,原理是编译期优化。

2、常量池中不会存在相同内容的常量。

3、只要其中一个是变量,结果就在堆中。变量的拼接的原理是StringBuilder。

4、如果拼接的结果调用intern()方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象地址。

参考代码:

 @Test
    public void test1(){
        String s1 = "a" + "b" + "c";
        String s2 = "abc";
        System.out.println(s1 == s2);//true
        System.out.println(s1.equals(s2));//true
    }
    @Test
    public void test2(){
        String s1 = "javaee";
        String s2 = "hadoop";
        String s3 = "javaeehadoop";
        String s4 = "javaee" + "hadoop";//编译期优化
        //拼接字符串的前后出现变量,相当于在堆空间中new String(),
        String s5 = s1 + "hadoop";
        String s6 = "javaee" + s2;
        String s7 = s1 + s2;
        System.out.println(s3 == s4);//true
        System.out.println(s3 == s5);//false
        System.out.println(s3 == s6);//false
        System.out.println(s3 == s7);//false
        System.out.println(s5 == s6);//false
        System.out.println(s5 == s7);//false
        System.out.println(s6 == s7);//false
        //intern(),判断字符串常量池中是否存在javaeehadoop值,有就返回该值地址,没有就重新加载一份。
        String s8 = s6.intern();
        System.out.println(s3 == s8);//true
    }

 3.1、字符串拼接操作底层原理

参考代码:

 @Test
    public void test3(){
       String s1 = "a";
       String s2 = "b";
       String s3 = "ab";
        /**
         * s1 + s2执行步骤
         * ①、StringBuilder s = new StringBuilder();
         * ②、s.append("a")
         * ③、s.append("b")
         * s.toString() ---> 约等于 new String("ab");
         */
       String s4 = s1 + s2;
        System.out.println(s3 == s4);//false
    }
    @Test
    public void test4(){
        /**
         * 字符串拼接操作不一定使用StringBuilder()
         * 如拼接符号左右两边都是字符串常量或常量引用,则仍然使用编译期优化
         * 针对于final修饰的类、方法、基本数据类型,引用数据类型的量的结构时,能使用final时建议使用
         */
        final String s1 = "a";
        final String s2 = "b";
        String s3 = "ab";
        String s4 = s1 + s2;
        System.out.println(s3 == s4);//true
    }

3.2、拼接操作与append操作效率对比 

参考代码:

@Test
    public void test5(){
        /**
         *通过StringBuilder的append()的方式添加字符串的效率要远高于字符串拼接
         * ①、StringBuilder的append()方式,只需要创建一个StringBuilder对象,而字符串拼接则需要创建多个StringBuilder和String对象
         * ②、使用String的字符串拼接方式,内存中创建了较多的StringBuilder和String对象,内存占用更大,如垃圾回收效率要更频繁
         * 优化:在基本确定要添加的字符串的长度不高于某个限定值highlevel,可以使用构造器new StringBuilder(参数)
         */
        long start = System.currentTimeMillis();
        method1(10000);
        long end = System.currentTimeMillis();
        System.out.println("method1花费时间为:" + (end - start));//89
        long start1 = System.currentTimeMillis();
        method2(10000);
        long end1 = System.currentTimeMillis();
        System.out.println("method2花费时间为:" + (end1 - start1));//0
    }
    public void method1(int highLevel){
        String str = "";
        for (int i = 0; i < highLevel; i++) {
            str = str + "a";
        }
    }
    public void method2(int highLevel){
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < highLevel; i++) {
            stringBuilder = stringBuilder.append("a");
        }
    }

四、intern()方法 

如果不是双引号声明的String对象,可以使用String提供的intern():intern方法会从字符串常量池中查询当前字符串是否存在,如不存在就会将当前字符串放入常量池中。

public class StringTest07 {
    //以jdk8为例
    public static void main(String[] args) {
        String s = new String("1");
        s.intern();//调用此方法之前,常量池中已经有“1"
        String s1 = "1";
        System.out.println(s == s1);//false
        //s3变量的记录地址为new String(”11“),但在常量池中没有创建的”11“
        String  s3 = new String("1") + new String("1");
        s3.intern();//该方法执行完就生成11,但没有在常量池中创建”11“,而是创建一个指向堆空间中new String(”11“)的地址
        String s4 = "11";//使用的是上行代码生成的”11“ --》即常量池中生成的”11“的地址
        System.out.println(s3 == s4);//true
    }
}

 总结:在jdk6中,如果串池中有,就返回已有的串池中的对象的地址,如果没有,就是将这个对象复制一份,放入串池,并返回这个对象的地址。从jdk7起,如果串池中有,就返回已有的串池中的对象的地址,如果没有,就是将这个对象的引用地址复制一份,放入串池,并返回串池中的引用地址。

4.1、intern()效率

参考代码:

public class StringTest08 {
    static final int MAX_COUNT = 1000 * 10000;
    static final String[] arr = new String[MAX_COUNT];
    public static void main(String[] args) {
        int[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9};
        long start = System.currentTimeMillis();
        for (int i = 0; i < MAX_COUNT; i++) {
            //arr[i] = new String(String.valueOf(data[i % data.length]));
            arr[i] = new String(String.valueOf(data[i % data.length])).intern();
        }
        long end = System.currentTimeMillis();
        System.out.println("花费的时间为:" + (end - start));
        try {
            Thread.sleep(100000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.gc();
    }
}

 结论:对于程序中大量存在的字符串,尤其其中存在很多重复字符串时,使用intern()可以节省很多内存空间。

五、StringTable的垃圾回收

参考代码:

参数设置:-Xms15m -Xmx15m -XX:+PrintStringTableStatistics -XX:+PrintGCDetails

public class StringTest09 {
    public static void main(String[] args) {
        for (int i = 0; i < 1000000; i++) {
            String.valueOf(i).intern();
        }
    }
}

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/150186.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

彩票双色球预测工具1.0

搏一搏 单车变跑车 祝各位开出大奖&#xff01;&#xff01; 后续会持续更新&#xff0c;欢迎关注&#x1f44f; from random import randintdef gener_blue_ball():return randint(1, 33)def gener_blue_ball_s():blue_ball_set set()while True:if len(blue_ball_set) 6:b…

【数据结构】希尔排序(最小增量排序)

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和算法 ✈️专栏&#xff1a;数据结构 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵 希望大佬指点一二 如果文章对你有帮助…

【数据结构初阶】链表OJ

链表OJ 题目一&#xff1a;移除链表元素题目二&#xff1a;反转链表题目三&#xff1a;链表的中间节点题目四&#xff1a;链表中倒数第k个结点题目五&#xff1a;合并两个有序链表题目六&#xff1a;链表分割题目七&#xff1a;链表的回文结构题目八&#xff1a;相交链表题目九…

JavaScript概述

一、JavaScript简介&#xff1a; JavaScript是互联网上流行的脚本语言&#xff0c;可用于HTML和web&#xff0c;可广泛应用于服务器、PC、笔记本、平板电脑和智能手机等设备。 JavaScript是一种轻量级的编程语言&#xff0c;可插入HTML页面的编程代码&#xff0c;插入HTML页面后…

联盟 | Quick Creator X HelpLook ,AI助力打造出海企业内容营销

随着人工智能的快速发展&#xff0c;各行各业也加快了人工智能工具的探索&#xff0c;积极将AI融入到行业发展中&#xff0c;出海外贸也不例外。AI渐渐地融入到外贸的各个环节&#xff0c;为企业出海提速增效。 为此&#xff0c;「 Quick Creator 」与 「 HelpLook 」达成战略合…

mask-rcnn原理与实战

一、Mask R-CNN是什么&#xff0c;可以做哪些任务&#xff1f; Mask R-CNN是一个实例分割&#xff08;Instance segmentation&#xff09;算法&#xff0c;可以用来做“目标检测”、“目标实例分割”、“目标关键点检测”。 1. 实例分割&#xff08;Instance segmentation&am…

Python爬虫的七个常用技巧总结,这些你一定得知道!

文章目录 前言1、基本抓取网页2、使用代理IP3、Cookies处理4、伪装成浏览器5、验证码的处理6、gzip压缩7、多线程并发抓取关于Python技术储备一、Python所有方向的学习路线二、Python基础学习视频三、精品Python学习书籍四、Python工具包项目源码合集①Python工具包②Python实战…

前端element的el-tooltip鼠标经过显示文字,没有文字显示空黑框问题

场景&#xff1a; 有时候在使用element的el-tooltip时会使用三元表达式&#xff0c;满足某个条件后才显示提示文字&#xff0c;否则不展示文字&#xff0c;但是却出现在在没有文字时展示了黑框&#xff0c;如下图&#xff1a; 解决方案&#xff1a; 加一个disabled便可&#…

手机维修店服务预约小程序的效果怎样

手机维修店在每个城市的商业街或商场里都很常见&#xff0c;目前人手一个或多个手机&#xff0c;虽然手机质量有了提升&#xff0c;但各种毛病依然无法避免&#xff0c;因此手机维修店虽然不是人挤人&#xff0c;但整体生意还可以&#xff0c;各种服务也愈加完善。 而市场接受…

人力物力和时间资源有限?守住1个原则,精准覆盖所有兼容性测试!

随着 APP 应用范围越来越广&#xff0c;用户群体越来越大&#xff0c;终端设备的型号也越来越多&#xff0c;移动终端碎片化加剧&#xff0c;使得 APP 兼容性测试成为测试质量保障必须要考虑的环节。 APP 兼容性测试通常会考虑&#xff1a;操作系统、厂家 ROM、屏幕分辨率、网…

掌握Python中的控制流语句:break, continue, quit的应用技巧详解

引言 在Python编程中&#xff0c;控制流语句是非常重要的一部分&#xff0c;它们可以帮助我们控制程序的执行流程。其中&#xff0c;break、continue和quit是常用的控制流语句&#xff0c;它们可以在循环中起到关键作用。本文将详细介绍这些控制流语句的应用技巧&#xff0c;帮…

7天入门python系列之爬取热门小说项目实战,互联网的东西怎么算白嫖呢

第七天 Python项目实操 编者打算开一个python 初学主题的系列文章&#xff0c;用于指导想要学习python的同学。关于文章有任何疑问都可以私信作者。对于初学者想在7天内入门Python&#xff0c;这是一个紧凑的学习计划。但并不是不可完成的。 学到第7天说明你已经对python有了一…

Playwright UI 自动化测试实战

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

VSCode配置MingW编译调试环境

1.MingW简介 MinGW&#xff0c;即 Minimalist GNU For Windows。它是一些头文件和端口库的集合&#xff0c;该集合允许人们在没有第三方动态链接库的情况下使用 GCC&#xff08;GNU Compiler C&#xff09;产生 Windows32 程序。 实际上 MinGW 并不是一个 C/C 编译器&#xf…

亚马逊产品召回后如何重新发布商品?儿童睡衣,婴儿沐浴座椅,不锈钢儿童水杯被召回

近日&#xff0c;又有一款亚马逊热销超34w的产品出现安全问题。据外媒报道&#xff0c;7月20日&#xff0c;CPSC发现一款产品名为CUPKIN双层不锈钢儿童水杯存在铅中毒的风险&#xff0c;该水杯的铅含量超过联邦铅含量禁令。 CPSC在召回公告中指出&#xff0c;因为铅极容易被6岁…

南湖HIT论坛|《医疗数据安全风险分析及防范实践(2023)》正式发布

11月11日&#xff0c;2023年南湖HIT论坛在浙江嘉兴隆重举行。作为颇受HIT从业者关注的年度盛会——本届论坛以“数据驱动医院运营管理”为主题&#xff0c;全国各地医疗机构信息主管和骨干围绕“数据驱动运营管理、运营数据中心建设、数据治理和数据安全”等话题展开深入研讨。…

Python自动化测试:web自动化测试——Selenium框架

web自动化测试1 Selenium介绍web自动化实现原理环境准备1&#xff09;Seleniumpython环境搭建安装步骤环境变量的配置 2&#xff09;浏览器驱动驱动下载驱动环境配置 3&#xff09;版本检查4&#xff09;其他异常情况排查版本不一致未激活卸载、降低/升级setuptools版本 web自动…

什么是CSV文件,以及如何打开CSV文件格式

​CSV代表Comma Separated Values。CSV文件是一种纯文本文件&#xff0c;用于存储表格和电子表格信息。内容通常是由文本、数字或日期组成的表格。使用将数据存储在表中的程序可以轻松导入和导出CSV文件。 CSV文件格式 通常CSV文件的第一行包含表格列标签。后面的每一行表示表…

【软考篇】中级软件设计师 第三部分(一)

中级软件设计师 第三部分&#xff08;一&#xff09; 十七. I/O管理软件十八. 输入/输出技术十九. 总线系统二十. 磁盘管理20.1 移臂调度算法20.2 缓冲区 二十一. 操作系统二十二. 数据库22.1 三级模式-两级映射22.2 数据库设计22.3 规范化理论22.4 范式判断22.5 模式分解 二十…