String some conclusions learning knowledge points

An example of a lead pig today.


        String ss = "ab";
        String s1 = "a";
        String s2 = "b";
        String s3 = s1 + s2;
        String s33 = "ab";
        String s4 = new String("a" + "b");

        System.out.println(ss == s3);//false
        System.out.println(s3 == s33);//false
        System.out.println(ss == s33);//true
        System.out.println("---------------------------------------");
        System.out.println(s3 == s1+s2);//false
        System.out.println(s3 == s4);//false
        System.out.println(ss == s4);//false
        System.out.println("---------------------------------------");
        String s5 = "a" + "b";
        System.out.println(s4 == s5);//false
        String s51 = "a" + "b";
        System.out.println(s51 == s5);//true
        System.out.println("---------------------------------------");
        final String s6 = "a";
        final String s7 = "b";
        System.out.println(ss == s5);//true
        System.out.println(ss == s6+s7); //true

The results:
false
false
to true

false
false
false

false
true

true
true

The following analysis of the results look
first of all, we all know that == 、equalsthese two are equal compare the way, comparing two basic types == judgment so true / false value based on equality, for non-basic type of comparison is the memory address They are the same.
ss == s3 falseDescription Variable String s3 = "a" + " b" is not equal to the address pointed SS = String "ab &";
String ss = "ab" ss 直接指向的是常量池中的“ab”的地址. S3 and not directly to the constant pool "ab" address, and therefore "a" + "b" there is a similar process to process new objects. It points to its address in the heap.

Next, ss == s33 truethe description is directed to the same address, are executed to the constant pool "ab".

 String s4 = new String("a" + "b");
 String ss = "ab";

s4 == ss false
This can be combined with the following frequently asked questions to explain:
As we all know, String There are two ways Statement

String a = "abc"
String b = new String("abc");

Both declarative approach is different, the first way is the stack memory a point to the constant pool "abc".
The second way is to open up the heap memory creates a String object, then the object points to the thread pool "abc".

Put another way is the answer to this question below.
String a = new String ( "abc ") How many objects are created?

  1. Find in constant pool if there is "abc" the object
    has a corresponding reference to an instance returns
    do not create a corresponding instance of an object
  2. In a heap new String ( "abc") objects
  3. The object address assigned to str4, create a reference

