JDK1.6、1.7、1.8内存区域的变化和String str=“aaa“与 String str=new String(“aaa“)一样吗?new String(“aaa”);创建了几个字符串对象

JDK1.6、1.7、1.8内存区域的变化?

JDK1.6、1.7/1.8内存区域发生了变化,主要体现在方法区的实现:

  1. JDK1.6使用永久代实现方法区:
    在这里插入图片描述
  2. JDK1.7时发生了一些变化,将字符串常量池、静态变量,存放在堆上:
    在这里插入图片描述
  3. 在JDK1.8时彻底干掉了永久代,而在直接内存中划出一块区域作为元空间,运行时常量池、类常量池都移动到元空间。
    在这里插入图片描述
    JVM将虚拟机分为5大区域,程序计数器、虚拟机栈、本地方法栈、java堆、方法区,其中方法区和堆是线程共享区,虚拟机栈、本地方法栈和程序计数器是线程私有的。
  4. 程序计数器:线程私有的,是一块很小的内存空间,作为当前线程的行号指示器,用于记录当前虚拟机正在执行的线程指令地址;
  5. 虚拟机栈:线程私有的,每个方法执行的时候都会创建一个栈帧,用于存储局部变量表、操作数、动态链接和方法返回等信息,当线程请求的栈深度超过了虚拟机允许的最大深度时,就会抛出StackOverFlowError;
  6. 本地方法栈:线程私有的,保存的是native方法的信息,当一个jvm创建的线程调用native方法后,jvm不会在虚拟机栈中为该线程创建栈帧,而是简单的动态链接并直接调用该方法;
  7. :Java堆是所有线程共享的一块内存,几乎所有对象的实例和数组都要在堆上分配内存,因此该区域经常发生垃圾回收的操作;
  8. 方法区:存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码数据。jdk1.8之前使用永久代实现,在jdk1.8中使用元数据区实现,原方法区被分成两部分;1:加载的类信息,加载的类信息被保存在元数据区中;2:运行时常量池。字符串常量池保存在堆中;
    在这里插入图片描述
    在了解JDK1.6、1.7、1.8内存解构之后,我们来看下面这个问题

