logo头像

Always believe youself.

JVM-2

方法区 内存

定义

  • 所有虚拟机线程的共享区域
    • 类结构 变量, 方法数据, 特殊方法 (类相关的信息)
    • 运行时常量池
  • 在虚拟机启动时被创建。逻辑上是 堆的 一部分。永久代(1.6) 是 jdk 1.8 之前的实现方法,之后是 移除到本地空地内存。
  • 也会导致内存溢出 的错误

组成

fuFbtg.md.png

方法区内存溢出问题

  • 设置元空间内存溢出 :-XX:MaxmetaspaceSize=8m
  • 1.6 溢出 是 永久代内存溢出错误, 1.8 以后是元空间不足内存溢出错误

场景: 动态的类加载技术

  • spring。代理 cglib 中会有涉及
  • mybatis

运行时常量池

  • 常量池:就是一张表,虚拟机指令根据这张表找到要执行的类名,方法名,参数类型等信息
  • 运行时常量池:常量池是 *.class 文件中的,当该类被加载,他的常量池信息就会被放入运行时常量池,并把这里面的符号地址变为真是的地址。

常量池,为 #2 #3 这些提供常量符号。

成员变量

fuZYRg.md.png

代码 编译成 二进制字节码 (类基本信息,常量池,类方法定义,包含了虚拟机指令)

虚拟机指令, 三条指令,每个指令去常量池找。然后指令进行正常的执行。

fuZPq1.md.png

方法区-StringTable

常量池中的信息,都会被加载到运行时常量池中,这时 a b ab 都是常量池中的符号, 还没有变为 java 的 字符串对象

等调用,先去stringTabe 中找,如果没有才会创建(用到才会创建)后会加载到 stringTable ,每个变量在 StringTable 是唯一的。 (hashtale的结构)

astore  4 把生成的结果放入到 4号变量
aload_1  取1号变量 从StringTable 取

String s4 = s1 + s2; // new StringBuilder().append(“a”).append(“b”).toString() 相当于 新创建字符串变量 new String(“ab”); 也会存储到堆中。

fK0aKH.md.png

s3 != s4
s3 = s5

字符串字面量也是【延迟】成为对象的。

创建字符串时,(一行一行创建到串池)。串池 如果已经有了就不会在进行创建。

特性

  • 常量池中的字符串仅是符号,第一次用到时才变为对象
  • 利用串池的机制,来避免重复创建字符串对象,串池的字符串只有一份
  • 字符串变量拼接的原理是 StringBuilder(1.8)
  • 字符串常量拼接的原理是编译期优化
  • 可以使用 intern 方法,主动将串池中还没有得字符串对象放入串池。

jdk1.8:

动态拼接的新的字符串 StringBuilder 是放在 堆中, 不再串池中。 串池中只放了常量的字符串值。动态拼接的字符串也是可以用 s.intern() 方法 尝试放入串池中。如有则不放入,无则放入。不管有无都会把串池的对象返回。 注:如果放入的是动态拼接的,且常量池已有(放入失败),则再次用 拼接的去 和 常量池有的对比时是不相等的。

jdk1.6:

将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则会把此对象复制一份,放入串池,会把串池的对象放回。注:如果放入的是动态拼接的,则再次用 拼接的去 和 常量池有的对比时是不相等的(核心是复制后放入常量池的)。

1.6 & 1.8 的区别,1.6 是 copy 的(源 字符串还是 堆,新赋值的是 串池的),1.8 是直接 intern

fKynBQ.md.png

StringTable 位置

  • 1.6 常量池的一部分
  • 1.8 移到了堆中

为什么迁移呢?(优化)

在永久代中,永久代的回收效率很低,需要 负gc 时才会对永久代进行垃圾回收 (老年代空间不足时才会触发),触发的时机有点晚。 但因为使用频率很高,所以会造成永久代的内存不足。

1.7 开始转移到堆,StringTable 垃圾回收触发简单,减轻了字符串对内存的占用。

可用报错间接的证明上面的 StringTable的 位置。

  • 1.6 时永久代空间不足内存溢出,OutOfMemoryError PermGen space
  • 1.8 堆空间不足内存溢出,OutOfMemoryError Java heap space。 如果不控制垃圾回收的开关会报这个错,OutOfMemoryError GC overhead limit exceeded。 (垃圾回收的规则,也是用虚拟机的参数控制的,-XX+UseGCOverheadLimit + 开 - 关)

fKgMAH.md.png

StringTable 垃圾回收

会发生垃圾回收,

StringTable 的性能调优的

  • 桶个数

    底层是hash 表。数组 + 链表, 用 桶 进行存储, 若 桶的个数比较多,hash 碰撞的几率就减小,查找速度快,反之,链表 长。

    优化点,主要优化hash 表 桶的个数。 (-XX:StringTableSize=200000) 默认的桶个数 60013。

    如果 stringTable 的 桶 少,每次在放入的时候都要去查一下是否存在,所以如果桶数少了,查的慢,则插入会很慢。

    如果开发系统中字符常量比较多时,建议可以把 StringTable 的size 设置大一些,会有好的hash 分布,减少hash 冲突,会有一个明显的性能提升。

  • 为什么要用StringTable ?

举例:大量客户地址信息很多重复的 ,可以用 intern 处理后 加入到常量池字符串在内存中去重。

  • 考虑将字符串对象入池 和 不入池 前后对占用内存的对比,对比很明显,加入StringTable 后 占用内存小很多。