zoukankan      html  css  js  c++  java
  • 深入JVM系列二:运行时数据区

    Java虚拟机定义了若干种程序运行期间会使用的运行时数据区:

    1、PC寄存器

    java虚拟机可以支持多条线程同时执行,每一条java虚拟机线程都有自己的pc寄存器,任意时刻一条java虚拟机线程只会执行一个方法的代码。如果方法是native的那么pc寄存器的值是undefined,此时线程应该在等待native方法的返回值,否则就保存java虚拟机正在执行的字节码指令地址。

    2、Java虚拟机栈

    每一条java虚拟机线程都有自己的私有java虚拟机栈,这个栈与线程同时创建,用于存放栈帧。java虚拟机栈用于存储局部变量与一些过程结果,同时也在方法调用与返回中起关键作用,栈帧存在于虚拟机堆中,不需要是连续的。java虚拟机栈被实现成固定大小的或者是根据计算动态扩展和收缩的,如果采用固定大小的 Java 虚拟机栈设计,那每一条线程的 Java 虚拟机栈容量应当在线程创建的时候独立地选定。Java 虚拟机实现应当提供给程序员或者最终用户调节虚拟机栈初始容量的手段,对于可以动态扩展和收缩 Java 虚拟机栈来说,则应当提供调节其最大、最小容量的手段。如果线程请求分配的栈容量超过 Java 虚拟机栈允许的最大容量时,Java 虚拟机将会抛出一个 StackOverflowError 异常。 如果 Java 虚拟机栈可以动态扩展,并且扩展的动作已经尝试过,但是目前无法申请到足够的内存去完成扩展,或者在建立新的线程时没有足够的内存去创建对应的虚拟机栈,那 Java 虚拟机将会抛出一个 OutOfMemoryError 异常。

    3、Java 堆

    在 Java 虚拟机中,堆(Heap)是可供各条线程共享的运行时内存区域,也是供所有类实例和数组对象分配内存的区域。Java 堆在虚拟机启动的时候就被创建,它存储了被自动内存管理系统(Automatic Storage Management System,也即是常说的“Garbage Collector(垃圾收集器)”)所管理的各种对象,这些受管理的对象无需,也无法显式地被销毁。Java 堆的容量可以是固定大小的,也可以随着程序执行的需求动态扩展,并在不需要过多空间时自动收缩。Java 堆所使用的内存不需要保证是连续的。 如果实际所需的堆超过了自动内存管理系统能提供的最大容量,那 Java 虚拟机将会抛出一个 OutOfMemoryError 异常。

    4、方法区

    方法区(Method Area)是可供各条线程共享的运行时内存区域。方法区与传统语言中的编译代码储存区(Storage Area Of Compiled Code)或者操作系统进程的正文段(Text Segment)的作用非常类似,它存储了每一个类的结构信息,例如运行时常量池(Runtime Constant Pool)、字段和方法数据、构造函数和普通方法的字节码内容、还包括一些在类、实例、接口初始化时用到的特殊方法。方法区的容量可以是固定大小的,也可以随着程序执行的需求动态扩展,并在不需要过多空间时自动收缩。方法区在实际内存空间中可以是不连续的。 如果方法区的内存空间不能满足内存分配请求,那 Java 虚拟机将抛出一个 OutOfMemoryError 异常。

    5、运行时常量池

    运行时常量池(Runtime Constant Pool)是每一个类或接口的常量池(Constant_Pool, §4.4)的运行时表示形式,它包括了若干种不同的常量:从编译期可知的数值字面量到必须运行期解析后才能获得的方法或字段引用。运行时常量池扮演了类似传统语言中符号表(Symbol Table)的角色,不过它存储数据范围比通常意义上的符号表要更为广泛。每一个运行时常量池都分配在 Java 虚拟机的方法区之中,在类和接口被加载到虚拟机后,对应的运行时常量池就被创建出来。

    6、本地方法栈

    Java 虚拟机实现可能会使用到传统的栈(通常称之为“C Stacks”)来支持 native 方法(指使用 Java 以外的其他语言编写的方法)的执行,这个栈就是本地方法栈(Native Method Stack)。如果支持本地方法栈,那这个栈一般会在线程创建的时候按线程分配。

    7、栈帧

    栈帧(Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接(Dynamic Linking)、方法返回值和异常分派(Dispatch Exception)。这个和c\c++类似,栈帧随着方法调用而创建,随着方法结束而销毁——无论方法是正常完成还是异常完成(抛出了在方法内未被捕获的异常)都算作方法结束。栈帧的存储空间分配在之中,每一个栈帧都有自己的局部变量表(Local Variables)、操作数栈(Operand Stack)和指向当前方法所属的类的运行时常量池的引用。 Java 虚拟机栈局部变量表和操作数栈的容量是在编译期确定,并通过方法的 Code 属性保存及提供给栈帧使用。因此,栈帧容量的大小仅仅取决于 Java 虚拟机的实现和方法调用时可被分配的内存。 如果当前方法调用了其他方法,或者当前方法执行结束,那这个方法的栈帧就不再是当前栈帧了。当一个新的方法被调用,一个新的栈帧也会随之而创建,并且随着程序控制权移交到新的方法而成为新的当前栈帧。

    8、局部变量表

    每个栈帧内部都包含一组称为局部变量表(Local Variables)的变量列表。栈帧中局部变量表的长度由编译期决定,并且存储于类和接口的二进制表示之中,既通过方法的 Code 属性保存及提供给栈帧使用。一个局部变量可以保存一个类型为 boolean、byte、char、short、float、reference 和 returnAddress 的数据,两个局部变量可以保存一个类型为 long 和 double 的数据。 局部变量使用索引来进行定位访问,第一个局部变量的索引值为零,局部变量的索引值是从零至小于局部变量表最大容量的所有整数。 Java 虚拟机使用局部变量表来完成方法调用时的参数传递,当一个方法被调用的时候,它的参数将会传递至从 0 开始的连续的局部变量表位置上。特别地,当一个实例方法被调用的时候,第 0 个局部变量一定是用来存储被调用的实例方法所在的对象的引用(即 Java 语言中的“this” 关键字)。后续的其他参数将会传递至从 1 开始的连续的局部变量表位置上。

    9、操作数栈

    在方法调用的时候,操作数栈也用来准备调用方法的参数以及接收方法返回结果。 在操作数栈中的数据必须被正确地操作,这里正确操作是指对操作数栈的操作必须与操作数栈栈顶的数据类型相匹配,例如不可以入栈两个 int 类型的数据,然后当作 long 类型去操作他们,或者入栈两个 float 类型的数据,然后使用 iadd 指令去对它们进行求和。

  • 相关阅读:
    python中多进程+协程的使用以及为什么要用它
    python爬虫——多线程+协程(threading+gevent)
    Python几种并发实现方案的性能比较
    Python threadpool传递参数
    python线程池(threadpool)模块使用笔记
    python下的select模块使用 以及epoll与select、poll的区别
    python中的select模块
    Python中threading的join和setDaemon的区别及用法
    python队列Queue
    和为S的连续正数序列
  • 原文地址:https://www.cnblogs.com/liujshi/p/4582280.html
Copyright © 2011-2022 走看看