Java运行时数据区详解

简介: Java虚拟机在执行java程序的过程中会把它所管理的内存划分为若干个不同的数据区域.根据 中的规定,将内存区域划分为 程序计数器(Program Counter Register),虚拟机栈(VM Stack),本地方法栈(Native Method Stack),方法区(Method Area)和堆(Heap)五大区域.

Java虚拟机在执行java程序的过程中会把它所管理的内存划分为若干个不同的数据区域.根据 <<java虚拟机规范>> 中的规定,将内存区域划分为 程序计数器(Program Counter Register),虚拟机栈(VM Stack),本地方法栈(Native Method Stack),方法区(Method Area)堆(Heap)五大区域.

img_ced0ec2ed7eb095d32e200dc97d2f987.png
运行时内存区域

程序计数器(Program Counter Register)

程序计数器是一块很小的内存区域,可以当成当前线程所执行的字节码的行号指示器.java解释器通过改变计数器值来选取下一条指令.分治,循环,跳转,异常处理,线程恢复等需要依赖计数器完成

特点:

  1. 每一个线程都有一个独立的程序计数器,互不影响.(线程私有)
  2. 线程执行Java方法,计数器记录的是正在执行的虚拟机字节码指令的地址.
  3. 线程执行Native方法,计数器则为空.
  4. 唯一一个没有定义 OutOfMemoryError的区域.

虚拟机栈(VM Stack)

虚拟机栈它的栈元素是一种叫做栈帧(Stack Frame)的结构.每一个栈帧都包括了 局部变量表(Local Variable Table),操作数栈(Operand Stack),动态链接(Dynamic Linking),方法返回地址(Return Address)和一些额外信息.
栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构,每一个方法从调用开始至执行完成的过程,都对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程.

img_57a659590f6f10d5a1a49d0e2ecef468.png
栈帧结构

特点:

  1. Java虚拟机栈是线程私有的,生命周期与线程一致
  2. 局部变量表所需的内存空间在编译期间确定,并完成分配.
  3. 在方法运行期间不会改变局部变量表的大小.
  4. 如果请求的栈深度大于虚拟机允许的深度,抛出StackOverflowError.
  5. 虚拟机栈扩展时无法申请足够的内存,抛出OutOfMemoryError

本地方法栈(Native Method Stack)

功能与虚拟机栈类似,java线程在调用本地方法时,该区用来存储本地方法的局部变量表,本地方法的操作数栈等等信息.区别在于虚拟机栈执行的是java方法,
本地方法栈执行的是native方法(c/c++方法).

java是高级编程语言,当对一些底层的如操作系统或某些硬件交换信息时,我们使用java来编程实现起来不容易,再者使用java来编程效率也很低下.这时候就可以通过 JNI方式来调用 native方法来实现.

img_8e3c36856090e5e15c850ece31464fe3.png
本地方法栈与java栈

如果,展示了java与native方法交互的过程,java方法中调用了C语言方法,产生在本地方法栈中产生一个本地栈帧,这个C语言方法调用了另一个C语言方法,并且把结果回调回java方法中.

一个线程可能在整个生命周期中都执行Java方法,操作他的Java栈;或者他可能毫无障碍地在Java栈和本地方法栈之间跳转。

特点 :

  1. 线程私有,生命周期与线程一致
  2. 调用的是 c/c++方法(一般用于底层交互,或者性能优化)
  3. 可抛出StackOverflowErrorOutOfMemoryError

java堆(Heap)

java虚拟机中最大的内存区域,几乎所有类实例和数组的内存均从此处分配。

  1. 线程共享
  2. 在 Java 虚拟机启动时创建的
  3. GC管理的主要区域
  4. 可位于物理内存不连续的空间.
  5. 可以是固定大小的,也可以是可扩展的.
  6. 在没有内存空间并且无法扩展时,抛出OutOfMemoryError

hotspot中的实现

在hotspot虚拟机中,从内存回收的角度来看是采用 分代收集策略.将堆划分为两个不同的区域:
新生代(Young Gen)老年代(Old Gen).

堆的空间大小 = 新生代 + 老年代; 默认情况下,新生代和老年代的比例是 1:2;

新生代又被划分为Eden,From SurvivorTo Survivor三个区域;大小比例为 8:1:1
由于新生代采用复制算法来管理空间,因此,无论什么时候,总是有一块 Survivor 区域是空闲着的。
新生代实际可用的内存空间为90%的新生代空间。

方法区(Mthod Area)

方法区中,存储着已加载的类信息,常量,静态变量,即时编译后的代码等数据.
其中类相关的信息,如类名,访问修饰符,常量池,字段描述,方法描述等.
方法区逻辑上属于堆的一部分,但是为了与堆进行区分,通常又叫“非堆”。
方法区的数据是线程共享的.

