string机制3

1.intern()定义

public native String intern(); 这是一个本地方法。在调用这个方法时,JAVA虚拟机首先检查字符串池中是否已经存在与该对象值相等对象存在,如果有则返回字符串池中对象的引用;如果没有,则先在字符串池中创建一个相同值的String对象,然后再将它的引用返回。

换一种解释:存在于.class文件中的常量池,在运行期被JVM装载,并且可以扩充。String的intern()方法就是扩充常量池的一个方法:当一个String实例str调用intern()方法时,Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用。换即当调用intern方法时,如果池已经包含一个等于此String对象的字符串(该对象由equals(Object)方法确定),则返回池中的字符串,否则,将此String对象添加到池中,并且返回此String。

2.intern()应用

我们说String str1 = "abc" 和 String str2 = new String("def")都分别在pool中创建了"abc"和"def"对象,String.intern()还能有什么用(pool已经存在与该对象值相等对象,还判断什么)。但我们错了,定义String对象就上面两种方法,但获得String对象的方式却很多:rs.getString(1)、new String("abc")+def、10+"abc"等等这些获得String的方法都没有在loop中创建String对象(而只是在heap中创建了对象)。这样intern()就有了用武之地。
举个例子:

sql脚本为

  1. DROP TABLE IF EXISTS temp;
  2. CREATE TABLE temp (
  3.     id INT(32) AUTO_INCREMENT NOT NULL,
  4.     content VARCHAR(200) NOT NULL,
  5.     PRIMARY KEY (id)
  6. );
  7. INSERT INTO temp (content) VALUES 
  8.     ('forrest test the performance of intern() method of String!! --> 1'),
  9.     ('forrest test the performance of intern() method of String!! --> 1'),
  10.     ... #插入10000个相同的数据
  11.     ('forrest test the performance of intern() method of String!! --> 1');

