教育改变生活

标题: 深入Java虚拟机之 --- JVM的爱恨情仇 [打印本页]

作者: 李玮    时间: 2020-5-22 10:21
标题: 深入Java虚拟机之 --- JVM的爱恨情仇
一、认识Java环境
在讲 JVM 之前,先讲讲 JDK、JRE和 JVM 的关系,如下面这张图(图片来自百度图片):

可以看到他们的包含关系是 JDK>JRE>JVM

JDK:jdk是支持 JAVA程序开发的最小环境,集成了JRE和一些工具包,如 javac,jar等;比如一个可运行jar,你就需要安装了jdk,才能运行起来
JRE:是Java运行时的标准环境,除了JVM的环境还有一些基本的JAVA库,比如界面的 swing、I/O等
JVM:熟称Java虚拟机,也叫运行时数据区域,是保证跨平台的基本,因为 jvm 只认识字节码,只要linux、window、mac 有jvm 都是可以编译执行的;

而这里,我们就需要讲解 JVM 这个 运行时数据区域的分布了,如下图(图片来自百度图片,稍微修改了一点):



上面解释了一个java程序是怎么运行的,其中 内存空间这里,就是 JVM 了;

线程共享区:即程序运行时,数据在各个线程之间是共享的,比如某个方法,某个类,还有一些运行时常量
线程私有区:各个线程之间的数据是独立的,比如多线程的数据
为了方便解释,这里的顺序不会像上图那里的顺序来;

二、线程私有区
2.1、程序计数器
首先先了解程序计算器,线程(UI线程)中程序语句的执行都离不开它,对它的解释如下:

是一块较小的内存存于,可以看做当前线程执行字节码时的行号指示器
程序的运行,比如跳转、循环等指令,就是通过改变计数器的数值,来选取下一条需要执行的字节码指令
多线程时,每个线程的程序计算器都是独立的,相互不干扰,独立储存;即记录每次线程的位置,方便下次线程切换过来,知道上次线程的运行到哪了
op
2.2 虚拟机栈
结合方法去中的一些变量和常量去理解会比较好
虚拟机栈也是线程私有的,与线程的生命周期相同;它对应着线程的内存模式,每个方法在执行的时候,都有一个栈帧用于存储局部表,操作数栈、动态链接、方法出口等信息;每个方法的执行,都对应着一个栈帧在虚拟机栈中的入栈和出栈,如下图(图片参考深入Java虚拟机第二版)

局部变量表:

存储了编译器存放着各种基本数据类型(boolean、byte、char等)
对象引用类型,这里的对象不是对象本身,可能是对象的寻址指针,也可能是句柄或者相关位置
returnAddress 类型,指向了一条字节码指令的地址 (现在已经很少有虚拟机用了)
当进入一个方法时,这些变量在帧中分配的内存大小时固定的,在运行时不会改变局部变量表的大小。针对这个区域,规定了两种异常情况

如果虚拟机不支持动态扩展,当线程请求的栈大小大于虚拟机规定的大小时,抛出 StackOverflowError
如果虚拟机栈可以动态扩展,如果扩展时,无法申请到足够的内存,抛出 OutOfMemoryError
操作数栈:
操作数栈,也可以称做操作栈,它可以是 Java 的任意类型,在数据提取时入栈和出栈,比如 int a = 1 + 2;它会先把 1,2 从栈中取出来,把它复制给 a 后,再把结果入栈。
动态连接:
这里需要先理解静态连接,比如类加载的解析步骤,是直接将符号引用转换为直接引用,称为静态解析;而动态连接,则是运行期间,把符号引用转换为直接引用。
可以这样理解符号引用:
比如有当前运行类D,D类中还有其他类的申明,比如C,但虚拟机只会加载当前类,所以C它会由一个符号引用来代替,当类在解析的时候,会把这个符号引用转换为直接引用,直接引用就是类C的指针地址;关于类加载,可以到深入Java虚拟机之 – 类加载机制 细看。

方法返回地址:
每个方法运行结束,只有两种方法可以退出方式;一是正常返回数据,这种称为正常完成出口;二是遇到异常,也会导致退出,称为异常完成退出。
无论哪种退出,都需要返回到方法被调用的位置,程序才能继续进行。

2.3 本地方法栈
本地方法栈与虚拟机栈的作用非常相似;只不过虚拟机栈执行的是 java 的字节码服务,而本地方法栈执行的是 Native 方法服务;
本地方法栈同样会穿件栈帧,如局部变量表、操作栈等信息,同时也有 StackOverflowError 和 OutOfMemoryError 异常







欢迎光临 教育改变生活 (http://bbs.goldoar.com/) Powered by Discuz! X3.2