Java基础之《JVM性能调优(2)—栈》

一、什么是程序计数器?
1、背景:
计算机组成原理中,CPU内部的寄存器中就包含一个程序计数器(x86下为eip寄存器,ARM下为R15寄存器),存放程序执行的下一条指令地址。
程序每次执行时,CPU都会自动存储下一条指令的地址,以便下次继续执行。

2、jvm偷学CPU:
JVM也是参考了CPU的程序计数器,自己设计了一个程序计数器,用于存储JVM当前执行bytecode的地址(字节码指令地址)。
程序计数器记录当前线程正在执行的字节码的地址。
程序计数器是线程隔离的,每一个线程在工作的时候都有一个独立的计数器。

3、什么是字节码?

4、为什么要存储这个字节码指令?
由于CPU的上下文切换,导致不停的切换每条线程,就会出现线程经常中断或恢复。
当CPU上下文切换,线程中断时,必须准确记录每条线程的执行位置,即记录当前线程执行的字节码指令地址。
当线程恢复时,再从中断的位置继续执行。
为了达到这样一个效果,JVM为每条线程都分配一个程序计数器。
这样每条线程都可以独立计算,故当出现CPU上下文切换时,线程中断,程序计数器记录了当前的字节码指令位置,线程恢复,就从中断位置继续执行。

5、小结:程序计数器的特点
(1)每条线程都有自己的程序计数器,而且是线程私有的,它的生命周期与线程相同(随线程而生,随线程而灭)
(2)程序控制流的指示器,例如:分支、循环、跳转、异常处理、线程恢复等基础功能都是依赖这个计数器来执行
(3)字节码解析器的工作原理是通过计数器的值来执行下一条字节码指令的
(4)程序计数器具有线程隔离性
(5)程序计数器占用的内存空间非常小,可以忽略不计
(6)程序计数器是java虚拟机规范中唯一一个没有规定任何OutOfMemeryError的区域
(7)执行native本地方法时,程序计数器的值为空。原因是native方法是java通过jni调用本地C/C++库来实现,非java字节码实现,所以无法统计

二、虚拟机栈
1、概念
Java虚拟机栈是线程私有的,它的生命周期与程序相同(随线程而生,随线程而灭)。
虚拟机栈描述的是Java方法执行的内存模型:栈帧(Stack Frame)是用于支持Java虚拟机进行方法调用和执行的数据结构,它是虚拟机栈中的栈元素。
每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。

2、例子

public class Stack {
    public void methodA() {
        int a = 10;
        this.methodB();
    }

    public void methodB() {
        int b = 10;
        this.methodC();
    }

    public void methodC() {
        int c = 10;
    }

    public static void main(String[] args) {

    }
}


(1)每条线程在创建的时候都会创建一个虚拟机栈,虚拟机栈内部是一个个栈帧,每个栈帧对应一个java方法调用。
例如上图,当主线程执行Stack类时,会创建3个栈帧,即每个java方法对应一个栈帧。
(2)栈的特性是后进先出
当执行到methodA时,新建一个栈帧,进栈,处于栈顶。
当执行到methodB时,新建一个栈帧,进栈,处于栈顶。
当methodB执行结束时,methodB这个栈帧,出栈,methodA处于栈顶。
当methodA执行结束时,methodA这个栈帧,出栈,栈空。

三、局部变量表是用来干嘛的?
1、概念
它是用来存储方法参数、方法体内的变量。

2、局部变量表的生命周期
随线程而生,随线程而灭。

3、如何查看这张表?
在class文件的字节码里面。

4、为什么通过字节码文件,就能查看到局部变量表?
因为这张表在编译的时候就建好了,就像我们操作数据库一样,要先把表建好,程序才能对表进行读写操作。

5、字节码指令行号如何和代码关联?
LineNumberTable:行号表

字节码的指令行号和源码对应起来,有个行表:
字节码指令行号0,对应第9行
字节码指令行号4,对应第10行
字节码指令行号8,对应第11行

6、如何查看局部变量表?
LocalVariableTable:局部变量表

引用类型I表示int类型

四、局部变量表存储了哪些数据类型?
1、数据库每个字段都要给他设置数据类型,那局部变量表的数据类型是什么呢
局部变量表的数据类型是slot(变量槽),而且只有一个数据类型,不像数据库那么多类型

2、那这个slot槽有多大?
一个slot位是32位bit
java的数据类型分为2种:原始类型和引用类型
原始类型:分为8种基本类型(byte、short、int、long、float、double、char、boolean)和returnAddress类型
引用类型:包括对象应用和数组引用

3、一个槽位32bit如何装下java的各种数据类型
小于32bit装一个slot,大于32bit装2个槽位
byte、short、int、float、char、boolean:装1个slot槽位
long、double:装2个slot槽位

 lang类型占2个槽位,引用类型是J

五、局部变量表的对象引用原理

 数据的对象引用只占一个槽位

这个referece对象引用,需要涉及3个区域:栈、堆、方法区
栈帧:本地变量表,存放了referece对象引用
堆:存放了Test3对象实例,例如对象成员变量name的值=bbb
方法区:存放对象的类型数据(对象类型、父类、实现接口、方法、常量等等)的地址信息,例如KEY的值

六、操作数栈的压栈和出栈
1、什么是操作数栈?
操作数栈主要用于计算过程中的临时数据存储,即计算过程中变量的临时存储空间。
来一种简单抽象的比喻:局部变量变类似磁盘、操作数栈类似内存。
具体的操作流程:
每次执行一个方法,方法内部的字节码指令,就会对栈进行写数据(入栈push)或取数据(出栈pop)

2、压栈出栈过程
程序计数器:执行下一条字节码指令的地址
操作数栈:保存临时数据
局部变量:保存方法参数,方法体内变量

指令说明:
 0 sipush 1000:将1000压入栈中
 3 istore_1:将变量a=1000出栈,存储到第1个局部变量(slot)中
 4 sipush 2000:将2000压入栈中
 7 istore_2:将变量b=2000出栈,存储到第2个局部变量(slot)中
 8 iload_1:将局部变量表中第1个变量1000压栈
 9 iload_2:将局部变量表中第2个变量2000压栈
10 iadd:将操作数栈顶两个int数弹出来,相加后再压入栈中
11 istore_3:将栈顶的int数3000弹出,存储到第3个局部变量(slot)中
12 return:执行return

3、操作数栈有什么特点
(1)当一个方法开始执行时,就会创建一个新的栈帧,同时栈帧就包含了一个空的操作数栈。
(2)栈的数据结构一般采用数组或者链表来实现,栈的元素大小是32bit(元素长度),如果是long类型的就占2个元素。
它的存储空间和局部变量表一样都是32bit,但是区别是,局部变量表是通过索引来访问;而操作数栈是通过压栈和出栈来访问的。例如,某个指令把一个值压入操作数栈,稍后另一个指令就可以弹出这个值来使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值