4.运行时数据区

目录

  • 概述
  • 虚拟机栈
    • 栈帧
    • 当前栈帧
    • 创建栈帧
    • 栈异常的两种情况
  • 本地方法栈
  • 方法区
    • 方法区存储
    • 永久代和元空间的区别
  • 结束

概述

整个 jvm 构成里,主要由三部分组成:类加载系统、运行时数据区、执行引擎。

4.运行时数据区_第1张图片

由上图总结如下。
按照线程使用情况和职责分成两大类:

  • 线程独享(程序执行区域)
    • 虚拟机栈、本地方法栈、程序计数器
    • 不需要垃圾回收
  • 线程共享(数据存储区域)
    • 堆和方法区
    • 存储类的静态数据和对象数据
    • 需要垃圾回收

java 堆在 jvm 启动时创建内存区域,去实现对象、数组与运行时常量的内存分配,它是虚拟机管理最大的,也是垃圾回收的主要内存区域。
4.运行时数据区_第2张图片

内存划分:
核心逻辑就是三大假说,基于程序运行情况进行不断的优化设计。

堆内存为什么会存在新生代和老年代
分代收集理论:当前商业虚拟机的垃圾收集器,大多数都遵循了 分代收集 (Generational Collection) 的理论进行设计,分代收集名为理论,实质是一套符合大多数程序运行实际情况的经验法则,它建立在两个分代假说之上:

  • 弱分代假说 (Weak Generational Hypothesis) :绝大多数对象都是很快会消亡的
  • 强分代假说 (Strong Generational Hypothesis) : 熬过越多次垃圾收集过程的对象就越难以消亡。

这两个分代假说是几个常用垃圾收集器的一致的设计原则 :收集器应将java堆划分出不同的区域,然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储。

这就同时 兼顾了垃圾收集的时间开销和内存的空间有效利用

虚拟机栈

4.运行时数据区_第3张图片

栈帧

栈帧(Stack Frame) 是用于支持虚拟机进行方法执行的数据结构。

栈帧存储了方法的 局部变量表操作数栈动态连接 和方法返回地址等信息。每一个方法从调用至执行完成的过程,都对应着一个栈帧在虚拟机里从入栈到出栈的过程。

栈内存为线程私有的空间,每个线程都会创建私有的栈内存,生命周期与线程相同,每个java方法在执行的时候都会创建一个栈帧(Stack Frame) 。栈内存大小决定了方法调用的深度,栈内存过小则会导致方法调用的深度过小,如递归调用的次数较少。

当前栈帧

一个线程中方法的调用链可能会很长,所以会有很栈帧。只有位于jvm虚拟机栈,栈顶的元素才是有效的,即称为当前栈帧,与这个栈帧相关连的方法称为当前方法,定义这个方法的类叫做当前类.

执行引擎运行的所有字节码指令都只针对当前栈帧 进行操作。如果当前方法调用了其方法,或者当前方法执行结束,那这个方法的栈帧就不再是当前栈帧了。

创建栈帧

什么时候创建栈帧?

调用新的方法时,新的栈帧也会随之创建。并且随着程序控制权转移到新方法时,新的栈帧成为了当前栈帧。

栈异常的两种情况

  • 如果线程请求的栈深度大于虚拟机所允许的深度(Xss 默认 1m),会抛出 StackOverflowError异常
  • 如果在创建新的线程时,没有足够的内存去创建对应的虚拟机栈,会抛出OutOfMemoryError导常(不一定能复现)

本地方法栈

本地方法栈虚拟机栈(自己编写的代码是在此运行)相似,区别就是虚拟机栈为虚拟机执行 java 服务(字节码服务),而本地方法栈为虚拟机使用到的 Native 方法(比如 C++) 服务。

简单来说,一个Native Method 就是一个 java 调用非 java 代码的接口。

方法区

方法区(Method Area) 是可供各个线程共享的运行时内存区域,方法区本质上是 java 语言编译后代码存储区域,它存储每一个类的结构信息,如:运行时常量池、成员变量、方法数据、构造方法和普通方法的字节码指令等内容。很多语言都有类似区域。

方法区的具体实现有两种:永久代(PermGen)元空间(Metaspace)

方法区存储

存储了以下数据

Class1 Class2
1.类型信息 1.类型信息
2.类型的常量池 2.类型的常量池
3.字段信息 3.字段信息
4.方法信息 4.方法信息
5.类变量 5.类变量
6.指向类加载器的引用 6.指向类加载器的引用
7.指向Class实例的引用 7.指向Class实例的引用
8.方法表 8.方法表
运行时常量池(所有Class共用,此为 Class1 与 Class2)

总结如下:
主要有如下三种类型。

  • 第一:Class
    • 类型信息,比如Class(idea中定义的类)
    • 方法信息,比如Method(方法名称、方法参数列表、方法返回值信息)
    • 字段信息,比如Field(字段类型、字段名称需要特殊设置才能保存的住)
    • 类变量(静态变量):jdk1.7这后,转移到堆中存储(按jvm规范是在方法区,实现不一定)
    • 方法表(方法调用的时候) 在A类的main方法中去调用B类的method方法时,是根据B类的方法表去查找合适的方法,进行调用。
  • 第二:运行时常量池(字符串常量池):从class中的常量池加载而来,jdk1.7之后,转移到堆中存储
    • 字面量类型
    • 引用类型 --> 内存地址
  • 第三:JIT编译器编译之后的代码缓存

如果需要访问方法区中类的其它信息,都必须先获得Class对象,才能获取该Class对象关联的方法信息或者字段信息。

永久代和元空间的区别

  • jdk1.8之前使用的方法区实现是永久代,jdk1.8及以后使用的方法区实现是元空间
  • 存储位置不同
    • 永久代所使用的内存区域是jvm进程所使用的区域,它的大小受整个jvm的大小所限制。
    • 元空间所使用的内存区域是物理内存区域;那么元空间的使用大小只会受物理内存大小的限制。
  • 存储内容不同
    • 永久代存储的信息基本上就是上面方法区存储内容中的数据。
    • 元空间只存储类的元信息,而静态变量和运行时常量池都挪到堆中

结束

至此,运行时数据区就结束了,如有疑问,欢迎评论区留言。

你可能感兴趣的:(jvm,jvm,运行时数据区,虚拟机栈,本地方法栈,方法区)