String str="aaa"与 String str=new String(“aaa”)一样吗?new String(“aaa”);创建了几个字符串对象?

  • 使用String a = “aaa” ;,程序运行时会在常量池中查找”aaa”字符串,若没有,会将”aaa”字符串放进常量池,再将其地址赋给a;若有,将找到的”aaa”字符串的地址赋给a。
  • 使用String b = new String(“aaa”);`,程序会在堆内存中开辟一片新空间存放新对象,同时会将”aaa”字符串放入常量池,相当于创建了两个对象,无论常量池中有没有”aaa”字符串,程序都会在堆内存中开辟一片新空间存放新对象。
    具体分析,见以下代码:
 @Test
    public void test(){
        String s = new String("2");
        s.intern();
        String s2 = "2";
        System.out.println(s == s2);


        String s3 = new String("3") + new String("3");
        s3.intern();
        String s4 = "33";
        System.out.println(s3 == s4);
    }

运行结果:

jdk6
false
false

jdk7
false
true

这段代码在jdk6中输出是false false,但是在jdk7中输出的是false true。我们通过图来一行行解释。
先来认识下intern()函数:

  1. 在JDK1.6中,intern的处理是 先判断字符串常量是否在字符串常量池中,如果存在直接返回该常量,如果没有找到,则将该字符串常量加入到字符串常量池,也就是在字符串常量池建立该常量;
  2. 在JDK1.7中,intern的处理是 先判断字符串常量是否在字符串常量池中,如果存在直接返回该常量,如果没有找到,说明该字符串常量在堆中,则处理是把堆区该对象的引用加入到字符串常量池中,以后别人拿到的是该字符串常量的引用,实际存在堆中
    JDK1.6
    在这里插入图片描述

String s = new String(“2”);创建了两个对象,一个在堆中的StringObject对象,一个是在常量池中的“2”对象。 s.intern();在常量池中寻找与s变量内容相同的对象,发现已经存在内容相同对象“2”,返回对象2的地址。 String s2 = “2”;使用字面量创建,在常量池寻找是否有相同内容的对象,发现有,返回对象"2"的地址。 System.out.println(s == s2);从上面可以分析出,s变量和s2变量地址指向的是不同的对象,所以返回false

String s3 = new String(“3”) + new String(“3”);创建了两个对象,一个在堆中的StringObject对象,一个是在常量池中的“3”对象。中间还有2个匿名的new String(“3”)我们不去讨论它们。 s3.intern();在常量池中寻找与s3变量内容相同的对象,没有发现“33”对象,在常量池中创建“33”对象,返回“33”对象的地址。 String s4 = “33”;使用字面量创建,在常量池寻找是否有相同内容的对象,发现有,返回对象"33"的地址。 System.out.println(s3 == s4);从上面可以分析出,s3变量和s4变量地址指向的是不同的对象,所以返回false

s 被初始化为一个新的字符串对象,内容为 “2”。在 s 上调用 intern() 方法,但这个调用不会影响与 s2 的比较,因为 s2 是字符串字面量,已经存在于字符串池中。因此,s == s2 的结果为 false。

s3 被初始化为两个内容为 “3” 的新字符串对象的连接。在 s3 上调用 intern() 方法,但同样地,这个调用不会影响与 s4 的比较,因为 s4 是字符串字面量,已经存在于字符串池中。因此,s3 == s4 的结果为 false。

大家这块需要注意的是:JDK1.7之前的常量池在永久代中,JDK1.6的方法区的实现为永久代,所以上图中的堆和常量池是在俩个区域中,而JDK1.7以后,字符串常量池放在了堆中

JDK1.7
大家首先要明确的一点就是jdk1.7以后字符串常量池放进了堆中,所以就会对intern()方法产生一些变化 ,大家可以回顾一下俩个版本中的变化:

  1. 在JDK1.6中,intern的处理是 先判断字符串常量是否在字符串常量池中,如果存在直接返回该常量,如果没有找到,则将该字符串常量加入到字符串常量池,也就是在字符串常量池建立该常量;
  2. 在JDK1.7中,intern的处理是 先判断字符串常量是否在字符串常量池中,如果存在直接返回该常量,如果没有找到,说明该字符串常量在堆中,则处理是把堆区该对象的引用加入到字符串常量池中,以后别人拿到的是该字符串常量的引用,实际存在堆中
    在这里插入图片描述
  • String s = new String(“2”);创建了两个对象,一个在堆中的StringObject对象,一个是在堆中的“2”对象,并在常量池中保存“2”对象的引用地址。 s.intern();在常量池中寻找与s变量内容相同的对象,发现已经存在内容相同对象“2”,返回对象“2”的引用地址。 String s2 = “2”;使用字面量创建,在常量池寻找是否有相同内容的对象,发现有,返回对象“2”的引用地址。 System.out.println(s == s2);从上面可以分析出,s变量和s2变量地址指向的是不同的对象,所以返回false

  • String s3 = new String(“3”) + new String(“3”);创建了两个对象,一个在堆中的StringObject对象,一个是在堆中的“3”对象,并在常量池中保存“3”对象的引用地址。中间还有2个匿名的new String(“3”)我们不去讨论它们。 s3.intern();在常量池中寻找与s3变量内容相同的对象,没有发现“33”对象,将s3对应的StringObject对象的地址保存到常量池中,返回StringObject对象的地址。 String s4 = “33”;使用字面量创建,在常量池寻找是否有相同内容的对象,发现有,返回其地址,也就是StringObject对象的引用地址。 System.out.println(s3 == s4);从上面可以分析出,s3变量和s4变量地址指向的是相同的对象,所以返回true。

  • 24
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值