Java虚拟机之内存区域划分

Java虚拟机之内存区域划分

  1. 程序计数器

    线程私有。在内存中占据一块较小的区域,可以看作是当前线程所执行的字节码的行号指示器

    由于Java虚拟机的多线程是通过线程轮流切换、分配处理器时间的方式来实现的,而每个处理器同一时间只能执行一条线程中的指令,所以每条线程需要一个独立的程序计数器,以便在线程切换过程中,记录并恢复到正确的执行位置。

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

  2. Java虚拟机栈

    线程私有。生命周期和线程相同。**虚拟机栈描述的是Java方法执行的线程内存模型:每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧,用于储存局部变量表、操作数栈、动态连接、方法出口等信息。**每一个方法从调用开始到执行结束,都对应这一个栈帧在虚拟机栈中从入栈到出栈的过程。

    局部变量表:用于存放方法参数和方法内部定义的局部变量。在Java程序被编译成字节码文件时,局部变量表的大小就已经被分析计算出来了。局部变量表的容量以变量槽为最小单位。

    操作数栈:和局部变量表一样,操作数栈的最大深度在编译期就已经确定。一个方法开始执行时,操作数栈为空,在方法执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是入栈和出栈的过程。(在算数计算时,通过将运算涉及的操作数压入栈顶,再调用运算指令来进行;调用其他方法时也是通过操作数栈来进行方法参数的传递)

    动态连接

    方法返回地址:在方法结束时,必须返回到最初方法被调用时候的位置。在方法正常退出时,主调方法的程序计数器的值就可以作为返回地址;而方法异常退出时,返回地址就需要通过异常处理器表来确定,栈帧中不保存这份信息。

    两种异常状况:StackOverflowError异常(线程请求的栈深度大于虚拟机所允许的深度【栈深度溢出】)

    OutOfMemoryError异常(栈扩展无法申请到足够的内存【栈扩展失败】)

    ​ 注:HotSpot虚拟机的栈容量是不可以动态扩展的,所以HotSpot虚拟机上不会由于虚拟机栈 无法扩展而导致OutOfMemoryError异常——只要线程申请栈空间成功就不会,如果申请 失败,还是会出现OOM异常。

  3. 本地方法栈

    线程私有。与虚拟机栈非常相似,区别是虚拟机栈为虚拟机执行Java方法,本地方法栈则为虚拟机执行本地方法。

    本地方法:Native Method,即一个java调用非java代码的接口,由非java语言实现。可以解决某些用Java实现不容易或者效率不高的程序问题。

  4. Java堆

    线程共享。占用内存最大的一块。此内存区域的唯一目的就是存放对象实例,是垃圾收集的主要区域("GC堆“)。现代垃圾收集器基本都是采用分代收集算法,其主要思想是针对不同类型的对象采用不同的垃圾回收算法。

    按照分代思想,堆可以划分为两大块:

    • 新生代(新生代中又包含一个Eden区域和两个Survivor区域)
    • 老年代

    Java堆可以处于物理上不连续的内存空间中,Java堆可以实现成固定大小,也可以进行扩展,-Xms设置堆的初值大小,-Xmx设置堆的最大值,如果扩展失败会抛出OutOfMemoryError异常。

  5. 方法区

    线程共享。用于储存已被虚拟机加载的类信息常量静态变量即时编译器编译后的代码等数据。对这块区域的垃圾回收的主要目标是对常量池的回收和对类的卸载,但一般比较难实现。

    在JDK8以前,HotSpot虚拟机把方法区当成 永久代 来进行垃圾回收,这样使得HotSpot的垃圾收集器可以像管理Java堆一样管理这部分内存,省去专门为方法区编写内存管理代码的工作。但是永久代也存在一些问题,永久代的大小有一个默认的上限,使得它很容易出现内存溢出(OutOfMemoryError)异常。所以在JDK8,就完全废弃了永久代的概念,把方法区移至 元空间 。元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制,也可以通过参数进行设置。

  6. 运行时常量池

    运行时常量池是方法区的一部分。、

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

    除了在编译期生成的常量,还允许动态生成,例如 String 类的 intern()。

  7. 直接内存

    直接内存并不是虚拟机运行时数据区的一部分。

    NIO(New input/output)是JDK1.4中新加入的类,引入了一种基于通道(channel)和缓冲区(buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过堆上的DirectByteBuffer对象对这块内存进行引用和操作。


注:本文参考自《深入理解Java虚拟机》第三版

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值