詳見:https://blog.csdn.net/guoxiaolongonly/article/details/80425548
1.常量池存放於方法區中
2.jdk1.6 方法區放在永久代(java堆的一部分),jdk1.7 特別將字符串常量池移動到了的堆內存中(使用參數-XX:PermSize 和-XX:MaxPermSize指定大小),jdk1.8放在單獨的元空間里面(-XX:MaxMetaspaceSzie設定大小),和堆相獨立。所以導致string的intern方法因為以上變化在不同版本會有不同表現。
3.jdk1.6將Hotspot虛擬機使用永久代來實現方法區,因為方法區的內存回收跟堆內存回收其實沒什么區別,這樣實現可以用垃圾收集器來管理這部分內存,但這樣容易導致內存溢出(達到-XX:MaxPermSize)。
JDK1.6,JDK1.7常量池的存放如下都存放於堆內存中
JDK1.8常量池的存放如下
具體可參考:https://blog.csdn.net/zhyhang/article/details/17246223/
知道了常量池在內存中的存放后,我們需要先了解一下 String str=“abc”;和 String str =new String(“abc”);的區別
1.String str=“abc”;
JDK1.6
(1) 當常量池中不存在"abc"這個字符串的引用,在堆內存中new一個String對象,復制這個對象加入常量池,返回常量池中的對象。
(2) 當常量池中存在"abc"這個字符串對象,str指向這個對象的引用;
JDK1.7以上
(1) 當常量池中不存在"abc"這個字符串的引用,在堆內存中new一個新的String對象,將這個對象的引用加入常量池。(跟1.6的區別是常量池不再存放對象,只存放引用。)
(2) 當常量池中存在"abc"這個字符串的引用,str指向這個引用;
2.String str =new String(“abc”)
單純的在堆內存中new一個String對象,通過StringBuilder 跟StringBuffer 構建的對象也是一樣
3.intern方法 (返回常量池中該字符串的引用)
(1) 當常量池中不存在"abc"這個字符串的引用,將這個對象的引用加入常量池,返回這個對象的引用。
(2) 當常量池中存在"abc"這個字符串的引用,返回這個對象的引用;
詳見:http://www.cnblogs.com/think-in-java/p/10418915.html
測試環境JDK1.8
常量池可以存放引用,也可以存放常量
String.intern()分析
判斷這個常量是否存在於常量池。
如果存在
判斷存在內容是引用還是常量,
如果是引用,
返回引用地址指向堆空間對象,
如果是常量,
直接返回常量池常量
如果不存在,
將當前對象引用復制到常量池,並且返回的是當前對象的引用
String a1 = "AA"; System.out.println(a1 == a1.intern()); //true String a2 = new String("B") + new String("B"); a2.intern(); String a3 = new String("B") + new String("B"); System.out.println(a2 == a3.intern());//true System.out.println(a3 == a3.intern());//false String a4 = new String("C") + new String("C"); System.out.println(a4 == a4.intern()); //true
三.總結
1.只在常量池上創建常量
String a1 = "AA";
2.只在堆上創建對象
String a2 = new String("A") + new String("A");
3.在堆上創建對象,在常量池上創建常量
String a4 = new String("A") + new String("A");//只在堆上創建對象AA a4.intern();//將該對象AA的引用保存到常量池上
5.在堆上創建對象,在常量池上創建引用, 在常量池上創建常量(不可能)
String a5 = new String("A") + new String("A");//只在堆上創建對象 a5.intern();//在常量池上創建引用 String a6 = "AA";//此時不會再在常量池上創建常量AA,而是將a5的引用返回給a6 System.out.println(a5 == a6); //true
6.
四.練習
String aa = "AA";//設置常量AA到常量池 String bb = "BB";//設置常量BB到常量池 String ccdd = "CC"+"DD";//設置常量CCDD到常量池 String neeff = new String("EE")+new String("FF");//設置EE和FF到常量池。並且添加EE、FF和EEFF對象到堆 String aabb = aa+bb;//添加AABB對象到堆 String gghh = "GG"+new String("HH");//設置GG和HH常量到常量池,設置HH和GGHH對象到堆 // aa.intern();//啥事都不做,返回AA常量 // ccdd.intern();//啥事都不做,返回CCDD常量 // neeff.intern();//添加EEFF對象的引用到常量池,並返回EEFF對象 // aabb.intern();//添加AABB對象的引用到常量池,並返回AABB對象 // gghh.intern();//添加GGHH對象的引用到常量池,並返回GGHH對象 System.out.println(aa.intern()==aa); //true System.out.println(neeff.intern()=="EEFF");//true System.out.println("EEFF"==neeff);//true String nccdd = new String("CCDD"); System.out.println(ccdd==nccdd);//false System.out.println(ccdd==nccdd.intern());//true System.out.println(aabb.intern()==aabb);//true System.out.println(gghh==gghh.intern());//true