String字符串内存分析(OOM出现后返补学习)

本文详细解析了Java中字符串的创建方式及其在内存中的表现形式,特别是字符串常量池的作用机制。通过对比不同的字符串创建方法,揭示了如何避免因字符串拼接导致的内存溢出问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考自:https://blog.youkuaiyun.com/weixin_36328444/article/details/79734967

最近使用+拼接字符串 出现OOM异常,经过分析,应该是在常量池中创建了过多的字符串,GC没有及时回收导致OOM

解决办法:使用StringBuffer(线程安全)或者StringBuilder(线程不安全)


位于常量池中的常量也可以被GC,常量GC的条件和实例对象类似,当没有一个引用指向该常量时,此时若发生内存回收,则可能被GC,常量池中的类或接口、方法、字段的符号引用也是如此


字符串对象在创建的时候有两种方式:

String str1 = "abc";

String str2 = new String("abc");
  • 1
  • 2
  • 3

这两种方式都是我们经常用到的,尤其是第一种方式。不管是哪一种创建字符串对象的方式,最终我们在程序中展现出来的效果是一样的。但这两种创建方式有什么不同呢,下面我们来从内存分析角度说明一下。

这两种实现其实存在着一些性能和内存占用的差别。这一切主要是源于JVM为了减少字符串对象的重复创建,其维护了一个特殊的内存结构,这个内存被成为字符串常量池。

字符串是常量,我们可以从字符串底层实现来看,它使用了一个final的char型数组保存字符串的每个字符,那么final所修饰的数组引用是不能被改变的,而该char型数组中也未提供任何可供修改单个元素的方法,所以一旦确定字符串内容,也就不能再改变底层数组中保存的元素了。

当代码中出现以字面量形式(上述第一种方式)创建字符串对象时,JVM首先会对这个字面量进行检查,如果字符串常量池中存在相同内容的字符串对象的引用,则将这个引用返回,否则新的字符串对象被创建,然后将这个引用放入字符串常量池,并返回该引用。

如:

String str = "java"; // 1
String test = "java"; // 2
String demo = new String("java"); // 3
  • 1
  • 2
  • 3

当执行第1条语句时,JVM检测“java”这个字面量,这里我们认为没有内容为“java”的对象存在。JVM通过字符串常量池查找不到内容为“java”的字符串对象存在,那么就创建这个字符串对象,然后将刚创建的对象放入到字符串常量池中,并且将引用返回给变量str。

当执行第2条语句时,JVM还是要检测这个字面量,JVM通过查找字符串常量池,发现内容为“java”字符串对象存在,于是将已经存在的字符串对象的引用返回给变量test,这里不会重新创建新的字符串对象。

1

我们也可以通过

System.out.println(str == test);
  • 1

来验证str与test是否指向同一个对象,==在此判断的是变量str与test中存放的引用,而不是对象本身的值。

当执行第3条语句时,会在堆上分配一个存放字符串对象的空间,然后查找常量池中是否存在内容为“java”的字符串对象,如果存在则引用,不存在则创建。

2

实际上现在demo所保存的引用是在堆内存上的地址,我们可以通过

System.out.println(demo == test);
  • 1

来验证demo与test是否是指向同一个对象。

字符串的连接中对运算符“+”作了重载,即“+”用于字符串运算时是将字符串连接在一起。我们知道,字符串是常量,那么要做字符串连接,不可能在原有字符串对象的后边直接连接,那是如何来实现连接操作的呢?

在字符串连接时,如:

String test = "hello" + " java";
  • 1

首先会在常量内存区创建”hello”字符串与”java”字符串两个对象,然后再开辟一个空间存放连接后的”hello java”字符串,所以这就造成了在常量内存区中创建了三个对象的现象,最后将连接后的”hello java”字符串的引用赋给变量test。

那么,当我们需要频繁修改字符串时,如果使用String来操作,则明显会使执行效率受影响,如何解决这个问题呢,请参考StringBuffer的使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值