JVM运行时数据区域

简介: 线程私有唯一一个没有规定 OutOfMemoryError 异常 的区域它可以看作是当前线程所执行的字节码的行号指示器如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是本地(Native)方法,这个计数器值则应为空(Undefined)

网络异常,图片无法展示
|

网络异常,图片无法展示
|

程序计数器

  • 线程私有
  • 唯一一个没有规定 OutOfMemoryError 异常 的区域
  • 它可以看作是当前线程所执行的字节码的行号指示器
  • 如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是本地(Native)方法,这个计数器值则应为空(Undefined)

(摘自网上)我们想象下,CPU是怎么知道记住之前A线程,执行到哪一处的?

答案是,CPU根本就不会记住之前执行到哪里了,它只是埋头苦干;那是什么保证了切换线程的程序可以正常执行的;答案是 : 程序计数器 ;程序计数器里面保存的是 当前线程执行的字节码的行号(看着像行号,其实是指令地址);

那么,我们需要几个程序计数器呢?如果,我们只有一个的话,切换B线程以后,程序计数器里面保存的就是B线程所执行的字节码的行号了,再切换回A线程,就蒙圈了,不知道执行到哪里了,因为,程序计数器里面保存的是B线程当前执行的字节码地址 ;因此,我们可以想象出,要为每个线程都分配一个程序计数器,因此,程序计数器的内存空间是线程私有的 ;这样即使线程 A 被挂起,但是线程 A 里面的程序计数器,记住了A线程当前执行到的字节码的指令地址了 ,等再次切回到A线程的时候,看一下程序计数器,就知道之前执行到哪里了!

那么程序计数器,什么时候分配内存呢?我们试想下,一个线程在执行的任何期间,都会失去CPU执行权,因此,我们要从一个线程被创建开始执行,就要无时无刻的记录着该线程当前执行到哪里了!因此,线程计数器,必须是线程被创建开始执行的时候,就要一同被创建

程序计数器,保存的是当前执行的字节码的偏移地址(也就是之前说的行号,其实那不是行号,是指令的偏移地址,只是为了好理解,才说是行号的,),当执行到下一条指令的时候,改变的只是程序计数器中保存的地址,并不需要申请新的内存来保存新的指令地址;因此,永远都不可能内存溢出的;因此,jvm虚拟机规范,也就没有规定,也是唯一一个没有规定 OutOfMemoryError 异常 的区域;

当线程执行的是本地方法的时候,程序计数器中保存的值是空(undefined);原因很简单:本地方法是C++/C 写的,由系统调用,根本不会产生字节码文件,因此,程序计数器也就不会做任何记录

Java虚拟机栈

  • 线程私有
  • 如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常
  • 如果Java虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够的内存会抛出OutOfMemoryError异常;(HotSpot虚拟机的栈容量是不可以动态扩展的,以前的Classic虚拟机倒是可以。所以在HotSpot虚拟机上是不会由于虚拟机栈无法扩展而导致OutOfMemoryError异常——只要线程申请栈空间成功了就不会有OOM,但是如果申请时就失败,仍然是会出现OOM异常的)
  • -Xss5m: 设置5m的栈容量
  • 每个方法执行都会创建一个栈帧,栈帧包含局部变量表、操作数栈、动态连接、方法出口等

本地方法栈

  • 线程私有
  • 与Java虚拟机栈相似
  • 与Java虚拟机栈区别: Java虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务。
  • Hot-Spot虚拟机直接就把本地方法栈和虚拟机栈合二为一
  • 与虚拟机栈一样,本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出StackOverflowError和OutOfMemoryError异常

Java堆

  • 线程共享
  • 所有的Java对象实例不一定都在Java堆上分配内存
  • Java堆既可以被实现成固定大小的,也可以是可扩展的,不过当前主流的Java虚拟机都是按照可扩展来实现的(通过参数-Xmx-Xms设定)。
  • 如果在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,Java虚拟机将会抛出OutOfMemoryError异常
  • Java堆是垃圾收集器(Garbage Collected)管理的内存区域

方法区

  • 线程共享
  • 用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
  • 虽然《Java虚拟机规范》中把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫作**“非堆”(Non-Heap),目的是与Java堆区分开来**。
  • 在JDK1.6及之前,使用永久代来实现方法区.
  • -XX:MaxPermSize 设置永久代内存上限
  • -XX:PermSize 设置永久代内存
  • JDK1.7把字符串常量池类的静态变量(class statics)转移到了java heap,但是永久代还是存在,主要放一些类信息(运算时常量池)等.
  • JDK1.8彻底移除永久代,方法区采用本地内存中实现的元空间(Meta-space)来代替,将JDK1.7中永久代的信息移到了元空间,像字符串常量池静态变量还是存在Java Heap中
  • 如果方法区无法满足新的内存分配需求时,将抛出OutOfMemoryError异常
