JVM专题-StringTable

Posted IT老刘

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JVM专题-StringTable相关的知识,希望对你有一定的参考价值。

1.StringTable特性

  • 常量池中的字符串仅是符号,第一次用到时才变成对象。

  • 利用串池的机制,来避免重复创建字符串对象

  • 字符串变量拼接的原理是StringBuilder(1.8)

  • 字符串常量拼接的原理是编译期优化

  • 可以使用intern方法,主动将串池中还没有的字符串对象放入串池

  • 1.8将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池,两种情况均会把串池中的对象返回。

  • 1.6将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份,放入串池,两种情况均会把串池中的对象返回。即调用intern的对象,和将来放入串池的对象,是两个对象。

public class Demo1_23 

    //  ["ab", "a", "b"]
    public static void main(String[] args) 

        String x = "ab";
        String s = new String("a") + new String("b");

        // 堆  new String("a")   new String("b") new String("ab")
        String s2 = s.intern(); // 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回

        System.out.println( s2 == x);//true
        System.out.println( s == x );//false
    


  1. 执行到第10行String s2 = s.intern(); 时,将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回。由于String x = “ab”;StringTable中有"ab"了,所以s不会放在常量池。但是会吧常量池的"ab"返回回来给s2。
    所以 s2 == x 即 常量池的"ab"
  2. s != x,因为s没有放进去
public class Demo1_223 

    //  ["a", "b","ab" ]
    public static void main(String[] args) 

        String s = new String("a") + new String("b");

        // 堆  new String("a")   new String("b") new String("ab")
        String s2 = s.intern(); // 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回

        System.out.println( s2 == "ab"); //true
        System.out.println( s == "ab" ); //true
    

  1. 执行到第10行String s2 = s.intern(); 时,将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回。此时StringTable还没有"ab",所以s会放在常量池。同时常量池的"ab"返回回来给s2。
    所以 s2 == x 即 常量池的"ab"
  2. s == x,因为s放进去了

JDK1.6运行情况:

public class Demo111 

    //  ["a", "b","ab" ]
    public static void main(String[] args) 

        String s = new String("a") + new String("b");

        // 堆  new String("a")   new String("b") new String("ab")
        String s2 = s.intern(); // 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回

        System.out.println( s2 == "ab"); //true
        System.out.println( s == "ab" ); //false
    


2.StringTable面试题

public class Demo1_21 

    public static void main(String[] args) 
        String s1 = "a";
        String s2 = "b";
        String s3 = "a"+"b"; // 优化为"ab"
        String s4 = s1 + s2; // new StringBuilder 在堆中
        String s5 = "ab"; // 去串池查找添加
        String s6 = s4.intern();

        System.out.println(s3 == s4); // false
        System.out.println(s3 == s5); // true
        System.out.println(s3 == s6); // true

        String x2 = new String("c") + new String("d"); // new String("cd")
        String  x1 = "cd"; //"cd"
        x2.intern(); // 串池已经有cd,x2放不进去
        System.out.println(x1 == x2); // false
    


如果调换位置

String x2 = new String("c") + new String("d"); // new String("cd")
x2.intern(); // 串池已没有cd,x2放进去
String  x1 = "cd"; //"cd"

System.out.println(x1 == x2); // true

3.StringTable位置

  • 在1.6之前。StringTable在方法区永久代中,但是放在这之中,虚拟机只会在full GC时才会对StringTable进行垃圾回收。但是其实StringTable的操作是非常频繁的,如果没有即使进行垃圾回收,容易造成永久代空间不足。

  • 在1.8后,StringTable放在了堆中,使其垃圾回收的效率更高。

4.案例

JDK1.6下

虚拟机参数

-XX:MaxPermSize=10m  //永久代空间设置
package com.bruce.test;


import java.util.ArrayList;
import java.util.List;

/**
 * 演示 StringTable 位置
 * 在jdk8下设置 -Xmx10m -XX:-UseGCOverheadLimit
 * 在jdk6下设置 -XX:MaxPermSize=10m
 */
public class Demo1_6 

    public static void main(String[] args) throws InterruptedException 
        List<String> list = new ArrayList<String>();
        int i = 0;
        try 
            for (int j = 0; j < 260000; j++) 
                list.add(String.valueOf(j).intern());
                i++;
            
         catch (Throwable e) 
            e.printStackTrace();
         finally 
            System.out.println(i);
        
    




JDK1.8下

虚拟机参数

// 堆空间大小设置
-Xmx10m
public class Demo1_221 
    public static void main(String[] args) 
        List<String> list = new ArrayList<>();
        int i = 0;
        try 
            for (int j = 0; j < 260000; j++) 
                list.add(String.valueOf(j).intern());
                i++;
            
         catch (Throwable e) 
            e.printStackTrace();
         finally 
            System.out.println(i);
        
    



添加JVM参数:

// 堆空间大小设置、关闭UseGCOverheadLimit
-Xmx10m -XX:-UseGCOverheadLimit

可以看到1.6是永久代空间溢出,1.8是堆空间溢出。

5.StringTable垃圾回收

StringTable中的字符串常量不再引用后,也会被垃圾回收。

package com.method.area;

/**
 * 演示 StringTable 垃圾回收
 * -Xmx10m -XX:+PrintStringTableStatistics -XX:+PrintGCDetails -verbose:gc
 */
public class Demo1_231 
    public static void main(String[] args) 
        int i = 0;
        try 
            for (int j = 0; j < 10; j++) 
                //String.valueOf(j).intern();
                i++;
            
         catch (Throwable e) 
            e.printStackTrace();
         finally 
            System.out.println(i);
        
    

public class Demo1_23 
    public static void main(String[] args) 
        int i = 0;
        try 
            for (int j = 0; j < 10; j++) 
                String.valueOf(j).intern();
                i++;
            
         catch (Throwable e) 
            e.printStackTrace();
         finally 
            System.out.println(i);
        
    


for (int j = 0; j < 100000; j++) 
    String.valueOf(j).intern();
    i++;



清理了一些未引用的字符串常量。

以上是关于JVM专题-StringTable的主要内容,如果未能解决你的问题,请参考以下文章

JVM专题-StringTable调优

JVM专题-方法区

JVM - StringTable

JVM系列之:String.intern和stringTable

JVM--13---StringTable

Day337&338.StringTable -JVM