So, there is no `constant pool" abc "literal create two objects, or create an object, and create a reference variable, tend to make such a modification problems:
String str1 = new new String (" A "+" B "); how many objects would be created?
string str2 = new new string (" ABC ") +" ABC "; how many objects would be created?

str1:
string constant pool:" AB ": 1 a
heap: new string (" AB "): a
reference: str1: 1
total: 5

str2:
String constant pool: "ABC": 1 Ge
heap: new String ( "ABC") : 1 Ge
quote: str2: 1 Ge
Total: 3

The following indicates what String a = new String ( "abc") in the distribution graphically jvm memory,
Here Insert Picture Description

JVM memory area

JVM memory area is divided into 5 parts of the program counter, stack virtual machine, the local region method, the heap, the method area.
This 5 part can be based on whether the thread private, grouped into two categories.
Here Insert Picture Description

Thread private: a program counter, stack virtual machines, local area method.
Threads share: heap, the method area.

The program counter (thread private): , 一块较小的内存空间, 是当前线程所执行的字节码的行号指示器,每条线程都要有一个独立的程序计数器this type of memory is also known as "thread private" memory.
Java method being executed, then the counter is recorded the address (address of the current instruction) virtual machine bytecode instructions.

虚拟机栈(线程私有):是描述java方法执行的内存模型 ,每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息 。每一个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
栈帧( Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接(Dynamic Linking)、 方法返回值和异常分派( Dispatch Exception)。栈帧随着方法调用而创建,随着方法结束而销毁——无论方法是正常完成还是异常完成(抛出了在方法内未被捕获的异常)都算作方法结束。

本地方法区(线程私有):本地方法区和 Java Stack 作用类似, 区别是虚拟机栈为执行 Java 方法服务, 而本地方法栈则为Native 方法服务 , 如果一个 VM 实现使用 C-linkage 模型来支持 Native 调用, 那么该栈将会是一个C 栈,但 HotSpot VM 直接就把本地方法栈和虚拟机栈合二为一。

堆(Heap- 线程共享)-运行时数据区:是被线程共享的一块内存区域,创建的对象和数组都保存在 Java 堆内存中,也是垃圾收集器进行垃圾收集的最重要的内存区域 。由于现代 VM 采用分代收集算法, 因此 Java 堆从 GC 的角度还可以细分为: 新生代( Eden 区 、 From Survivor 区 和 To Survivor 区 )和老年代。

方法区/ 永久代(线程共享):即我们常说的永久代(Permanent Generation), 用于存储被 JVM 加载的类信息、常量、静态变量、即时编译器编译后的代码等数据 . HotSpot VM把GC分代收集扩展至方法区, 即使用Java堆的永久代来实现方法区, 这样 HotSpot 的垃圾收集器就可以像管理 Java 堆一样管理这部分内存,而不必为方法区开发专门的内存管理器(永久带的内存回收的主要目标是针对常量池的回收和类型的卸载, 因此收益一般很小)。
运行时常量池(Runtime Constant Pool)是方法区的一部分。Class 文件中除了有类的版本、字段、方法、接口等描述等信息外,还有一项信息是常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中 。 Java 虚拟机对 Class 文件的每一部分(自然也包括常量池)的格式都有严格的规定,每一个字节用于存储哪种数据都必须符合规范上的要求,这样才会被虚拟机认可、装载和执行。
常量池的设计保证了String 类型的唯一性,避免了重复开辟空间,节省了内存。这里可以扩展一个问题,String 为什么被设计成不可变的?

另java8开始 没有永久代一说,改成元空间。
Java 8: 从永久代(PermGen)到元空间(Metaspace)

String类型为什么不可变

下面来聊一下的String 为什么被设计成不可变的?

String是Java中最常用的类,是不可变的(Immutable), 那么String是如何实现Immutable呢,String为什么要设计成不可变呢?
先翻看String 底层实现:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0
    
    //other codes
}

String 的底层实现是依靠 char value[] 数组,既然依靠的是基础类型变量,那么他一定是可变的, String 之所以不可变,是因为 Java 的开发者通过技术实现,隔绝了使用者对 String 的底层数据的操作。

String 不可变的技术实现
String 类由关键字 final 修饰,说明该类不可继承
char value[] 属性也被 final 所修饰,说明 value 的引用在创建之后,就不能被改变
以上两点并不能完全实现 String 不可变 ,原因在于:

final int[] value={1,2,3}
      int[] another={4,5,6};
value=another;    // 编译器报错,final不可变

value 被 final 修饰,只能保证引用不被改变,但是 value 所指向的堆中的数组,才是真实的数据,只要能够操作堆中的数组,依旧能改变数据。

final int[] value={1,2,3};
value[2]=100;  //这时候数组里已经是{1,2,100}

所有的成员属性均被 private 关键字所修饰
为了实现 String 不可变,关键在于Java的开发者在设计和开发 String 的过程中,没有暴露任何的内部成员,与此同时 API 的设计是均没有操作 value 的值 , 而是采用 new String() 的方式返回新的字符串,保证了 String 的不可变。
JDK String API 源码:

public static String valueOf(char c) {
        char data[] = {c};
        return new String(data, true);  //采用 new String() 的方式返回新的字符串
    }
    
    public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);  //采用 new String() 的方式返回新的字符串
    }

整个String设成final禁止继承,避免被其他人继承后破坏。所以String是不可变的关键都在底层的实现,而不是一个final。

为什么会将 String 设计为不可变?

安全
保证线程安全,在并发场景下,多个线程同时读写资源时,会引竞态条件,由于 String 是不可变的,不会引发线程的问题而保证了线程的安全。
HashCode,当 String 被创建出来的时候,hashcode也会随之被缓存,hashcode的计算与value有关,若 String 可变,那么 hashcode 也会随之变化,针对于 Map、Set 等容器,他们的键值需要保证唯一性和一致性,因此,String 的不可变性使其比其他对象更适合当容器的键值。
性能
当字符串是不可变时,字符串常量池才有意义。字符串常量池的出现,可以减少创建相同字面量的字符串,让不同的引用指向池中同一个字符串,为运行时节约很多的堆内存。若字符串可变,字符串常量池失去意义,基于常量池的String.intern()方法也失效,每次创建新的 String 将在堆内开辟出新的空间,占据更多的内存。

字符串常量池

在《深入理解java虚拟机》这本书上是这样写的:对于HotSpot虚拟机,根据官方发布的路线图信息,现在也有放弃永久代并逐步的改为采用Native Memory来实现方法区的规划了,在目前已经发布的JDK1.7的HotSpot中,已经把原来存放在方法区中的字符串常量池移出。根据查阅的资料显示在JDK1.7以后的版本中字符串常量池移到堆内存区域;同时在jdk1.8中移除整个永久代,取而代之的是一个叫元空间(Metaspace)的区域。

在 JAVA 语言中有8中基本类型和一种比较特殊的类型String。这些类型为了使他们在运行过程中速度更快,更节省内存,都提供了一种常量池的概念。常量池就类似一个JAVA系统级别提供的缓存。

java中两种创建字符串对象的方式的分析。

它的主要使用方法有两种:

  • 直接使用双引号声明出来的String对象会直接存储在常量池中。
  • 如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中
String s1 = "abc";
String s2 = "abc";
System.out.println(s1==s2);  true

采用字面值的方式创建一个字符串时,JVM首先会去字符串池中查找是否存在"abc"这个对象,如果不存在,则在字符串常量池中创建"abc"这个对象,然后将池中"abc"这个对象的引用地址返回给"abc"对象的引用s1,这样s1会指向字符串常量池中"abc"这个字符串对象;如果存在,则不创建任何对象,直接将池中"abc"这个对象的地址返回,赋给引用s2。因为s1、s2都是指向同一个字符串池中的"abc"对象,所以结果为true。

String s3 = new String("xyz");
String s4 = new String("xyz");
System.out.println(s3==s4); false

采用new关键字新建一个字符串对象时,JVM首先在字符串池中查找有没有"xyz"这个字符串对象,如果有,则不在池中再去创建"xyz"这个对象了,直接在堆中创建一个"xyz"字符串对象,然后将堆中的这个"xyz"对象的地址返回赋给引用s3,这样,s3就指向了堆中创建的这个"xyz"字符串对象;如果没有,则首先在字符串池中创建一个"xyz"字符串对象,然后再在堆中创建一个"xyz"字符串对象,然后将堆中这个"xyz"字符串对象的地址返回赋给s3引用,这样,s3指向了堆中创建的这个"xyz"字符串对象。s4则指向了堆中创建的另一个"xyz"字符串对象。s3 、s4是两个指向不同对象的引用,结果当然是false。

Intern的实现原理

/**
     * Returns a canonical representation for the string object.
     * <p>
     * A pool of strings, initially empty, is maintained privately by the
     * class {@code String}.
     * <p>
     * When the intern method is invoked, if the pool already contains a
     * string equal to this {@code String} object as determined by
     * the {@link #equals(Object)} method, then the string from the pool is
     * returned. Otherwise, this {@code String} object is added to the
     * pool and a reference to this {@code String} object is returned.
     *
     * @return  a string that has the same contents as this string, but is
     *          guaranteed to be from a pool of unique strings.
     */
    public native String intern();

String # intern method see, this method is a native method, but written comments are very clear. "When the intern method is invoked, if the pool already contains a string such as this String object (determined by equals (oject) method), the string pool is returned. Otherwise, this String object is added to the pool and return reference to this String object.

public void test(){
        //发现原来是在JVM启动的时候调用了一些方法,在常量池中已经生成了"java"字符串常量,
         String s2 = new String("ja") + new String("va");
         String s3 =  s2.intern();
         String s4 = "java";
         System.out.println(s2 == s3);fasle
         System.out.println(s3 == s4);true
    }

JDK1.8 version, String constant pool area has been isolated from the operating method of the time constant pool to the heap, then the String constant pool is stored in the stack String object or reference it?

Relevant information:

String the interview do not ask me
why the immutable type String
String string "true storage location"
String memory model, why String is designed to be immutable
depth analysis Intern # String
JDK1.8 version of java string constant pool of memory a String object or a reference?

If you are keen to progress with technology Welcome to the additive group: 230,274,309. Share together, progress together! Less the water, dry goods and more! ! welcome everybody! ! ! (Convicted of diving into the group added)

Here Insert Picture Description

Published 134 original articles · won praise 197 · views 410 000 +

Guess you like

Origin blog.csdn.net/u011733020/article/details/103924943