Java Core - JVM运行时内存管理

在读正文之前,阅读以下两篇博客学习并理解堆栈、作用域、本地方法的概念。

作用域:https://www.cnblogs.com/AlanLee/p/6627949.html

操作数栈:https://denverj.iteye.com/blog/1218359 (必读)

堆内存和栈内存:https://www.cnblogs.com/whgw/archive/2011/09/29/2194997.html (必读)

 

一、区域划分(包含5个区域,其中程序计数器、虚拟机栈、本地方法栈为线程私有的,堆和方法区是线程共享的)

JVM在执行程序时,将内存划分为若干个不同的数据区域。

将这些区域分为两类:1.线程私有的数据区域 2.所有线程共享的数据区域

1.线程私有的数据区域

线程私有的意思是:该数据区域随着单个线程的启动而建立,当该线程结束后这个私有数据区域也被销毁。各线程的私有线程互不影响,独立存在。

(一)程序计数器

通常的程序计数器(PC)是计算机CPU中的一个重要部件,它存储着CPU要执行的命令的地址。在计算机从通电到断电这期间,CPU做的事儿也就是不断执行程序计数器指向的指令和更新程序计数器的值使之指向下一条要执行的指令。

Java虚拟机中的程序计数器是Java运行时数据区中的一小块内存区域,但是它的功能和通常的程序计数器是类似的,它指向虚拟机正在执行字节码指令的地址(行号)。具体点儿说,当虚拟机执行的方法不是native的时,程序计数器指向虚拟机正在执行字节码指令的地址;当虚拟机执行的方法是native的时,程序计数器中的值是未定义的。另外,程序计数器是线程私有的,也就是说,每一个线程都拥有仅属于自己的程序计数器。

顺便提一下:java文件是通过编译器被编译成字节码文件的,这个编译器就是javac,这就是JDK和JRE的区别之一,JDK是开法工具包,包括了编译器javac;而JRE是运行环境,不需要包含编译器。

JVM中的程序计数器和CPU中的程序计数器的区别在于:前者指向这个在指向的字节码的行号,而后者指向下一条将被执行的指令的地址。

(二)Java虚拟机栈(方法执行的内存模型栈)

一个线程的虚拟机栈包含着多个栈帧,每个方法在执行时都会创建一个栈帧,它的栈帧里包含着它的局部变量表、操作数栈、动态链接、方法的返回地址等信息。一个方法的被调用和执行完成的时,分别对应着栈帧的创建和入栈、出栈。

局部变量表:

主要用于存储方法中的局部变量,包括方法的局部变量信息和方法的参数信息。如各种基本数据类型的数据以及引用类型。

这里特别指出:引用类型即Reference类型,它不等于对象的本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表

对象的句柄或其他与此对象相关的位置。

  操作数栈:

  不同于程序计数器,Java虚拟机没有寄存器,程序计数器也无法被程序指令直接访问。Java虚拟机的指令是从操作数栈中而不是从寄

器中取得操作数的,因此它的运行方式是基于栈的而不是基于寄存器的。虽然指令也可以从其他地方取得操作数,比如从字节码流中

跟随在操作码(代表指令的字节)之后的字节中或从常量池中,但是主要还是从操作数栈中获得操作数。

 虚拟机把操作数栈作为它的工作区,程序中所有的计算过程都是在借助操作数栈来完成的,大多数指令都是要从这里弹出数据,执行运

 算后将运算结果压回操作数栈。 和局部变量区一样,操作数栈也是被组织成一个以字长为单位的数组。但是和前者不同的是,它不是通

过索引来访问,而是通过标准的栈操作—压栈和出栈—来访问的。比如,如果某个指令把一个值压入到操作数栈中,稍后另一个指令就可以弹出这个值来使用。

 动态链接:

Class 文件中存放了大量的符号引用,字节码中的方法调用指令就是以常量池中指向方法的符号引用作为参数。这些符号引用一部分会

类加载阶段或第一次使用时转化为直接引用,这种转化称为静态解析。另一部分将在每一次运行期间转化为直接引用,这部分称为动态

接。

 方法返回地址:

当一个方法执行完毕之后要回到调用它的地方,因此在栈帧中必须保存一个方法返回地址。

(三)本地方法栈

java native方法是指本地方法,当在方法中调用一些不是由java语言写的代码或者在方法中用java语言直接操纵计算机硬件时要声明为native方法。

通常的程序计数器(PC)是计算机CPU中的一个重要部件,它存储着CPU要执行的命令的地址。在计算机从通电到断电这期间,CPU做的事儿也就是不断执行程序计数器指向的指令和更新程序计数器的值使之指向下一条要执行的指令。

Java虚拟机中的程序计数器是Java运行时数据区中的一小块内存区域,但是它的功能和通常的程序计数器是类似的,它指向虚拟机正在执行字节码指令的地址。具体点儿说,当虚拟机执行的方法不是native的时,程序计数器指向虚拟机正在执行字节码指令的地址;当虚拟机执行的方法是native的时,程序计数器中的值是未定义的。另外,程序计数器是线程私有的,也就是说,每一个线程都拥有仅属于自己的程序计数器。

2.线程共享的数据区域

(一)堆(Heap)

栈存储的是基本类型的变量和引用类型的变量,而堆中存放的通过new关键字产生的对象和数组。从存取速度上来讲,栈比堆更快,但是不够灵活,堆之所以慢是因为每次存取都是动态的。记住一点:堆可以处于物理位置不连续的内存空间中,只要逻辑上是连续的就可以了。

(二)方法区

方法区用于存储已被虚拟机加载的类信息、常量、静态变量以及被编译器编译后的class字节码文件。class字节码文件包含常量池,而当class文件被类加载时,class文件中的常量池中的字面量和符号引用就会别加载到运行时常量池中。

 

 

posted @ 2019-04-09 01:29  Jwsmai  阅读(447)  评论(0编辑  收藏  举报