Java进阶训练营 第一周JVM 预习笔记

第一周 预习资料

1.环境准备

JDK、JRE、JVM关系


JDK = JRE(运行环境) + 开发工具

JRE = JVM + 类库

Java进阶训练营 第一周JVM 预习笔记_第1张图片


开发Java程序交互关系:

用JDK开发JAVA程序,编译成字节码,打包给装有JRE的程序运行。

JRE启动JVM实例,加载、验证、执行Java字节码及依赖库,运行Java程序。


Linux配置Java环境变量


$ cat ~/.bash_profile


# JAVA ENV
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home
export PATH=$PATH:$JAVA_HOME/bin


让环境配置立即生效:

$ source ~/.bash_profile


查看环境变量:

echo $PATH

echo $JAVA_HOME


查找JDK安装在哪个目录?

jps ‐v

whereis javac

ls ‐l /usr/bin/javac

find / ‐name javac


2.量化性能指标

关注硬件:CPU+内存+IO(磁盘+网络)


衡量系统性能3个维度:

延迟:请求响应时间

吞吐量:交易类每秒事务数(TPS),查询类每秒请求数(QPS)

系统容量:硬件配置


性能调优总结:

第一步:收集数据,制定指标

第二步:分析解决瓶颈问题


3.JVM基础知识


1)编程语言分类

机器语言:二进制编码

汇编语言:缩写英文标符号

高级语言:多种编程语言总称


4.字节码技术

4.1.字节码简介

Java字节码由单字节指令组成,最多支持256个操作码,由前缀+操作名称组成。

指令分为:

1、栈操作指令,包括与局部变量操作指令

2、程序流程控制指令

3、对象操作指令,包括方法调用指令

4、算术运算以及类型转换指令


4.2.获取字节码清单


# javac编译
javac demo/jvm0104/HelloByteCode.java
# javap反编译
javap -c demo/java0104/HelloByteCode.class

4.3.解读字节码清单

4.4.查看class文件中的常量池信息

javap -c -verbose demo.jvm0104.HelloByteCode
ACC_PUBLIC: public类
ACC_SUPER: 调用super类
#1 常量编号
= 分隔符
Mathodref这个常量指向的是一个方法

4.5.查看方法信息

方法描述image.png

小括号内    入参/形参信息

左方括号    表示数组

L    表示对象

V    这个返回值是void

Java进阶训练营 第一周JVM 预习笔记_第2张图片

栈深度stack=2;

局部变量表保留多少个槽位locals=2;

方法的参数个数args_size=1


无参构造函数的个数不是0

对于非静态方法,this将被分配到局部变量表的第0号槽位中


4.6.线程栈与字节码执行模型

每个线程有自己独有的线程栈,用于存储栈帧。

每执行一个方法,JVM都会创建栈帧。

栈帧由操作数栈,局部变量数组以及class引用。

Java进阶训练营 第一周JVM 预习笔记_第3张图片

class引用指向常量池中class

局部变量数组:方法参数,局部变量

操作数栈是一个LIFO结构的栈, 用于压 入和弹出值。

4.7.方法体中的字节码解读

方法体中字节码解读

方法体中字节码前数字是数组索引号

Java进阶训练营 第一周JVM 预习笔记_第4张图片

image.png


4.8.对象初始化指令:new,init,clinit

new创建对象,但没调构造函数

invokespecial调特殊方法,这里构造函数

dup复制栈顶值

astore {N}或astore_{N}赋值给局部变量

putfield将值赋值给实例字段

putstatic将值赋值给静态字段

静态初始化方法



4.9.栈内存操作指令

Java进阶训练营 第一周JVM 预习笔记_第5张图片

dup复制栈顶值

pop删除栈顶值

swap交换栈顶值

dup_x1复制粘贴栈顶值

dup_x2复制粘贴栈顶两个值

4.10.局部变量表

javac -g xx.java

javap -c xx.class

astore_1 将引用地址值存储到编号1的局部变量中

iconst_1 将常量值1加载到栈里面

dstore 4 将double值保存到本地变量4号槽位

静态方法,槽位0没有this引用位置。

Java进阶训练营 第一周JVM 预习笔记_第6张图片

4.11.流程控制指令

if_icmpge: if,integer,compare,great equal

一个值是否大于等于另一个值


iinc 4,1: 4号槽位值加1

goto 18: 跳到循环开始地方


4.12.算术运算指令与类型转换指令

i2d: int to double


inic 不需要将数值load到操作数栈,直接对LocalVariableTable值进行运算


4.13.方法调用指令和参数传递

invokestatic 调用静态方法

invokespecial 调用构造方法,private方法,超类方法

invokevirtual 调用公共,受保护和打包私有方法

invokeinterface 调用接口


4.14.InvokeDynamic

为了实现动态类型语言

把实际翻译策略隐藏在JDK库实现


5.Java类加载机制

5.1.类的生命周期和加载过程

Java进阶训练营 第一周JVM 预习笔记_第7张图片


一个类在JVM里的生命周期有7个阶段,

