String:源码如下,由一个char的数组组成。
1、该对象是放在常量池的,
2、对象内容不可变(不是绝对,通过反射可以修改),所有的修改内容操作,,都是new一个新的String对象返回。
3、Sting s = "hello";字符串放到常量池,太多放不下的时候就GC那些没有引用的。因为等价于String s = new String("hello").intern();(这个说法待定!!!)
4、String s = new String("hello");不会放到常量池,同理:String s = "hello"+1;在源码中重新new了一个字符串,并没有涉及intern()操作,所以"hello"会放入但"hello1"不会放到常量池。(只有通过String s = "hello";或者调用intern(),才会放入常量池。)所以for循环拼接时,其实是在内存中不断开辟空间存储新的字符串,浪费时间和内存空间。
5、intern()方法,是将当前字符串放到常量池,如果常量池已存在相同字符串则返回已存在的字符串引用,如果不存在则将当前字符串拷贝一份放到常量池,并返回拷贝的字符串引用(注意:java6以及之前版本常量池是放在永久代的,java7之后常量池也在堆内存里)。
网上的总结:
1)不要在Java 6及以前的版本使用String.intern()将字符串放入常量池,因为这是的字符串常量池是存储在固定大小的内存区(PermGen)
2)Java 7和8中将字符串常量池存储在堆内存中。
3)Java 7和8中可以通过JVM参数-XX:StringTableSize来控制字符串常量池的大小。
4)-XX:StringTableSize的默认值在Java 7中是1009,在Java 8中大概是25~50K
5)在多线程情况下可以随意使用Stirng.intern()方法。8个写线程只比1个写线程的负载多了17%,1个写7个读线程只比一个线程的负载多了9%
6)字符串常量池不是线程隔离的
7)虽然Java 7+对String.intern()做了很多优化,但是它还是花费CPU资源。在简单的例子中没有调用String.intern()方法的程序比调用了的程序要快3.5倍。所以不需要对所有的字符串调用String.intern()方法,只需要对经常被使用的字符串(例如:省、市等)调用String.intern()方法加入常量池。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
private int hash; // Default to 0
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
public native String intern();
}
其中,在虚拟机规范所说的那样,JVM中的内存分为5个虚拟的区域:堆、方法区和持久代(非堆)、JVM栈、本地栈、PC寄存器
java8以后,持久代已经被彻底删除了,取代它的是另一个内存区域也被称为元空间。
元空间:
1、它是本地堆内存中的一部分
2、它可以通过-XX:MetaspaceSize和-XX:MaxMetaspaceSize来进行调整
3、当到达XX:MetaspaceSize所指定的阈值后会开始进行清理该区域
4、如果本地空间的内存用尽了会收到java.lang.OutOfMemoryError: Metadata space的错误信息。
5、和持久代相关的JVM参数-XX:PermSize及-XX:MaxPermSize将会被忽略掉。
char:包装类为Character