第二章 Java内存区域与内存溢出异常(JVM)

运行时数据区域

Java虚拟机所管理的内存将会包括以下几个运行时数据区域:
这里写图片描述

程序计数器

程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取吓一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

Java虚拟机栈

虚拟机栈描述的是Java 方法 执行的内存模型:虚拟机栈的栈元素是栈帧,当有一个方法被调用时,代表这个方法的栈帧入栈;当这个方法返回时,其栈帧出栈。因此,虚拟机栈中栈帧的入栈顺序就是方法调用顺序。

什么是栈帧?

栈帧可以理解为方法的运行空间。它主要由两部分组成:一部分是局部变量表,方法中定义的局部变量以及方法的参数就存放在这张表中;另一部分是操作数栈,用来存放操作数。我们知道,Java 程序编译之后就变成了一条条字节码指令,其形式类似汇编,但和汇编有不同之处: Java 字节码指令的操作数存放在操作数栈中。


注意,局部变量表中的变量不可直接使用,如需使用必须通过相关指令将其加载至操作数栈中作为操作数使用。比如有一个方法 void foo(),其中的代码为:int a = 1 + 2; int b = a + 3;,编译为字节码指令就是这样的:
iconst_1 //把整数 1 压入操作数栈
iconst_2 //把整数 2 压入操作数栈
iadd //栈顶的两个数出栈后相加,结果入栈;实际上前三步会被编译器优化为:iconst_3
istore_1 //把栈顶的内容放入局部变量表中索引为 1 的 slot 中,也就是 a 对应的空间中
iload_1 // 把局部变量表索引为 1 的 slot 中存放的变量值(3)加载至操作数栈
iconst_3
iadd //栈顶的两个数出栈后相加,结果入栈
istore_2 // 把栈顶的内容放入局部变量表中索引为 2 的 slot 中,也就是 b 对应的空间中
return // 方法返回指令,回到调用点

什么是slot?

slot 是局部变量表中的空间单位

Java字节码指令

Java 的指令以字节为单位,也就是一个字节代表一条指令(一个字节是八位)。

Java虚拟机栈

Java堆

Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,就是我们所说的new对象,该对象会在堆中开辟一块内存来存放对象的一些信息,比如属性啊什么的。中存放的局部引用变量所指向的大多数都会在中存放。


方法区和其中的运行时常量池

是线程共享的内存区域,用于存储已被虚拟机加载的类信息常量静态变量编译器编译后的代码(也就是存储字节码文件)。这里可以看到常量也会放到方法区里,因为方法区中有一个运行时常量池,为什么叫运行时常量池,因为在编译后期生成的是各种字面量(字面量的意思就是,比如int i=3,这个3就是字面量的意思)和符号引用。这些是存放在一个叫做常量池(这个常量池是在字节码文件中)的地方,当类加载进入方法区时,就会把该常量池中的内容放入运行时常量池中。现在只需要知道方法区中有一个运行时常量池,就是用来存放常量的。

练习:画内存图
画内存图

分析过程
1. 首先运行程序,Demo1_car.java就会变为Demo1_car.class,将Demo1_car.class加入方法区,检查是否字节码文件常量池中是否有常量值,如果有,那么就加入运行时常量池
2. 遇到main方法,创建一个栈帧,入虚拟机栈,然后开始运行main方法中的程序
3. Car c1 = new Car(); 第一次遇到Car这个类,所以将Car.java编译为Car.class文件,然后加入方法区,跟第一步一样。然后new Car()。就在堆中创建一块区域,用于存放创建出来的实例对象,地址为0X001.其中有两个属性值 color和num。默认值是null 和0
4. 然后通过c1这个引用变量去设置color和num的值
5. 调用run方法,然后会创建一个栈帧,用来装run方法中的局部变量的,入虚拟机栈,run方法中就打印了一句话,结束之后,该栈帧出虚拟机栈。又只剩下main方法这个栈帧了
6. 接着又创建了一个Car对象,所以又在堆中开辟了一块内存,之后就是跟之前的步骤一样了。


对象的创建

Java是一门面向对象的编程语言,在Java程序运行过程中无时无刻都有对象被创建出来。在语言层面上,创建对象通常仅仅是一个new关键字而已,而在虚拟机中,对象的创建是怎样的一个过程呢?

对象的内存布局

对象的访问定位

Java程序需要通过栈上的reference(引用)来操作堆上的具体对象。

  1. 使用句柄访问
    Java堆中将会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息
  2. 使用指针访问
    reference直接存放的就是对象地址。

Java虚拟机栈


JVM参数信息

堆设置
1. -Xms:初始堆大小
2. -Xmx:最大堆大小
3. -XX:设置年轻代大小
4. -XX:NewRatio=n:设置年轻代和年老代的比值.如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
5. -Xss:设置虚拟机栈的大小

JVM配置信息

猜你喜欢

转载自blog.csdn.net/qq_32682177/article/details/82346661