分别是加载(Loading)、验证 (Verification)、准备(Preparation)、解析(Resolution)、初始化 (Initialization)、使用(Using)、卸载(Unloading)。


前五个部分(加载,验证,准备,解析,初始化)统称为类加载


1)加载

找class文件,找不到报NoClassDefFound


2)校验

检查 classfile 语义,常量池中的符号,并执行类型检查


加载所有超类和接口,检查类层次结构


3)准备

创建静态字段,初始化


4)解析

解析常量池,主要有以下四种:类或接口的解析、字段解析、类方法解析、接 口方法解析。


5)初始化

初始化的过程包括执行:

类构造器方法

static静态变量赋值语句

static静态代码块


5.2 类加载时机


5.3 类加载器机制

通过一个类的全限定名a.b.c.XXClass来获取描述此类的Class 对象


系统自带的类加载器分为三种:

启动类加载器(BootstrapClassLoader) 加载 Java 的核心类

扩展类加载器(ExtClassLoader) 加载JRE的扩展目录

应用类加载器(AppClassLoader)加载来自Java命令的classpath或者­cp选项、java.class.path系统属性指定的jar包和类路径


Java进阶训练营 第一周JVM 预习笔记_第8张图片

类加载机制有三个特点:


  1. 双亲委托:当一个自定义类加载器需要加载一个类,比如java.lang.String,它很
    懒,不会一上来就直接试图加载它,而是先委托自己的父加载器去加载,父加载
    器如果发现自己还有父加载器,会一直往前找,这样只要上级加载器,比如启动
    类加载器已经加载了某个类比如java.lang.String,所有的子加载器都不需要自己
    加载了。如果几个类加载器都没有加载到指定名称的类,那么会抛出
    ClassNotFountException异常。
  2. 负责依赖:如果一个加载器在加载某个类的时候,发现这个类依赖于另外几个类
    或接口,也会去尝试加载这些依赖项。
  3. 缓存加载:为了提升加载效率,消除重复加载,一旦某个类被一个类加载器加
    载,那么它会缓存这个加载结果,不会重复加载。


怎么看到加载了哪些类,以及加载顺序?

在类的启动命令行参数加上 ‐XX:+TraceClassLoading 或者 ‐verbose 即 可


6.JMM模型

6.1 JVM内存结构

JVM的内存区域分为: 堆内存 和 栈内存 ;

Java进阶训练营 第一周JVM 预习笔记_第9张图片


栈保存了调用链上正在执行的方法的局部变量。

每个线程都有一份自己的局部变量副本。


Java进阶训练营 第一周JVM 预习笔记_第10张图片

方法中使用的原生数据类型和对象引用地址在栈上存储;对象、对象成员 与类定义、静态变量在堆上。


虽然各个线程自己使用的局部变量都在自己的栈上,但是大家可以共享堆 上的对象,特别地各个不同线程访问同一个对象实例的基础类型的成员变量,会给每 个线程一个变量的副本。


6.2 栈内存的结构

Java进阶训练营 第一周JVM 预习笔记_第11张图片


6.3 堆内存的结构



堆内存是所有线程共用的内存空间

6.4 CPU指令与乱序执行

CPU的实现都是采用流水线的方式

通过内部调度把这些指令打乱了执行,充分利用流水线资源

6.5 JMM背景

JMM规范明确定义了不同的线程之间,通过哪些方式,在什么时候可以看见其他线程 保存到共享变量中的值;以及在必要时,如何对共享变量的访问进行同步。


6.6 JMM简介



  1. JVM的内存区域分为: 堆内存 和 栈内存 ;
  2. 堆内存的实现可分为两部分: 堆(Heap) 和 非堆(Non‐Heap) ;
  3. 堆主要由GC负责管理,按分代的方式一般分为: 老年代+年轻代;年轻代=新生代
    +存活区;
  4. CPU有一个性能提升的利器: 指令重排序 ;
  5. JMM规范对应的是 JSR133, 现在由Java语言规范和JVM规范来维护;
  6. 内存屏障的分类与作用。


6.7 内存屏障简介

JMM引入了内存屏障机制。

内存屏障可分为 读屏障 和 写屏障 ,用于控制可见性。 常见的 内存屏障 包括:

#LoadLoad
#StoreStore
#LoadStore
#StoreLoad

#LoadLoad , 那么屏障前面的Load指令就一定要先执行完,才能执行 屏障后面的Load指令。 比如我要先把a值写到A字段中,然后再将b值写到B字段对应的内存地址。如果 要严格保障这个顺序,那么就可以在这两个Store指令之间加入一个

#StoreStore 屏障。 遇到

#LoadStore 屏障时, CPU自废武功,短暂屏蔽掉指令重排序功能。

#StoreLoad 屏障, 能确保屏障之前执行的所有store操作,都对其他处理器可 见; 在屏障后面执行的load指令, 都能取得到最新的值。换句话说, 有效阻止屏障 之前的store指令,与屏障之后的load指令乱序 、即使是多核心处理器,在执行这 些操作时的顺序也是一致的。


参考资料

极客时间-Java进阶训练营

你可能感兴趣的:(Java进阶训练营)