为何叫方法区? 方法区中除了包括“已加载的类的基本信息、常量、静态变量等”外,还包括编译器编译后的代码,而且这应该是方法区中主要的一部分,这可能是为何把这部分内存成为方法区的原因.

注释 : 类的对象和实例对象存放在 java堆中, 类的元数据存放在 方法区中.

不同jdk(hotspot)版本,方法区数据的变化

JDK 1.6以及之前,方法区的实现为 永久代(Permanent Gen)的方式,目的是为了垃圾收集器能像管理java堆一样管理这部分内存.垃圾回收目标是针对常量池的回收和对类型的卸载.

JDK 1.7中,存储在永久代的部分数据就已经转移到Java Heap或者Native Heap。但永久代仍存在于JDK 1.7中,并没有完全移除,如符号引用(Symbols)转移到了native heap;字面量(interned strings)转移到了Java heap;类的静态变量(class statics)存放于定义类型的class对象中,存放在Java heap中.

JDK 1.8中, 完全移除了永久代,取而代之的实现方式成为元空间(Metaspace),将类元数据放到本地内存中,将字符常量池和静态变量放到Java堆里。虚拟机会为类的元数据明确分配和释放本地内存。

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间的最大区别在于:元空间并不在虚拟机中,而是使用本地内存。

Native memory:本地内存,也称为C-Heap,是供JVM自身进程使用的。当Java Heap空间不足时会触发GC,但Native memory空间不够却不会触发GC。即GC不管理元空间(Metaspace)的内存.

为什么移除永久代?

  1. 字符串存在永久代中,容易出现性能问题和内存溢出。
  2. 永久代大小不容易确定,PermSize指定太小容易造成永久代OOM
  3. 永久代会为 GC 带来不必要的复杂度,并且回收效率偏低。
  4. Oracle 可能会将HotSpot 与 JRockit 合二为一。

参考

目录
相关文章
|
4天前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
45 14
|
8天前
|
前端开发 JavaScript Java
Java打包jar运行时分离lib和jar
在`pom.xml`的`build`节点中,设置`packaging`为`jar`,并配置插件分离依赖库到`lib`目录和资源文件到`resources`目录。这样可以在运行时通过`-Dloader.path=lib,resources`加载外部依赖和资源文件,便于独立升级依赖库和修改资源文件,而无需重新打包程序。具体插件包括`maven-dependency-plugin`、`maven-resources-plugin`和`spring-boot-maven-plugin`等。
45 1
|
4月前
|
IDE Java 编译器
Java:如何确定编译和运行时类路径是否一致
类路径(Classpath)是JVM用于查找类文件的路径列表,对编译和运行Java程序至关重要。编译时通过`javac -classpath`指定,运行时通过`java -classpath`指定。IDE如Eclipse和IntelliJ IDEA也提供界面管理类路径。确保编译和运行时类路径一致,特别是外部库和项目内部类的路径设置。
336 5
|
4月前
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
66 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
|
4月前
|
分布式计算 大数据 Java
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
75 1
大数据-86 Spark 集群 WordCount 用 Scala & Java 调用Spark 编译并打包上传运行 梦开始的地方
|
4月前
|
Java
jvm复习,深入理解java虚拟机一:运行时数据区域
这篇文章深入探讨了Java虚拟机的运行时数据区域,包括程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区、元空间和运行时常量池,并讨论了它们的作用、特点以及与垃圾回收的关系。
84 19
jvm复习,深入理解java虚拟机一:运行时数据区域
|
4月前
|
Java Maven Spring
用Spring导致的无法运行Java文件的问题的解决方案
本文提供了解决在IntelliJ IDEA社区版中使用Spring Initializr插件创建Spring项目后,Java文件无法运行的问题的方法,主要是通过加载Maven项目来解决。
109 0
|
4月前
|
Java
Java关键字 —— super 与 this 详细解释!一看就懂 有代码实例运行!
本文介绍了Java中this和super关键字的用法,包括在构造方法中使用this来区分参数和成员变量、使用super调用父类构造方法和方法,以及它们在同一个方法中同时使用的场景。
229 0
Java关键字 —— super 与 this 详细解释!一看就懂 有代码实例运行!
|
4月前
|
Java
Java关键字 —— static 与 final 详细解释!一看就懂 有代码实例运行!
这篇文章详细解释了Java中static和final关键字的用法,包括它们修饰类、方法、变量和代码块时的行为,并通过代码示例展示了它们的具体应用。
339 0
Java关键字 —— static 与 final 详细解释!一看就懂 有代码实例运行!

热门文章

最新文章