一、前言
这篇博客起,小编会向一个更加深层次、逼格满满的区域进发——JVM。
可以说JVM不是一个新鲜的东西,但是做java的都会了解JVM,都听过JVM。有的时候我们写的代码运行跟JVM也有关系。
二、JVM介绍
在java诞生的时候,就说“一处编译,到处运行”,是什么来保障“到处运行”的呢?
答案就是JVM。
宏观把控
要想学习jvm,就要对jvm有一个全局把控。
下面的图中是小编画的简单的jvm的重要的内容,也是小编在后面的博客中重点学习的,本节中,就先向大家介绍java运行时的区域。
JVM的位置和作用
我们在使用java之前呢,一开始就要安装jdk,jdk包含了java运行环境JRE、java工具包和java基础类库。其中JRE又包括了JVM、Java核心类库和支持文件。
从上面的图可以看出,我们写好的java文件,经过编译为class文件(字节码文件)。class文件就运行在JVM上,经过java解释执行。
JVM发展历史
三、java内存区域
针对Java的内存区域,我推荐大家可以用四象限的方式来学习。如下图:
在图中,把整个内存区域分层了四块:栈、方法区、堆、程序计数器。
栈
有过一两年的编程经验的工程师都会听过过这个。学过数据结构的就更不在话下。栈的最大的特点就是:“先进后出”。
在Jvm中,栈存储的是局部变量、操作数栈和动态链接方法。
2018年7月26日补充:
栈是线程私有的
栈分为了两个部分:
- Java虚拟机栈,线程私有。生命周期和线程相同。虚拟机栈描述的是Java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧。每个方法从调用到执行完成的过程,就对应着一个栈帧在虚拟机中入栈到出栈的过程。
- 本地方法栈,线程私有。和虚拟机栈的区别就在于一个是为了执行Java方法服务,一个是为了虚拟机使用到的Native方法服务。
什么是本地方法?带有native的方法就是本地方法,一般是底层的方法,
2018年7月26日 补充:
每个线程执行每个方法的时候都会在栈中申请一个栈帧,每个栈帧包括局部变量区和操作数栈,用于存放此次方法调用过程中的临时变量、参数和中间结果。
我们知道了栈中存储是局部变量和操作数栈等。这里就要引出一个概念——栈帧。
现在理解为栈帧包括了局部变量表 + 操作数栈 + 动态链接 + 出口 四个部分:
- 局部变量表:存储局部变量,固定长度的数据集合,定宽的, 32位,一个int的。
- 操作数栈:存储sum中间结果的
- 动态链接:有点抽象,指向的是 方法区中 运行时常量池 的 方法引用。
动态链接是什么? - 返回地址:方法执行完成后,返回。1.正常出口,2.异常出口(上抛?,catch?)
如下图:
a 和 b为局部变量,初始值分别为100 和98 ,局部变量初始化在局部变量表中,分别位于0和1的位置,然后 int c = 0;
,也是局部变量,存储在局部变量表2的位置。然后是c=a+b
,把a压栈到操作数栈,把b压栈到操作数栈,完成a+b后,把结果压栈到操作数栈。因为c=结果,所以要把结果放入到局部变量表的2的位置。这样c就变成了结果。
对于更多的栈帧操作,可以参看小编的这篇博客:
从内存层面理解,为什么 int i = 0; i = i++;
方法区
class static
方法区主要就是存储类、常量、静态变量 和 即时编译器编译后的代码。
方法区中没有垃圾收集。
方法区也叫做静态区,是线程共享的。
有人会问,方法区跟方法有关系吗?
我的回答是:没有关系,老婆跟老婆饼有关系吗?蚂蚁跟蚂蚁上树有关系吗?
堆
new 的对象
堆中主要存储的是对象实例和数组实例。
堆是主要的内存回收区域。
一般我们把堆分为新生代和老年代。更新细致一点的分配是Eden、From Survivor 和To Survivor。
我们这样分配是跟要使用的内存分配和回收算法有关系,也是为了更好的分配和回收内存。
堆中的数据线程共享
程序计数器
主要是计数的。
2018年7月26日补充
可以看做当前线程要执行的代码(字节码)的行号指示器。字节码解释器工作的时候,就是要通过改变整个计数器的值,来找下一行要执行的代码,无论是循环、选择、顺序等都要按照计数器的指示来执行下一行代码。
重要的是,程序计数器是线程私有的,所有线程之间不会共享,独立存储。 这个是因为JVM的多线程是通过线程切换并分配处理器执行时间的方式来实现的。在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。因此为了线程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,所以线程私有。
程序计数器是唯一一个在Java虚拟机规范中没有规定任何OOM的区域。
四、小结
通过这次的学习,我们可以了解,我们写的代码在PC中运行的时候,代码中的各个部分是存储在内存中的什么位置,进一步我们可以分析分析,如何写代码可以让程序运行的更快。