目录
相关文章
|
1月前
|
存储 Java 程序员
JVM自动内存管理之运行时内存区
这篇文章详细解释了JVM运行时数据区的各个组成部分及其作用,有助于理解Java程序运行时的内存布局和管理机制。
JVM自动内存管理之运行时内存区
|
26天前
|
存储 算法 Java
JVM组成结构详解:类加载、运行时数据区、执行引擎与垃圾收集器的协同工作
【8月更文挑战第25天】Java虚拟机(JVM)是Java平台的核心,它使Java程序能在任何支持JVM的平台上运行。JVM包含复杂的结构,如类加载子系统、运行时数据区、执行引擎、本地库接口和垃圾收集器。例如,当运行含有第三方库的程序时,类加载子系统会加载必要的.class文件;运行时数据区管理程序数据,如对象实例存储在堆中;执行引擎执行字节码;本地库接口允许Java调用本地应用程序;垃圾收集器则负责清理不再使用的对象,防止内存泄漏。这些组件协同工作,确保了Java程序的高效运行。
17 3
|
29天前
|
消息中间件 设计模式 安全
多线程魔法:揭秘一个JVM中如何同时运行多个消费者
【8月更文挑战第22天】在Java虚拟机(JVM)中探索多消费者模式,此模式解耦生产与消费过程,提升系统性能。通过`ExecutorService`和`BlockingQueue`构建含2个生产者及4个消费者的系统,实现实时消息处理。多消费者模式虽增强处理能力,但也引入线程安全与资源竞争等挑战,需谨慎设计以确保高效稳定运行。
58 2
|
1月前
|
存储 安全 Java
JVM常见面试题(二):JVM是什么、由哪些部分组成、运行流程,JDK、JRE、JVM关系;程序计数器,堆,虚拟机栈,堆栈的区别是什么,方法区,直接内存
JVM常见面试题(二):JVM是什么、由哪些部分组成、运行流程是什么,JDK、JRE、JVM的联系与区别;什么是程序计数器,堆,虚拟机栈,栈内存溢出,堆栈的区别是什么,方法区,直接内存
JVM常见面试题(二):JVM是什么、由哪些部分组成、运行流程,JDK、JRE、JVM关系;程序计数器,堆,虚拟机栈,堆栈的区别是什么,方法区,直接内存
|
2月前
|
Java 编译器 程序员
JVM常见面试题(一):JVM是什么、由哪些部分组成、运行流程是什么,JDK、JRE、JVM的联系与区别
JVM常见面试题(一):JVM是什么、由哪些部分组成、运行流程是什么,JDK、JRE、JVM的联系与区别
JVM常见面试题(一):JVM是什么、由哪些部分组成、运行流程是什么,JDK、JRE、JVM的联系与区别
|
20天前
|
C# UED 开发者
WPF动画大揭秘:掌握动画技巧,让你的界面动起来,告别枯燥与乏味!
【8月更文挑战第31天】在WPF应用开发中,动画能显著提升用户体验,使其更加生动有趣。本文将介绍WPF动画的基础知识和实现方法,包括平移、缩放、旋转等常见类型,并通过示例代码展示如何使用`DoubleAnimation`创建平移动画。此外,还将介绍动画触发器的使用,帮助开发者更好地控制动画效果,提升应用的吸引力。
41 0
|
2月前
|
Java 程序员 C++
大牛程序员用Java手写JVM:刚好够运行 HelloWorld
大牛程序员用Java手写JVM:刚好够运行 HelloWorld
|
1月前
|
算法 Java
JVM常用运行时参数说明
JVM常用运行时参数说明
|
2月前
|
存储 算法 Java
(四)JVM成神路之深入理解虚拟机运行时数据区与内存溢出、内存泄露剖析
前面的文章中重点是对于JVM的子系统进行分析,在之前已经详细的阐述了虚拟机的类加载子系统以及执行引擎子系统,而本篇则准备对于JVM运行时的内存区域以及JVM运行时的内存溢出与内存泄露问题进行全面剖析。
|
1月前
|
Java Docker 索引
记录一次索引未建立、继而引发一系列的问题、包含索引创建失败、虚拟机中JVM虚拟机内存满的情况
这篇文章记录了作者在分布式微服务项目中遇到的一系列问题,起因是商品服务检索接口测试失败,原因是Elasticsearch索引未找到。文章详细描述了解决过程中遇到的几个关键问题:分词器的安装、Elasticsearch内存溢出的处理,以及最终成功创建`gulimall_product`索引的步骤。作者还分享了使用Postman测试接口的经历,并强调了问题解决过程中遇到的挑战和所花费的时间。