简介
本篇内容
- 进程内存与java运行时数据区的关系
- java内存模型(运行时数据区)
- 模拟程序执行
- 模拟对象创建过程
- jvm堆
进程的内存
进程
我们先来简单的说一下进程。
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位
我们电脑上安装的,qq,微信,360等软件,运行起来后,都是以一个个的进程运行在操作系统上,操作系统会把计算机的内存分配给每个进程。
进程的内存
我们把放大镜放到进程中的内存上,其实每个进程内部又把内存分了几块区域,大致分为,代码段,栈段,堆段
java进程
我们的java程序,也是一个进程。这个进程会加载,解析class文件,并在内存中存储、处理。存储的位置就在进程的堆中
本篇文章的重点就是介绍运行时数据区的样子
运行时数据区
运行时数据区分为,方法区,虚拟机栈。本地方法栈。堆。程序计数器。下面依次介绍。
方法区
作用范围: 方法区是所有线程共享的
作用: 方法区用来存储被加载过的类信息,常量,变量等数据
永久代与元空间
方法区是java虚拟机规范给出的“规范(理解为程序中的接口)”,永久代与元空间是不同时期对方法区的实现(1.8后是元空间,之前是永久代)。
还记得我们在基础一中讲的klass模型嘛(klass是java类在c++中的映射)。c++中klass对象其实就是存储在方法区(元空间).
虚拟机栈
虚拟栈是每个线程私有的区域
看这张图想一个问题:虚拟栈的个数有几个?答案显而易见,有几个线程就有多少个。
结构
再看一眼虚拟机栈里面的样子
非常简单,其实就是栈。栈就一定有栈帧。
那这里的栈帧的是什么呢?
每个方法被执行的时候,Java虚拟机都 会同步创建一个栈帧
例如:
public class Test {
public static void main(String[] args) {
add();
}
private static void add(){
}
}
对应的虚拟栈就是这样。
我们来分析下这个图。
- 程序运行后,main方法先执行,在虚拟机栈里创建main方法的栈帧并压栈。main方法中调用add方法后,为add创建栈帧并压栈。所以说main在下add在上。
- 有多少栈帧?经我们第一点的分析后,很容易得出结论:调用了多少方法就有多少栈帧(注意不是有多少方法就有多少个,因为同一个方法可能被调用多次)
栈帧结构
栈帧包括:局部变量表,操作数栈,动态链接,返回地址等信息。
操作数栈与局部变量表
大家来看这个例子就明白了(对字节码不熟悉的请看上篇文章)
public static void main(String[] args) {
int num = 1;
int num2 = 2;
int res = num + num2;
}
操作码
我们模拟一下这段程序的执行过程
- 此时局部变量表的内容
重点记住这几个变量的顺序。
- 常量 1 压入操作数栈
- 栈顶元素弹出赋值给局部变量表的第二个变量
- 常量1 压入操作数栈
- 栈顶元素弹出赋值给局部变量表第三个变量
6.局部变量表第一个int类型的元素压栈
-
局部变量表第二个int类型元素压栈
-
栈顶两个变量相加,结果放入栈顶
- 栈顶元素弹出并赋值给局部变量表的第四个变量
看完这个例子,相信大家对操作数栈跟局部变量表有了一定的认识,我们接下来再看下,new 一个对象时,都做了什么事情。
new 对象的过程
public static void main(String[] args) {
Test test = new Test();
}
操作码
我们逐一解释下
- new #2 <com/haozi/Test>
这个操作符干两件事
第一件:在堆上申请一块空间
第二件:操作数栈,压入这块空间的地址
注意: 此时只在堆上申请了这个对象的空间,并没有执行该对象的构造函数也就是未初始化(对类加载过程不了解的请看,基础三)
- dup
复制栈顶元素,并压入栈顶
为什么要复制一份呢?
因为要弹出一个元素给this指针赋值,如果不复制一份this指针赋值后,栈里就没有地址了。
- invokespecial
执行构造方法
- astore_1
赋值给局部变量表第二个元素
DCL与volatile
为什么懒汉模式下单例对象要加volatile? (不是本篇文章的重点)
我们的cpu执行时为提高效率会乱序执行,上一个例子的第三步与第四步有可能颠倒,那么理论上就有可能拿到还未初始化的对象。
堆
默认大小
最小是物理内存的 1/64 最大是物理内存的 1/4
堆中划分
堆分为老年代,新生代(比例为2:1),新声代分为 eden区,from区,to区(比例为8:1:1)
为什么老年代的空间大?
概括:老年代要存储的对象多.
详细说:(不懂没关系,垃圾回收还会聊)
老年代要负责存储以下情况的对象
- 两年代要存储gc大于15的对象
from区to区来回倒腾15次对象还没被回收旧进入老年代)
- 为新生代进行空间担保 新生代发生gc,且放不下时,这个对象直接进入老年代
例:对象占20兆,ygc后,没被回收,from区只有5兆,此时直接进老年代
- 大对象,对象超过eden区的1/2直接进老年代