您的位置 首页 java

Java运行时数据区

方法区

(1)方法区是各个 线程 共享的内存区域,在虚拟机启动时创建

(2)虽然 Java虚拟机 规范把方法区描述为堆的一个逻辑部分,但是它却又一个别名叫做Non-Heap(非 堆),目的是与Java堆区分开来

(3)用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据

(4)当方法区无法满足内存分配需求时,将抛出 OutOfMemory Error异常

堆内存

(1)Java堆是Java虚拟机所管理内存中最大的一块,在虚拟机启动时创建,被所有线程共享。

(2) Java 对象实例以及数组都在堆上分配。

虚拟机栈

栈帧(Stack Frame)

栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。每一个方法从调用开始至执行完成的过程,都对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程。一个方法对应一个栈帧内存区域

main方法运行时栈模型

1.1 局部变量表

用于存放方法参数和方法内部定义的局部变量。在编译期由 Code 属性中的max_locals确定局部变量表的大小。 局部变量表的容量以变量槽(Variable Slot)为最小单位。

注意:

1. 对于64位的数据类型,虚拟机会以高位对齐的方式为其分配两个连续的Slot空间。

2. Java语言中明确的(reference类型则可能是32位也可能是64位)64位的数据类型只有long和double两种。

3. 虚拟机通过索引定位的方式使用局部变量表,索引值的范围是从0开始至局部变量表最大的Slot数量。如果访问的是32位数据类型的变量, 索引 n就代表了使用第n个Slot,如果是64位数据类型的变量,则说明会同时使用n和n+1两个Slot。

4. 对于两个相邻的共同存放一个64位数据的两个Slot,不允许采用任何方式单独访问其中的某一个,Java虚拟机规范中明确要求了如果遇到进行这种操作的 字节码 序列,虚拟机应该在类加载的校验阶段抛出异常。

5. 局部变量表中第0位索引的Slot默认“this”关键字的引用。

6. Slot可重用,方法体中定义的变量,其作用域并不一定会覆盖整个方法体,如果当前字节码PC计数器的值已经超出了某个变量的作用域,那这个变量对应的Slot就可以交给其他变量使用。

1.2 操作栈

javap -c

同局部变量表一样,操作数栈的最大深度也在编译的时候写入到Code属性的

max_stacks数据项中。操作数栈的每一个元素可以是任意的Java数据类型,包括long和double。32位数据类型所占的栈容量为1,64位数据类型所占的栈容量为2。

当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈/入栈操作。例如,在做 算术 运算的时候是通过操作数栈来进行的,又或者在调用其他方法的时候是通过操作数栈来进行参数传递的。

1.3 动态连接

javap -v

每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接(Dynamic Linking)。

1.4 方法返回地址

当一个方法开始执行后,只有两种方式可以退出,一种是遇到方法返回的字节码指令;一种是遇见异常,并且这个异常没有在方法体内得到处理。

演示程序

  public class Math {
      private  int numbre = 1;
     public int compute() {
         int a = 1;
         int b = 2;
         int c = (a + b) * 10;
         return c;
    }

     public  static   void  main(String[] args) {
         Math math = new Math();
         math.compute();
         System.out.println("end");
    }
 }                

程序计数器

我们都知道一个 JVM 进程中有多个线程在执行,而线程中的内容是否能够拥有执行权,是根据 CPU 调度来的。

假如线程A正在执行到某个地方,突然失去了CPU的执行权,切换到线程B了,然后当线程A再获得CPU执行权的时候,怎么能继续执行呢?这就是需要在线程中维护一个变量,记录线程执行到的位置。

如果线程正在执行Java方法,则 计数器 记录的是正在执行的虚拟机字节码指令的地址;

如果正在执行的是Native方法,则这个计数器为空。

本地方法栈

如果当前线程执行的方法是Native类型的,这些方法就会在本地方法栈中执行。

那如果在Java方法执行的时候调用native的方法呢?

Java虚拟机栈通过动态链接调用native方法

除了上面五块内存之外,其实我们的JVM还会使用到其他两块内存

直接内存(Direct Memory)

并不是虚拟机运行时数据区的一部分,也不是JVM规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError 异常出现,所以我们放到这里一起讲解。在JDK1.4 中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffffer)的I/O 方式,它可以使用Native 函数库直接分配堆外内存,然后通过一个存储在Java 堆里面的DirectByteBuffffer 对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java 堆和Native 堆中来回复制数据。

本机直接内存的分配不会受到Java 堆大小的限制,但是,既然是内存,则肯定还是会受到本机总内存的大小及处理器寻址空间的限制。因此在分配JVM空间的时候应该考虑直接内存所带来的影响,特别是应用到NIO的场景。

其他内存:

Code Cache:JVM本身是个本地程序,还需要其他的内存去完成各种基本任务,比如, JIT编译器 在运行时对热点方法进行编译,就会将编译后的方法储存在Code Cache里面;GC等功能。需要运行在本地线程之中,类似部分都需要占用内存空间。这些是实现JVM JIT等功能的需要,但规范中并不涉及。

文章来源:智云一二三科技

文章标题:Java运行时数据区

文章地址:https://www.zhihuclub.com/179755.shtml

关于作者: 智云科技

热门文章

网站地图