美文网首页
JVM分区-运行时数据区

JVM分区-运行时数据区

作者: bit_拳倾天下 | 来源:发表于2022-04-23 18:22 被阅读0次

    前提:基于 java8 的 HotSpot

    1. 程序计数器/PC 寄存器(Program Counter Register)

    相当于程序执行的标记,字节码计时器就是通过计数器的值来决定下一条需要执行的命令,它控制着分支循环跳转异常处理线程恢复等。
    另外,由于 cpu 会在多个线程之前切换,当 cpu 切回原线程时,也要根据计数器的值来继续执行,不至于重新执行或迷失。

    程序计数器是线程独占的,每个线程都有各自的程序计数器。它的生命周期和线程相同,线程结束后会自动释放,而且程序计数器占用的内存较小,通常不会发生内存溢出,也不是垃圾回收关注的对象。

    如果线程在执行一个 java 方法,程序计数器的值就是正在执行的虚拟机字节码指令的地址;如果线程在执行一个本地方法,这个计数器值则为空(undefined)。

    2. 本地方法栈

    与 java 虚拟机栈类似,只不过它是针对本地方法,java 虚拟机栈针对的是 java 方法(也就是字节码)

    *3. java虚拟机栈

    通常所说的栈就是指这个区域,它也是线程独占,生命周期与线程相同。就是用来执行方法的。

    虚拟机栈描述了 java 方法执行的线程内存模型:
    每个方法被执行的时候,虚拟机都会创建一个栈帧(Stack Frame),用于存局部变量表动态链接方法出口操作数栈,和一些附加信息信息,每个方法被调用直到执行完毕,就对应着一个栈帧的入栈、出栈。

    • 局部变量表 存放了编译期间可知的八种基本数据类型对象引用(reference类型,指向对象真实地址),returnAddress类型
      局部变量表,通常是栈帧中最大的一部分,不仅影响栈的最大占帧数,而且由于它属于 GC Roots,所以也影响着 GC,是性能调优的重点关注对象。
    • 操作数栈 用于存储计算过程中的中间结果,也作为计算过程中变量的临时存储
    • 动态链接 用于将符号引用转换为对方法的直接引用
    • 方法出口 就是指向程序计数器的值,也就是方法执行完成后的出口或者说下一条指令
    异常

    *4. java堆

    这是 jvm 中最大的一块区域,是线程共享的区域,随着虚拟机启动而创建。它是用来存放对象实例的。
    Java 堆主要分为2个区域-年轻代与老年代,年轻代包括 Eden 区和 Survivor 区,Survivor 区又分 From 区和 To 区。如下图:

    堆内存空间及默认比例

    堆区为什要分代?都放一起不好么?
    根据工业统计,百分之九十的对象都是朝生夕死,只有较少的一部分对象需要长久保存,因此,把需要长久保存的对象放在一块(回收频率低),把刚创建的对象放在另一块(回收频率),方便回收,不用每次 GC 都扫描全堆

    什么是 TLAB(Thread Local Allocation Buffer)?
    堆区是线程共享的,这给线程通信带来便利,但是却给线程安全增加了负担。为了保证线程安全,不得不加锁,这就使得运行速度降低。TLAB 正是解决这个问题的,JVM 为每个线程在 Eden区 开辟一个线程独占的缓存空间,线程会优先使用 TLBA,在 TLAB 满了之后再使用 Eden 的空间。线程安全一定程度上得到了保障,同时也提升了内存分配的吞吐量,这种分配方式也称之为快速分配策略

    TLAB内存分配

    *5. 方法区

    方法区也是线程共享区,它用于存储虚拟机已加载的类型信息常量静态变量即时编译器编译后的代码缓存等。

    我们经常能听到“永久代”,它只是方法区的一种实现,由于“永久代”需要设置上限,而这个上限有比较难估算,所以内存溢出比较常见。HotSpot 虚拟机在 java 8,已经用“元空间”(用本地内存实现)代替“永久代”,这样,方法区不用设置上限,理论上本地内存上限才是对它的限制。

    方法区的数据也并非是“永久”的,常量池的回收和类型的卸载会在这里进行。

    运行时常量池:

    它是方法区的一部分。Class 文件除了有类的版本信息、字段、方法、接口等描述信息外,还有一项信息——常量池表,用于存放编译器申城的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池。

    6. 直接内存

    *注意:这部分不属于运行时数据区

    随着 NIO 的加入,引入了一种基于通道和缓冲区的 I/O 方式,他可以使用 Native 函数库直接分配堆外内存,然后通过一个存储在 java 堆 的 DirectByteBuffer 对象操作这块内存。这样避免了 java 堆和 Native 堆来回复制数据,可显著提高性能。

    所以分配内存时,除了运行时数据区,还要留下冲粗的空间给直接内存,否则也会出现 OOM。

    相关文章

      网友评论

          本文标题:JVM分区-运行时数据区

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