一、字符串池基础概念
-
延迟加载机制
- 常量池中的字符串仅是符号,首次使用时才会从运行时常量池中取出,并在字符串池中创建对象。
- 示例:
String s = "abc";会在首次访问时创建对象。
-
复用机制
- 字符串池会复用已存在的字符串对象,避免重复创建。
- 示例:
String s1 = "abc"; String s2 = "abc";→s1 == s2为true。
二、字符串拼接与编译期优化
-
变量拼接的底层实现
- 若为变量拼接(如
new String("a") + new String("b")),底层会调用StringBuilder,结果存储在堆中而非常量池。 - 示例:
- 若为变量拼接(如
String s1 = new String("a");
String s2 = new String("b");
String s3 = s1 + s2; // 实际等价于 new
StringBuilder().append(s1).append(s2).toString();
```
2. **编译期优化**
- 字面量拼接(如 `'a' + 'b'`)在编译期优化为 `"ab"`,直接放入常量池。
- 示例:`String s = "a" + "b";` → 编译后等价于 `String s = "ab";`。
---
## **三、intern() 方法详解**
1. **功能**
- 主动将堆中字符串对象加入字符串池。
- 若池中已存在,则返回池中对象;若不存在,则加入池并返回池中对象。
2. **JDK 版本差异**
- **JDK 1.6**:复制一份新对象到池中(拷贝值)。
- **JDK 1.7+**:复制堆中对象的引用到池中(不拷贝值)。
- 示例:
```java
String s1 = new String("hello").intern();
String s2 = "hello";
System.out.println(s1 == s2); // JDK 1.6: false; JDK 1.7+: true
```
---
## **四、字符串池的位置与垃圾回收**
1. **位置迁移**
- **JDK 1.6**:位于 **永久代(PermGen)**,GC频率低,易导致内存溢出(`OutOfMemoryError: PermGen space`)。
- **JDK 1.7+**:迁移到 **堆(Heap)**,纳入常规GC管理,回收效率提升。
2. **垃圾回收机制**
- 废弃常量判定:若字符串池中无任何引用指向该字符串,则会被GC回收。
- 示例:动态生成大量 `new String(...).intern()` 但未持续引用,最终会被回收。
---
## **五、字符串池底层实现**
1. **数据结构**
- 类似 `Hashtable`(数组 + 链表),通过哈希码快速定位。
- 桶(数组元素)存储键值对(字符串常量)。
2. **性能调优**
- **哈希碰撞优化**:增大 `StringTableSize` 可减少碰撞,提升查找效率。
- **参数设置**:`-XX:StringTableSize=<size>`(默认值通常为 60013)。
- **减少重复对象**:通过 `intern()` 复用堆中对象,降低内存占用。
---
## **六、直接内存与ByteBuffer性能**
1. **直接内存(Direct Memory)**
- 属于操作系统内存,不受JVM管理,常见于NIO的 `ByteBuffer`。
- **优点**:读写性能高,避免用户态与内核态切换。
- **缺点**:分配回收成本高,存在内存溢出风险。
2. **传统IO vs NIO**
- **传统IO**:需将数据从内核态缓冲区复制到Java堆缓冲区,性能低。
- **NIO**:直接操作内核态缓冲区(直接内存),减少复制开销。
3. **内存泄漏问题**
- **虚引用机制**:`ByteBuffer` 通过虚引用关联直接内存,GC回收时触发 `unsafe.freeMemory()` 释放。
- **显式GC限制**:若启用 `-XX:+DisableExplicitGC`,`System.gc()` 无效,可能导致直接内存泄漏。
- **解决方案**:手动调用 `ByteBuffer.clear()` 或 `FileChannel.map()` 后显式释放。
---
## **七、总结与调优建议**
1. **优先使用字面量**:`"abc"` 优于 `new String("abc")`,减少堆对象创建。
2. **谨慎使用 `intern()`**:避免高频动态字符串入池导致池膨胀。
3. **监控直接内存**:通过 `jconsole` 或 `VisualVM` 监控直接内存使用,规避溢出风险。
4. **调优参数**:根据应用需求调整 `-XX:StringTableSize` 和 `-XX:MaxDirectMemorySize`。
---
**注**:JDK 9+ 引入 `Metaspace` 替代永久代,字符串池仍位于堆中,但元数据(如类名)迁移至 Metaspace。