JVM运行时数据区域

学习《深入理解java虚拟机》第三版 周志明著笔记

1.1 程序计数器

是一块较小的内存空间,可以看作是当前线程所执行的字节码的行号指示器。在Java虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。

程序计数器是线程私有的:由于Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一台哦线程中的指令。而为了线程切换后都能恢复当正确的执行位置,每天线程都需要一个独立的程序计数器,各条线程之间互不影响,独立存储。

如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是本地(Native)方法,这个计数器值则为空(native 本地方法是大多是通过C实现并未编译成需要执行的字节码指令所以在计数器中当然是空)。

问: 那native 方法的多线程是如何实现的呢?

答: native 方法是通过调用系统指令来实现的,那系统是如何实现多线程的则 native 就是如何实现的

摘博客一段话:

Java线程总是需要以某种形式映射到OS线程上。映射模型可以是1:1(原生线程模型)、n:1(绿色线程 / 用户态线程模型)、m:n(混合模型)。以HotSpot VM的实现为例,它目前在大多数平台上都使用1:1模型,也就是每个Java线程都直接映射到一个OS线程上执行。此时,native方法就由原生平台直接执行,并不需要理会抽象的JVM层面上的“pc寄存器”概念——原生的CPU上真正的PC寄存器是怎样就是怎样。就像一个用C或C++写的多线程程序,它在线程切换的时候是怎样的,Java的native方法也就是怎样的。

此内存区域是唯一一个在《Java虚拟机规范》中没有任何OutOfMemoryError情况的区域。

1.2 java虚拟机栈

Java虚拟机栈也是线程私有的,它的生命周期与线程相同。

描述的是Java执行线程的内存模型:每个方法被创建的时候,Java虚拟机都会同步创建一个栈帧用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程,都对应着一个栈帧在虚拟机栈帧从入栈到出栈的过程。

局部变量表存放了编译器可知的各种Java虚拟机基本数据类型、对象引用类型和returnAddress类型(指向一条字节码指令的地址)。这些数据类型在局部变量表中都以局部变量槽来表示,64位长度的long和double类型都会占用两个变量槽,其他的数据类型占一个。因为当进入一个方法时,这个方法需要在栈帧中分配多大的局部变量空间是可以完全确认的,所以在方法的运行期间局部变量表的大小(变量槽的数量)是不会改变的。变量槽的大小是由具体的虚拟机确定的。

如果线程请求的栈深度大于虚拟机允许的深度,会抛出StackOverflowError异常;如果Java虚拟机栈容量可以动态扩展,当栈扩展时无法申请到足够的内存会抛出OutOfMemoryError()异常(HotSpot虚拟机的内存是不可以动态扩展的,但是如果申请空间的时候就失败了,任然会抛出OOM异常)。

1.3 本地方法栈

与虚拟机栈一样,本地方法栈也会在深度溢出或者栈扩展失败时分别抛出StackOverFlowError和OutOfMemoryError异常。具体实现由具体虚拟机根据需求自由实现。

注:以下参数设置都只能在 Edit configuration中配置,或者在idea 的vmoptions中修改。

//HotSpot虚拟机不区分虚拟机栈和本地方法栈,所以-Xoss 参数(设置本地方法栈大小)并没有任何效果
//栈容量只能由 -Xss 参数来设定
/**
*VM Args: Xss128K
*/

1.4 Java堆

Java堆是虚拟机所管理的内存中最大的一块。在虚拟机启动时创建被所有线程共享的一块内存区域。该内存的唯一目的就是存放实例对象(Java世界里几乎所有的对象实例都在这里分配内存)。Java堆是垃圾收集器管理的内存区域。

Java堆可以处于物理上不连续的空间中,但在逻辑中是被视为连续的。

Java堆可以被实现成固定大小的,也可以是可扩展的(当前主流的Java虚拟机都是按照可扩展来实现的,通过参数-Xmx和Xms设定。因为是可扩展,所以在Java堆中没有内存完成实例分配,而且堆无法再扩展时Java虚拟机将会抛出OutOfMemoryError异常。

//-Xms设置堆的最小值 -Xmx堆的最大值,当两者大小相等时表示堆不可扩展
/**
*VM Args: -Xms20m -Xmx20m
*
*/

1.5 方法区

方法区与Java堆一样,是各个线程共享的内存区域,它用于存储被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。

根据《Java虚拟机规范》的规定,如果方法区无法满足新的内存分配时,将抛出OutOfMemoryError异常。

1.5.1 运行时常量池

运行时常量池是方法区的一部分。存放的是常量池表(Class文件中的一项信息),常量池表用于存放编译期生成的各种字面量与符号引用,这部分内容将在类加载后存放到方法区的运行时常量池中。

运行时常量池相对于Class文件常量池具备动态性。并非预置入Class文件中常量池的内容才能进入方法区运行时常量池,运行期间也可以将新的常量放入池中。例如:String类的intern()方法。

 直接内存(扩展)

并不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。

但是这部分内存也可能导致OutOfMemoryReeor异常

/**
*VM Args: -XX:MaxDirectMemorySize=10M
*/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值