Java String intern()对性能的影响

225 阅读2分钟

Java String intern()。对性能的影响

java.lang.String#intern()是Java中一个有趣的函数。如果在正确的地方使用,它有可能通过消除应用程序中的重复字符串来减少应用程序的整体内存消耗。要了解intern()函数如何工作,你可以参考这篇博客。在这篇文章中,我们将讨论在你的应用程序中使用java.lang.String#intern()函数的性能影响。

intern()函数演示

为了研究intern()方法的性能行为,我们创建了这两个简单的程序:

public class InternDemo {

   private static List<String> datas = new ArrayList<>(10_000_000);
   
   public static void main(String args[]) throws Exception {
   
      BufferedReader reader = new BufferedReader(new FileReader("C:\\workspace\\random-data.txt"));
      String data = reader.readLine();
      while (data != null) {
         data = reader.readLine().intern();
         datas.add(data);
      }
      reader.close();   
   }
}
public class NoInternDemo {

   private static List<String> datas = new ArrayList<>(10_000_000);
   
   public static void main(String args[]) throws Exception {
   
      BufferedReader reader = new BufferedReader(new FileReader("C:\\workspace\\random-data.txt"));
      String data = reader.readLine();
      while (data != null) {
         data = reader.readLine();
         datas.add(data);
      }
      reader.close();   
   }
}

我要求你在进一步阅读之前,先查看上述源代码。这是个简单的程序。如果你注意到,"InternDemo"程序每次从 "随机数据.txt"中读取每一行,然后对读取的数据调用intern()操作。由intern()函数返回的字符串被添加到*'datas'ArrayList中。'NoInternDemo'程序也做了完全相同的事情,唯一的区别是'NoInternDemo'没有调用'intern()*'操作,而'InternDemo'调用了'intern()'操作。

你还需要了解'random-data.txt'的内容。这个文件包含1000万个UUID(Universally Unique Identifiers)字符串。尽管这个文件中有1000万个UUID字符串,但其中有大量的重复内容。基本上只有10个唯一的UUID,它们被插入到这个文件中1000万次。数据的结构是故意的,所以在这个文件中存在大量的重复字符串。你可以从这个位置下载我们在这个实验中使用的*"随机数据.txt "*文件。

内存影响

我们执行了这两个程序。在程序退出之前,我们从它们那里捕获了堆转储。*堆转储基本上是内存的一个快照,它包含了驻留在内存中的所有对象的信息。*我们通过HeapHero--一个堆转储分析工具来研究堆转储。下面是这个工具生成的实时报告。

下表总结了这两个程序之间的区别:

InternDemoNoInternDemo
总大小38.37MB1.08GB
对象数量4,18420,004,164
类计数456456

你可以注意到*'InternDemo'只有4千多个对象,只消耗了38.37MB,而'NoInternDemo'有2000多万个对象,消耗了1.08GB的内存。基本上'NoInternDemo'消耗的内存是'InternDemo'*的28倍 *。这个演示清楚地说明了内存优化是通过intern()*函数实现的。

重复字符串的影响

HeapHero工具在其报告中指出,由于低效的编程实践,有多少内存被浪费了。我们注意到,*"InternDemo "没有浪费任何内存,而"NoInternDemo "*由于低效的编程实践,浪费了1.04gb(即97%)的内存。在这97%的内存浪费中,96.5%的浪费是由于重复的字符串造成的。

图:InternDemo的内存浪费情况由 浪费的内存

该工具还指出,以下是内存中存在的重复字符串。基本上这10个字符串是随机数据txt文件中存在的10个独特的UUID。由于重复,每个字符串浪费了106MB的内存。如果使用*intern()*操作,那么这种内存的浪费是可以避免的。

响应时间的影响

我们执行了几次*"InternDemo "*和 "*NoInternDemo "*程序。下面的图表显示了这两个程序的平均响应时间。

InternDemoNoInternDemo
2042 ms1164毫秒

基本上'InternDemo''NoInternDemo'慢了75%。这只是因为*'InternDemo'*必须对String intern池中的所有对象调用string.equals()方法来处理这1000万条记录。因此,它消耗了更多的CPU和时间。因此'*InternDemo'*的响应时间要比'NoInternDemo'慢。难怪人们说:'*没有免费的午餐'。*从内存的角度来看,'*InternDemo'*的性能很高。从CPU/响应时间的角度来看,'*NoInternDemo'*的性能很高。

注意:intern()函数的性能影响是 在很大程度上依赖于数据你的应用程序的处理。在上面的例子中,有大量的重复字符串,因此你看到内存利用率大大降低,响应时间激增。这不一定是所有应用程序的相同行为。在你的应用程序中使用intern()函数之前,你必须进行适当的测试。