美文网首页
深入理解Java虚拟机阅读笔记-02Java内存区域

深入理解Java虚拟机阅读笔记-02Java内存区域

作者: 惊天动地猪儿虫 | 来源:发表于2020-12-08 00:12 被阅读0次

    JVM在执行时会将所管理的内存划分为多个不同的区域。

    1 JDK7为例

    虚拟机主要由运行时数据区、执行引擎、类加载器三者构成。


    Java虚拟机运行时数据区

    1.1 程序计数器

    \color{#FF3030}{线程私有,不会产生OOM异常}。一块较小的内存空间,是当前线程所指向的字节码的行号指示器。任何一个时间,一个处理器(即多核处理器中的一个内核)只会执行一个线程中的指令,因此每个线程都需要有一个独立的程序计数器。

    1.2 Java虚拟机栈

    \color{#FF3030}{线程私有,会产生OOM异常和StackOverflowError异常}。生命周期与线程相同,其描述的是Java方法执行的内存模型。
    栈帧:方法执行时创建, 用于存储局部变量表,操作数栈,动态链表,方法出口等信息。

    1.3 本地方法栈

    \color{#FF3030}{线程私有,会产生OOM异常和StackOverflowError异常}。与Java虚拟栈类似,不同在于本地方法栈用于执行Native方法,Java虚拟机栈执行Java方法。

    1.4 Java堆

    \color{#FF3030}{线程共享,会产生OOM异常}。此内存区域的唯一目的就是存放对象实例,几乎所有对象实例都在这里分配。同时,这块内存也是垃圾收集器管理的主要区域。
    根据Java虚拟机规定,Java堆可以处于物理上不连续的内存空间,只要逻辑上是连续的即可。
    通过参数 -Xmx -Xms控制堆大小。

    1.5 方法区

    \color{#FF3030}{线程共享,会产生OOM异常}。用于存储被虚拟机加载的类信息(类的名称、方法信息、字段信息),常量,静态变量,即时编译器编译后的代码等数据。方法区可以不是连续的内存,并且可以不选择垃圾收集。
    通过参数-XX:MaxPermSize控制方法区上限。

    1.6 运行时常量池

    这里在介绍运行时常量池的同时,简单的把Java中几种常量池都大概说明下:

    1. 字符串常量池(String Constant Pool)

      • 字符串常量池在Java内存区域的哪个位置?
        在JDK6.0及之前版本,字符串常量池是放在Perm Gen区(也就是方法区)中;
        在JDK7.0版本,字符串常量池被移到了堆中了。至于为什么移到堆内,大概是由于方法区的内存空间太小了。
        JDK8以后也还是放在了Heap空间中,并没有移到元空间。
      • 字符串常量池是什么?
        在HotSpot VM里实现的String pool功能的是一个StringTable类,它是一个Hash表,默认值大小长度是1009;这个StringTable在每个HotSpot VM的实例只有一份,被所有的类共享。字符串常量由一个一个字符组成,放在了StringTable上。
        在JDK6.0中,StringTable的长度是固定的,长度就是1009,因此如果放入String Pool中的String非常多,就会造成hash冲突,导致链表过长,当调用String#intern()时会需要到链表上一个一个找,从而导致性能大幅度下降;
        在JDK7.0中,StringTable的长度可以通过参数指定:-XX:StringTableSize=66666
      • 字符串常量池里放的是什么?
        在JDK6.0及之前版本中,String Pool里放的都是字符串常量;
        在JDK7.0中,由于String#intern()发生了改变,因此String Pool中也可以存放放于堆内的字符串对象的引用。
        需要说明的是:字符串常量池中的字符串只存在一份!
    2. Class常量池(Class Constant Pool)

      • 也可以成为静态常量池。
      • Java类被编译后,就会形成一份class文件;class文件中除了包含类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池(constant pool table),用于存放编译器生成的各种字面量(Literal)和符号引用(Symbolic References);每个class文件都有一个class常量池。


        Class常量池
    3. 运行时常量池(Runtime Constant Pool)

      • 运行时常量池存在于内存中,也就是class常量池被加载到内存之后的版本,不同之处是:它的字面量可以动态的添加(String#intern()),符号引用可以被解析为直接引用。
      • JVM在执行某个类的时候,必须经过加载、连接、初始化,而连接又包括验证、准备、解析三个阶段。而当类加载到内存中后,JVM就会将class常量池中的内容存放到运行时常量池中,由此可知,运行时常量池也是每个类都有一个。在解析阶段,会把符号引用替换为直接引用,解析的过程会去查询字符串常量池,也就是我们上面所说的StringTable,以保证运行时常量池所引用的字符串与字符串常量池中是一致的。
        参考文章:

    1.7 直接内存

    \color{#FF3030}{会产生OOM异常}。直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范定义中的内存区域。例如:NIO中就可以通过Native函数库直接分配堆外内存。

    2 HotSpot虚拟机

    相关文章

      网友评论

          本文标题:深入理解Java虚拟机阅读笔记-02Java内存区域

          本文链接:https://www.haomeiwen.com/subject/bygnwktx.html