本系列相关链接
尚硅谷 宋红康 JVM教程_01_内存与垃圾回收篇——01 (20210103-20210110)
https://blog.youkuaiyun.com/wei198621/article/details/112128852
尚硅谷 宋红康 JVM教程_01_内存与垃圾回收篇——02 (20210111-20210117)
https://blog.youkuaiyun.com/wei198621/article/details/112389917
尚硅谷 宋红康 JVM教程_02_字节码与类的加载篇 (20210118~ )
https://blog.youkuaiyun.com/wei198621/article/details/112760463
todo 3 , 4
目录
尚硅谷 宋红康 JVM教程 学习笔记
https://www.bilibili.com/video/BV1PJ411n7xZ
ppt地址:
https://download.youkuaiyun.com/download/wei198621/14040543
JVM参数列表:
https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
1.Class文件结构
P204-JVM中篇内容概述 11:51
https://www.bilibili.com/video/BV1PJ411n7xZ?p=204
1.Class文件结构
2.字节码指令集与解析举例
3.类的加载过程详解
4.再谈类加载器
本节开始用思维导图
中篇开始关注字节码文件,类加载器
下篇,讲性能监控 、 性能调优
P205-字节码文件的跨平台性 13:12
第一章_尚硅谷_宋红康_Class文件结构.mmap
java语言跨平台,write once,run anywhere
java虚拟机:跨语言的平台 —Java虚拟机 只与遵循JVM规范的“class文件”关联
docs.oracle.com/javase/specs/index.html
P206-了解Java的前端编译器 08:30
java 是半解释 半编译型的语言
javac 是一种能够将JAVA源码编译为字节码的前端编译器,是一种全量编译器。
ECJ编译器: Eclipse Compiler for java ,ECJ ,是一种增量式的编译器,Tomcat中使用ECJ编译器编译JSP文件
AOT: Ahead of Time Compiler ,静态提前编译器。
P207-透过字节码看代码执行细节举例1 11:15
Integer.ValueOf()
Integer Cache : -128~127 ,在这之间的数字,预先加载。
package com.tiza.jvm.chapter21;
/**
* @author leowei
* @date 2021/1/18 - 23:33
*/
public class IntegerTest {
public static void main(String[] args) {
m1();
m2();
m3();
}
/**
*
0 bipush 10
2 invokestatic #5 <java/lang/Integer.valueOf>
5 astore_0
6 bipush 10
8 invokestatic #5 <java/lang/Integer.valueOf>
11 astore_1
12 getstatic #6 <java/lang/System.out>
15 aload_0
16 aload_1
17 if_acmpne 24 (+7)
20 iconst_1
21 goto 25 (+4)
24 iconst_0
25 invokevirtual #7 <java/io/PrintStream.println>
28 return
通过查看字节码文件,看到i1 i2 取值的时候会调用valueOf方法,
如下所示valueOf 方法 会从一个cache 中取数据
Cache 的值 在 -128~127 之间
所以取 -128 ~ 127 之间的数字,不会新建空间,调用cache中的空间就可以了
* @since 1.5
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
这解释了 m1 m2 连个问题
*/
private static void m1() {
Integer i1=10;
Integer i2=10;
System.out.println(i1==i2); //true
}
private static void m2() {
Integer i3=128;
Integer i4=128;
System.out.println(i3==i4); //false
}
/**
*
0 iconst_5
1 invokestatic #5 <java/lang/Integer.valueOf>
4 astore_0
5 iconst_5
6 istore_1
7 getstatic #6 <java/lang/System.out>
10 aload_0
11 invokevirtual #8 <java/lang/Integer.intValue>
14 iload_1
15 if_icmpne 22 (+7)
18 iconst_1
19 goto 23 (+4)
22 iconst_0
23 invokevirtual #7 <java/io/PrintStream.println>
26 return
此处涉及自动装箱拆箱,暂时没有搞懂此时是装箱还是拆箱 20210118
*/
private static void m3() {
Integer x=5;
int y=5;
System.out.println(x==y); //true
}
}
P208-透过字节码看代码执行细节举例2 03:51
package com.tiza.jvm.chapter21;
/**
* @author leowei
* @date 2021/1/18 - 23:55
*/
public class StringTest {
public static void main(String[] args) {
// 还是没有搞懂这块,待认真学习
m1();
m2();
m3();
}
private static void m1() {
String str1 = new String("hello")+new String("world");
String str2 ="helloworld";
System.out.println(str1 == str2); // false str1 在堆中创建 helloworld str2 在 stringtable 中 创建 helloworld
}
private static void m2() {
String str1 = new String("helloworld") ;
String str2 ="helloworld";
System.out.println(str1 == str2); // false str1 在堆中创建 helloworld str2 在 stringtable 中 创建 helloworld
}
private static void m3() {
String str1 = new String("hello")+new String("world");
String str2 = new String("helloworld");
System.out.println(str1 == str2); // false str1 在堆中创建 helloworld str2 在 stringtable 中 创建 helloworld
}
}
P209-透过字节码看代码执行细节举例3 11:52
20210118 todo
- 类的非静态成员变量赋值过程
- 前置1 aload_0 a
- 前置2 父类初始化
- step 1 成员变量 默认初始化 x=0
- step 2 成员变量 显示初始化/代码块中初始化 x=10 后打印
- step 3 构造器初始化 x=20
- step 4 (有了对象后) 对象.属性 赋值
package com.tiza.jvm.chapter21;
/**
*
0 aload_0 0 是this
1 invokespecial #1 <java/lang/Object.<init>> 调用构造器,首先调用父类的初始化方法
4 aload_0
5 bipush 10
7 putfield #2 <com/tiza/jvm/chapter21/Father.x> 给x赋值 为 10
10 aload_0
11 invokevirtual #3 <com/tiza/jvm/chapter21/Father.print> 打印
14 aload_0
15 bipush 20
17 putfield #2 <com/tiza/jvm/chapter21/Father.x> 给x赋值 为 20
20 return
*/
class Father{
int x=10;
public Father(){
this.print();
x=20;
}
public void print(){
System.out.println("Father.x="+x);
}
}
class Son extends Father{
int x=30;
public Son(){
this.print();
x=40;
}
public void print(){
System.out.println("Son.x="+x);
}
}
/**
* @author leowei
* @date 2021/1/19 - 7:12
*/
public class SonTest {
public static void main(String[] args) {
//m1();
m2();
//m3();
}
/**
* 类的非静态成员变量赋值过程
* 前置1 aload_0 a
* 前置2 父类初始化
* step 1 成员变量 默认初始化 x=0
* step 2 成员变量 显示初始化/代码块中初始化 x=10 后打印
* step 3 构造器初始化 x=20
* step 4 (有了对象后) 对象.属性 赋值
* 打印内容
Father.x=10
20
*/
private static void m1() {
Father father = new Father();
System.out.println(father.x);
}
/**
* new Son 先 new Father
Son.x=0
Son.x=30
40 --- 属性不存在多态,所以一定是 20
*/
private static void m2(){
Son son=new Son();
System.out.println(son.x);
}
/**
*
0 aload_0 ---------------------------------------------------- 加载当前 Son 的 this
1 invokespecial #1 <com/tiza/jvm/chapter21/Father.<init>> --- 构造器初始化时候,先init 其父类的(Father) 构造器
--- Father 构造器会执行new Father() 构造器函数中的 this.print()
--- 此时 Father 类中的 this.print() 中的 this 是 Son ,所以会打印 Son.x =0 ;
4 aload_0
5 bipush 30 ---- 注意,此时才赋值30 给X
7 putfield #2 <com/tiza/jvm/chapter21/Son.x>
10 aload_0
11 invokevirtual #3 <com/tiza/jvm/chapter21/Son.print>
14 aload_0
15 bipush 40
17 putfield #2 <com/tiza/jvm/chapter21/Son.x>
20 return
Son.x=0
Son.x=30
20 --- 属性不存在多态,所以一定是 20
*/
private static void m3(){
Father f=new Son();
System.out.println(f.x);
}
}
P210-解读Class文件的三种方式 15:47
字节码是一种二进制的类文件,它的内容是JVM指令,不像C,C++由编译器直接生成机器码
什么是字节码 byte code ?
操作码+ 操作数 = 字节码
opcode + operand = byte code
解读字节码的方式三种:
notepad++ (HEX-Editor 插件)
jclasslib 或者 bytecode viewer
javap 反编译指令
https://github.com/chcg/NPP_HexEdit/releases 下载地址
P211-Class文件本质和内部数据类型 15:58
P212-Class文件内部结构概述 08:00
常量类型结构
第一项,字符串 CONSTANT_utf8_info 比较特别
P213-字节码数据保存到excel中的操作 07:28
P214-Class文件的标识:魔数 06:33
P215-Class文件版本号 09:40
P216-常量池概述 05:50
P217-常量池计数器 03:53
P218-常量池表中的字面量和符号引用 15:49
P219-解析得到常量池中所有的常量 16:34
P220-常量池表数据的解读1 10:31
P221-常量池表数据的解读2 10:53
P222-常量池表项数据的总结 08:13
20210119
P223-访问标识 08:20
访问标识 access_flag ;使用两个字节标识,
P224-类索引、父类索引、接口索引集合 07:41
当前类索引
父类索引
接口计数器
P225-字段表集合的整体理解 08:13
字段计数器,字段数组。
字段是指 类级变量 实例变量 ,不包括方法内部,代码内部的变量
字段表,只包含当前类定义的field,不包括父类中的field
P226-字段表数据的解读 12:01
fields_info1 (字段访问标识+字段名索引+字段描述符索引+属性计数器+属性)
fields_info2
fields_info3
字段表访问标识
P227-方法表集合的整体理解 06:51
方法计数器
方法表
P228-方法表数据的解读 10:48
访问标识
方法名索引
描述符索引
属性计数器
属性集合
方法访问标志
P229-属性表集合的整理理解 04:47
属性计数器
属性数组
P230-方法中Code属性的解读 13:44
JAVA8里面定义了23种属性
属性名索引
属性长度
属性表
P231-LineNumberTable和LocalVariableTable属性的解读 21:34
P232-SourceFile属性的解读 07:45
P233-Class文件结构的小结 03:42
20210120
P234-javac -g操作的说明 13:21
解析Class文件
解析字节码的作用
javac -g 操作
javap 的用法
使用举例
总结
javac xx.java -------- 无局部变量表
javac -g xx.java ------有局部变量表
IDEA EClipse 编译的class 文件使用的是 javac -g ***指令
javac -g localVariableTable
P235-javap主要参数的使用 21:16
javap
javap
javap -help
javap -version
javap -public javacls.class ----显示公共成员变量和方法
javap -protected javacls.class — ***
javap -private javacls.class ----
javap -package javacls.class — 非私有的
javap -sysinfo javacls.class ---- 类的系统信息
javap -constants javacls.class — 静态常量
javap -s javacls.class ---- 内部类型签名
javap -s -p javacls.class — 内部类型签名(私有及以上)
javap -l javacls.class ---- 输出行号和本地变量表
javap -c javacls.class ---- 对代码进行反汇编
javap -v javacls.class ----- 输出附加信息(包括行号,本地变量表,反汇编,也就是 -v= -l + -c )
javap -v -p javaclass.class -----最全的信息 。
P236-javap解析得到的文件结构的解读 21:19
P237-javap使用小结 05:05
不显示类索引,父类索引,接口结合索引,() , () 等结构
字节码指令集与解析举例
字节码指令集与解析举例
01 概述
02 加载与存储指令
03 算数指令
04 类型转换指令
05 对象的创建于访问指令
06 方法调用与返回指令
07 操作数栈指令
08 比较控制指令
P238-字节码指令集的概述 14:33
操作码,助记符,不能超过256
地址是:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html
P239-指令与数据类型的关系及指令分类 10:11
指令地址:https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html
iload , int 加载
fload , float 加载
aload , 引用加载
指令分类
20210121
P240-加载与存储指令概述 08:12
加载: 将数据加载到操作数栈中
存储: 将数据存储到局部变量表
指令(助记符):
https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html
指令的分类
加载与存储指令
算术指令
类型转换指令
对象的创建于访问指令
方法调用与返回指令
操作数栈指令
比较控制指令
异常处理指令
同步控制指令
加载和存储指令
加载指令
存储指令
load push ldc const : 入栈
store : 出栈
局部变量表压栈 : xload_ (x = i/l/f/d/a, n=0/1/2/3)
常量池压栈: bipush/sipush/ldc/ldc_w/iconst_/lconst_/dconst_
出栈装入局部变量表: xstore/xstore_ (x = i/l/f/d/a, n=0/1/2/3)
P241-再谈操作数栈与局部变量表 07:24
操作数栈:存放计算的操作数以及返回结果。
执行每条指令之前,java虚拟机要求该指令的操作数已被压入操作数栈中,在执行指令时,java虚拟机会将该指令所需操作数弹出,并且将指令的结果重新压入栈中。
局部变量表(Local Variables ):
字节码程序将计算的结果缓存在局部变量之中。
java虚拟机将局部变量当成一个数组,依次存放this指针,
局部变量表中
int , float … = 1 slot
long ,double = 2 slot
P242-局部变量压栈指令 08:44
从局部变量中取出数据,放到栈中。
xload (x= i/l/f/d/a)
//局部变量压栈指令
public void load(int num,Object obj,long count,boolean flag,short[] arr){
System.out.println(num);
System.out.println(obj);
System.out.println(count);
System.out.println(flag);
System.out.println(arr);
}
P243-常量入栈指令 14:13
const:
iconst_x (x= 1-5)
int i=3; iconst_3 — 小于5的,一个指令
int j=6; iconst 6 ----大于5的, 指令 + 数字 ,错错错
int j=6; bipush 6 ----大于5的, 指令 + 数字 ,指令为bipush ,这个写法是对的lconst_x ( x=1-2)
fconst_x( x=0-2)
dconst_x( x=0-1)
aconst_nullpush:
bipush: 8位整数 (-128~127)
sipush: 16位整数 (-32768 ~32767)