careful! Writing code with String may leak memory!

content

  • How are Strings stored in memory?
  • String.intern() method
  • How does String cause memory leak?
  • Summarize

Today I will tell you about the most common String code when we usually write code, some of its underlying principles, and the problem of memory leaks that may be caused by improper use. I believe it will be helpful for everyone to develop and write code in daily life. .

How are Strings stored in memory?

First of all, when we usually write a line of String type code in the code, do you know how this String string is stored in memory? For example, such a line of code: String username = "zhangsan", this "zhangsan" is actually a string of strings, in fact, it is stored in an array at the bottom, and the size of the array is strictly equal to the length of the string, It is immutable, as shown below.

Next, for strings in Java, there is a concept of constant pool , which means that for the same string content, it is often represented by the same array in memory, but not for the same characters. The content of the string creates different arrays to store, for example, the following two lines of code, let's take a look:

String username = "zhangsan"; 
String nickname = "zhangsan";
复制代码

The above two strings of username and nickname point to "zhangsan", which are actually stored in the same array at the bottom layer, as shown in the following figure.

So, it is precisely because the same string refers to the same underlying array , so if you use a judgment code similar to System.out.println(username == nickname), you will find that username == nickname returns true, because they both point to the same underlying array.

另外再给大家普及一个字符串的知识点,那就是如果我们用一个字符串创建一个String对象的话,那它在内存里一定是另外的一个对象了,如下代码所示,大家看看:

String username = "zhangsan"; 
String nickname = new String("zhangsan"); 
System.out.println(username == nickname);
复制代码

大家看上面代码,**此时username和nickname比较还是返回true吗?**那不可能的,此时一定是false,因为此时在内存里,username是指向一个数组的,但是nickname是指向一个String对象的,只不过这个String对象里面是有一个"zhangsan"字符串而已,如下图。

但是这个时候又给大家再次介绍一个知识点了,那就是这个String对象内部的"zhangsan"字符串,是怎么存储的呢?其实啊,这个String对象内部的"zhangsan"字符串还是引用了之前的那个数组的,如下图所示。

String.intern()方法

所以说,如果此时你用String.intern()方法,就会发现你可以拿到String对象里的"zhangsan"字符串,此时再用这个字符串做比较,还是返回的是true,大家看下面代码就懂了。

String username = "zhangsan"; 
String nickname = new String("zhangsan");
System.out.println(username == nickname.intern()); // 返回的是true
复制代码

String字符串是如何引发内存泄漏呢?

好,那么大家都理解了Java里字符串的基本原理后,我们就可以来给大家讲讲平时我们用字符串String写代码,一旦要是不注意,是如何引发内存泄漏问题的。这个问题主要是出现在Java 6以及之前的版本里,在这个较为旧的Java版本中,String.substring()这种字符串截取动作,是会导致内存泄漏的,什么意思呢,我们来看看。

在Java 6以前的版本中,当你调用String.substring()进行字符串截取的时候,它在底层的运作模式是这样的,它会把你的原字符串的数组直接拷贝一份过来,然后用一个offset指针和count标记,来表明截取后的字符串你是需要哪些,如下图所示。

可是在这种运作模式下就有一个问题了,就是你每次substring都会把原数组拷贝一份,可是对于你的子字符串来说仅仅是需要里面的一部分而已,而你缺把原字符串每次都拷贝一份,导致了子字符串中不需要的那部分拷贝内容都是浪费掉的,如下图红圈部分都是子字符串不需要的。

所以此时子字符串不需要的红圈部分处的内容还依然占据了内存,这属于什么问题呢?就是典型的内存泄漏了,也就是说,你要是大量的进行substring一类的操作,就可能会大量的拷贝字符串数组,然后很多拷贝后的字符串数组里,很多内容都是不需要用的,结果还占据了很多内存空间,这就叫做内存泄漏。

内存泄漏指的就是你很多内存空间被占用了,结果你又不用它,别人也没法用,就是典型的占着茅坑不拉屎的行为。

所以后来在Java 7版本开始就对String.substring()进行了源码重构,开始改造了这部分的实现,每次你执行String.substring,它是把原字符串数组中你需要的那部分拷贝过来就可以了,就避免了每次都重复的拷贝原字符串数组,如下图。

总结

虽然在Java 7以及往后的新版本中已彻底的解决了substring导致的内存泄漏问题了,但大家平时在用字符串做开发的过程中,也一定要小心谨慎,避免误用老版本的Java触发这种内存泄漏隐患。

当然现在一般都是用Java 8以上的版本,尤其较多的是用Java 9、Java 10甚至Java 11这几个新版本了,但是不排除有一些公司的非常老旧的系统在维护的时候,用的还是曾经很风靡的Java 6这个版本,大家在对这类老系统维护的时候,一定要谨慎注意substring内存泄漏问题

END

====惊喜====

Guess you like

Origin juejin.im/post/7083659693393969188