方法区
所有Java线程共享的区,存储和类的结构相关的信息,成员变量,方法数据,成员方法和构造器方法的代码部分,类的构造器,运行时常量池
方法区在虚拟机启动时被创建,逻辑上是堆内存(不强制)的组成部分
方法区也会内存溢出
本地内存是操作系统内存
方法区内存溢出的现象:类加载的太多,云空间一般不会溢出,因为使用的是系统所给的内存,需要设置
元空间设置:溢出报错((Metaspace))
-XX:MaxMetaspaceSize=8m
永久代内存设置:溢出报错((PermGen space))
-XX:MaxPermSize=8m
场景
spring:运行期间使用cglib动态生成字节码,完成动态类加载,会使用大量动态加载类,使用不当会方法区内存
mybatis
运行时常量池
二进制字节码包含类基本信息,常量池,类方法定义,包含虚拟机指令
要看懂字节码,需要使用反编译即:javap -v Hello.class
StringTable常量池
常量池中的信息,都会被加载到运行时常量池中,这时a b ab 都是常量池中的符号,还没有变为java字符串对象
等到具体执行到引用常量的代码时,会将相应的常量池符号变为相应的字符串对象,并且会准备一个空间StringTable[],会将字符串对象当作key,去查找是否有相同的key,没有相应的key,则存入字符串对象,数据结构是hashTable,长度一开始固定并不能扩容,如果StringTable中有相应的字符串对象,则使用StringTable中的对象。
特点:创建字符串对象时懒惰的,没有运行相应代码就不会创建
javac在编译期间的优化,结果已经在编译期确定ab,因为是常量一定不会变化直接就进行拼接了
StringTable特性
1、常量池中的字符串仅是符号,第一次用到时才会变为对象
2、利用串池的机制,来避免重复创建字符串对象
3、字符串变量拼接的原理是StringBuilder(1.8)
4、字符串常量拼接的原理是编译期优化 “a”+“b”
5、可以使用intern方法,主动将串池中还没有的字符串对象放入串池
1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池,会把串池中的对象返回
1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份并放入串池,并返回串池中的对象
StringTable的位置
从1.7开始StringTable从永久代中转移到了堆中,因为永久代的内存回收效率很低,触发垃圾回收的时间有点晚,导致StringTable回收效率不高,而且字符串常量对象使用很频繁,代码中有大量的常量,占用大量内存,导致永久代内存不足,从1.7放入堆中,触发GC的时间早,所以效率相对较高
永久代内存空间设置:-XX:MaxPermSize=10m
StringTable垃圾回收
当StringTable的堆的内存空间满了的时候,就会触发垃圾回收
StringTable性能调优
为什么使用StringTable,因为存入相同的字符串耗费大量的内存,所以进行优化减少重复内容
1、StringTableSize(增加桶的个数),减少向链表加入数据时花费的时间消耗