• Java运行时的数据区域


    2.1 运行时的数据区域

    运行时的数据区域按照生命周期的不同可以分为两个部分,分别是

    1、随着虚拟机进程的启动而一直存在:方法区+Java堆

    2、随着用户线程的启动和结束而建立和销毁:虚拟机栈+本地方法栈+程序计数器

    运行时的数据区域按照共享的不同可以分为两个部分,分别是:

    1、由所有线程共享的数据区:方法区+Java堆

    2、线程隔离的数据区:虚拟机栈+本地方法栈+程序计数器

    Java虚拟机所管理的数据区域划分图如下:

    2.1.1 程序计数器

    程序计数器是当前线程执行字节码的行号指示器,它也是程序控制流的指示器,分支、循环、跳转、异常处
    理、线程恢复等基础功能都需要依赖这个计数器来完成。

    对于单核处理器,同一时刻都只会执行一条线程中的指令,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储。

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

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

    2.1.2 Java虚拟机栈

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

    虚拟机栈用来描述Java方法执行的线程内存模型:每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧用于存储局部变量表、操作数栈、动态连接、方法出口等信息。

    每个方法对应一个栈帧,每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。

    栈帧的局部变量表是非常重要的:

    局部变量表存放了方法在编译时期的所要用到的(1)基本数据类型boolean、byte、char、short、int、float、long、double(2)对象引用(reference类型)(3)returnAddress类型(指向了一条字节码指令的地址)。

    这些数据类型在局部变量表中的存储空间以局部变量槽(Slot)来表示,其中64位长度的long和double类型的数据会占用两个变量槽,其余的数据类型只占用一个。注意:局部变量槽占用32位还是64位,完全取决于Java虚拟机的配置

    2.1.3 本地方法栈

    只需要记住:

    1、Java虚拟机栈管Java的方法、本地方法栈管本地的方法服务(可能是其他语言的服务)

    2、HotSpot虚拟机直接把Java虚拟机栈和本地方法栈合二为一

    2.1.4 Java栈

    Java虚拟机栈和本地方法栈统称为Java栈(特别是在HotSpot虚拟机中,两者就是一体)

    在《Java虚拟机规范》中,对Java栈这个内存区域规定了两类异常状况

    (1)如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;

    (2)如果Java虚拟机栈容量可以动态扩展(HotSpot不支持),当栈扩展时无法申请到足够的内存会抛出OutOfMemoryError异常。

    2.1.5 Java堆

    对于Java应用程序来说,Java堆(Java Heap)是虚拟机所管理的内存中最大的一块

    Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。

    此内存区域的唯一目的就是存放对象实例,Java世界里“几乎”所有的对象实例以及数组都在这里分配内存。之所是“几乎”,是因为随着即使编译技术的增强,对象实例的位置都在堆上也不是那么绝对了。

    Java堆是垃圾收集器管理的内存区域。

    Java堆可以处于物理上不连续的内存空间中,但在逻辑上它应该被视为连续的,但是对于数组这种大对象,大部分虚拟机都会要求连续的内存空间。

    Java堆既可以被实现成固定大小的,也可以是可扩展的,不过当前主流的Java虚拟机都是按照可扩展来实现的(通过参数-Xmx和-Xms设定)。

    2.1.6 Java方法区

    开宗明义:不要被名字所迷惑,Java方法区并不是管理Java方法的,Java虚拟机栈才是

    Java方法区主要管理:

    (1)类型信息:类名、访问修饰符、字段描述、方法描述

    (2)常量(3)静态变量(4)即时编译器编译后的代码缓存等数据

    在JDK8以前,方法区和永久代是基本等价的(HotSpot平台)。在JDK8之前,由于Java的方法区被HotSpot的垃圾收集器的当成永久代管理,因此和堆在一起管理,所以,被称为永久代。

    但是,到JDK6的时候,HotSpot团队就准备放弃永久代,准备利用本地内存来实现方法区了。

    JDK7的时候,字符串常量池(移动到Java堆中)、静态变量从永久代移出。

    JDK8的时候,永久代退出历史舞台,把JDK 7中永久代还剩余的内容(主要是类型信息)全部移到元空间中,方法区由本地内存的元空间实现

    2.1.7 运行时常量池

    运行时常量池(Runtime Constant Pool)是方法区的一部分。注意:字符串常量池在Java堆中

    2.1.8 直接内存

    直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是《Java虚拟机规范》中定义的内存区域。它是Java的堆外内存,属于本地内存中的一部分

    按道理来说,直接内存由主机操作系统管理,不会有OOM异常。但是,在JDK 1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样就可能导致,直接内存爆掉。

  • 相关阅读:
    No module named _tkinter
    Camera2与TextureView使用
    Collections常用方法总结
    Android插件化框架
    《战狼2》观后感——民族荣耀
    《茶马古道》观后感——朝圣之路
    点击查看大图Activity
    图片压缩代码
    《天那边》观后感——对一些现象的反思
    recyclerView的使用
  • 原文地址:https://www.cnblogs.com/doubest/p/12858091.html
Copyright © 2020-2023  润新知