测试代码为

  1. public static void main(String[] args) {
  2.     Connection conn = null;
  3.     PreparedStatement pstmt = null;
  4.     ResultSet rs = null;
  5.     List<String> cList = new ArrayList<String>();
  6.     String sql = "SELECT content FROM temp";
  7.     try {
  8.         Class.forName("com.mysql.jdbc.Driver");
  9.         conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testcasetrack""root""");
  10.         pstmt = conn.prepareStatement(sql);
  11.         rs = pstmt.executeQuery();
  12.         while (rs.next()) {
  13.             String contentBean = new String();
  14.          contentBean = rs.getString(1);
  15.             cList.add(contentBean);
  16.         }
  17.     } catch (SQLException e) {
  18.         e.printStackTrace();
  19.     } catch (ClassNotFoundException e) {
  20.         e.printStackTrace();
  21.     } finally {
  22.         try {
  23.             rs.close();
  24.             pstmt.close();
  25.             conn.close();
  26.         } catch (SQLException e) {
  27.             e.printStackTrace();
  28.         }
  29.         System.out.println(Runtime.getRuntime().totalMemory());
  30.         System.out.println(Runtime.getRuntime().freeMemory());
  31.     }
  32. }
  33. /*
  34.  * 测试结果
  35.  * Runtime.getRuntime().totalMemory() :5984256
  36.  * Runtime.getRuntime().freeMemory() :2441160 
  37.  * 
  38.  * */
  1. public static void main(String[] args) {
  2.     Connection conn = null;
  3.     PreparedStatement pstmt = null;
  4.     ResultSet rs = null;
  5.     List<String> cList = new ArrayList<String>();
  6.     String sql = "SELECT content FROM temp";
  7.     try {
  8.         Class.forName("com.mysql.jdbc.Driver");
  9.         conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testcasetrack""root""");
  10.         pstmt = conn.prepareStatement(sql);
  11.         rs = pstmt.executeQuery();
  12.         while (rs.next()) {
  13.             String contentBean = new String();
  14.          contentBean = rs.getString(1).intern();
  15.             cList.add(contentBean);
  16.         }
  17.     } catch (SQLException e) {
  18.         e.printStackTrace();
  19.     } catch (ClassNotFoundException e) {
  20.         e.printStackTrace();
  21.     } finally {
  22.         try {
  23.             rs.close();
  24.             pstmt.close();
  25.             conn.close();
  26.         } catch (SQLException e) {
  27.             e.printStackTrace();
  28.         }
  29.         System.out.println(Runtime.getRuntime().totalMemory());
  30.         System.out.println(Runtime.getRuntime().freeMemory());
  31.     }
  32. }
  33. /*
  34.  * 测试结果
  35.  * Runtime.getRuntime().totalMemory() :3411968
  36.  * Runtime.getRuntime().freeMemory() :1585368 
  37.  * 
  38.  * */
  1. static String get() {
  2.     return "forrest test the performance of intern() method of String!! --> 1";
  3. }
  4.     
  5. public static void main(String[] args) {
  6.     Connection conn = null;
  7.     PreparedStatement pstmt = null;
  8.     ResultSet rs = null;
  9.     List<String> cList = new ArrayList<String>();
  10.     String sql = "SELECT content FROM temp";
  11.     try {
  12.         Class.forName("com.mysql.jdbc.Driver");
  13.         conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/testcasetrack""root""");
  14.         pstmt = conn.prepareStatement(sql);
  15.         rs = pstmt.executeQuery();
  16.         while (rs.next()) {
  17.             String contentBean = new String();
  18.             contentBean = get();
  19.             cList.add(contentBean);
  20.         }
  21.     } catch (SQLException e) {
  22.         e.printStackTrace();
  23.     } catch (ClassNotFoundException e) {
  24.         e.printStackTrace();
  25.     } finally {
  26.         try {
  27.             rs.close();
  28.             pstmt.close();
  29.             conn.close();
  30.         } catch (SQLException e) {
  31.             e.printStackTrace();
  32.         }
  33.         System.out.println(Runtime.getRuntime().totalMemory());
  34.         System.out.println(Runtime.getRuntime().freeMemory());
  35.     }
  36. }
  37. /*
  38.  * 测试结果
  39.  * Runtime.getRuntime().totalMemory() :3428352
  40.  * Runtime.getRuntime().freeMemory() :1609760 
  41.  * 
  42.  * */

从以上代码的测试结果可以看出:如果db中有10000条相同数据的时候,使用intern()的话,系统会占用更少内存(使用intern()占用的内存/没有使用intern()占用的内存 = 57%);而如果直接利用get()方法将pool中的String对象赋值,系统占用的内存和使用intern()从db中取数据时占用的内存几乎是相同的。

当然,如果我们sql脚本是创建的10000个不同的数据,则上面三段代码测试的系统使用内存情况几乎是相同的(也做过测试。1000个测试数据是用一个循环产生的or利用excel的数字拖拽自增长的方式来产生),这也就印证了我们在前两篇博文中得出的结论。

最后我们再来说说String对象在JAVA虚拟机(JVM)中的存储,以及字符串池与堆(heap)和栈(stack)的关系。

栈(stack):主要保存基本类型(或者叫内置类型)(char、byte、short、int、long、float、double、boolean)和对象的引用,数据可以共享,速度仅次于寄存器(register),快于堆。

堆(heap):用于存储对象。

我们查看String类的源码就会发现,它有一个value属性,保存着String对象的值,类型是char[],这也正说明了字符串就是字符的序列。 当执行String a="abc";时,JAVA虚拟机会在栈中创建三个char型的值'a'、'b'和'c',然后在堆中创建一个String对象,它的值(value)是刚才在栈中创建的三个char型值组成的数组{'a','b','c'},最后这个新创建的String对象会被添加到字符串池中。如果我们接着执行 String b=new String("abc");代码,由于"abc"已经被创建并保存于字符串池中,因此JAVA虚拟机只会在堆中新创建一个String对象,但是它的值(value)是共享前一行代码执行时在栈中创建的三个char型值值'a'、'b'和'c'。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值