当前位置:   article > 正文

通俗讲解JVM(二)_jdk1.8元空间会不会内存溢出

jdk1.8元空间会不会内存溢出

1、java内存区域

1.1方法区

方法区与java堆类似,但方法区还有一个别名叫做Non-Heap(非堆),是各个线程共享的内存区域,用于存储已经被虚拟机加载的类信息、常量、变量、即时编译器编译后的代码等数据。

从图中我们不难发现,JDK1.7及以前串池是在方法区内的,通过永久代来实现的。JDK1.8以后,串池移入到了堆中,使用元空间来代替永久代。

JDK1.8之前是永久代内存溢出

JDK1.8之后是元空间内存溢出。

HotSpot虚拟机设计团队使用永久代的方法来实现了方法区,省去了专门为方法区编写内存管理的代码。方法区内存回收主要是常量池的回收以及对类型的卸载,但是这个区域回收的效果很一般,特别是类型的卸载,条件很苛刻,但是内存回收是必要的,否则可能会发生内存泄漏。

当方法区无法满足分配需求时,将会抛出OutOfMemoryErroe异常。

1.2运行时常量池

1.2.1区别

class常量池、运行时常量池、字符串常量池(串池)都是什么?

class常量池主要用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References);

  • 字面量包括:1.文本字符串 2.八种基本类型的值 3.被声明为final的常量等;
  • 符号引用包括:1.类和方法的全限定名 2.字段的名称和描述符 3.方法的名称和描述符。

运行时常量池是方法区的一部分,class文件中类的版本、字段、方法、接口等信息以及class常量池中的字面量以及符号引用等这部分内容将在类加载后进入方法区内的运行时常量池中存放。由此可知,每个类都有一个运行时常量池。

字符串常量池,在解析阶段,会把符号引用替换为直接引用,解析的过程会去查询字符串常量池,也就是我们所说的StringTable,以保证运行时常量池所引用的字符串与字符串常量池中是一致的。

1.2.2查看常量池

javap -v  HelloWorld.class

显示反编译类的详细信息,注意只有类运行只有生成字节码文件才可以查看到

 方法对应的虚拟机指令

getstatic 获取#2位置的静态成员变量

ldc 获取串表#3位置的参数

invokevirtual 执行一次虚方法调用

虚拟机中的串表(StringTable)

  • 运行时常量池就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息

  • 运行时常量池,常量池是*.class文件中的,当该类被加载,它的常量就会放入运行时常量池,并且将里面的符号地址变为真实地址

栗子:

ldc 去#2位置中获取参数

astore_1 将参数存到指定的 LocalVariableTable位置中

常量开始存在字节码文件中,运行时会被加载到运行时常量池中,但还未成为对象只是常量符号

只有当虚拟机指令执行到该符号时,才会把常量符号变为java对象,放入到串池中

StringTable是一个长度固定且不会扩容的哈希Table,每次生成对象都会去串池中寻找(用到时才回去加载),若没有则生成

  1. class st{
  2. public static void main(String[] args) {
  3. String s1 = "a";
  4. String s2 = "b";
  5. String s3 = "ab";
  6. String s4 = s1+s2;
  7. String s5 = s2;
  8. String s6 = "a"+"b";
  9. }
  10. }

s4!=s3, s4相当于调用了StringBuilder方法新创建的对象,是new出来的,放在堆中

 s3==s4,因为"a"、"b"都是定值,所以在编译期间就已经确定了,可以直接去常量池寻找

1.2.3.StringTabel特性

  • 常量池中的字符串仅是符号,第一次用到时才变成对象

  • 利用串池机制,避免重复创建字符串对象

  • 字符串变量拼接的原理是StringBuilder(1.8)

  • 字符串常量拼接的原理是编译期优化

  • 可以使用intern方法,主动将串池中还没有的字符串对象放入串池

    • jdk 1.8将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则会放入串池,会把串池中的对象返回

    • jdk 1.6将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则会把此对象复制一份放入串池,会把串池中的对象返回

总结:

jdk 1.8后的版本中,在堆中的对象调用intern方法,如果常量池中有这个值,那么返回的是常量池中的值,该对象还在堆中。如果常量池中没有这个值,则将该对象从堆中放入常量池,返回常量值。

jdk 1.8前的版本中,在堆中的对象调用intern方法,如果常量池中有这个值,那么返回的是常量池中的值,该对象还在堆中。如果常量池中没有这个值,则将该对象复制一份放入常量池,该对象还在堆中,返回常量值。

1.2.4. StringTable垃圾回收

当内存占用到一定数目时,GC会自动回收无用的字符串常量

1.2.5. StringTable性能调优

  • 调整-XX:StringTableSize = 桶个数

    StringTable底层原理用的是hashTable,通过提高桶(数组)的个数来减少hash碰撞,从而提高效率

  • 如果需要存储大量重复的字符串,可以将字符串放入到StringTable中,节约内存使用

之前跟着黑马的老师刷过一遍jvm,讲的真的非常棒,可以结合这PDF和课程一起刷

学习视频链接:

黑马程序员JVM完整教程,全网超高评价,全程干货不拖沓_哔哩哔哩_bilibili

深入理解JVM网盘链接
链接:https://pan.baidu.com/s/1iiMsS3vBWbLOxXstE1EpEw 
提取码:spoi

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/569759
推荐阅读
相关标签
  

闽ICP备14008679号