- 浏览: 77612 次
文章分类
最新评论
-
kevinflynn:
...
ThreadLocal 源码分析 -
kevinflynn:
[url=aaaa][/url]
ThreadLocal 源码分析 -
kevinflynn:
学习到了 感谢楼主。
ThreadLocal 源码分析
我们都知道 *.java 源代码经过编译器编译后会生成 *.class 文件,方法体中的代码会存放在方法表中 Code 属性中(接口和抽象类除外,没有 Code 属性).
Code 属性的结构:
attribute_name_index -> UTF8(Code)
attribute_length
info{
max_stack
max_locals
code_length
code
LineNumberTable
LocalVariableTable
...
}
java 编译器编译后的字节码指令就存放在 code 中.
那么下面我们重点专注下虚拟机的字节码.
我们知道指令是由操作码和操作数组成的,java 虚拟机定义的操作码由一字节表示,这样意味着最多只有256个操作码. class 文件放弃了操作数对齐,那么对于16字节的无符号数,虚拟机必须在运行时重构出该数据,类似 byte1 << 8 | byte2 这样. 所以其会牺牲一部分性能,但是好处同样是显而易见的.不需要多操作数进行补位填充,节省了空间.
java 虚拟机是面向操作数栈而不是寄存器的,所以它的指令大多数是不带操作数的. java 操作码大多都带有特定含义.
下面说下虚拟机是如何工作的:
do{
PC寄存器自动+1
根据寄存器位置读出操作码
if(判断操作码后是否有操作数) 读取操作数
执行相关功能
}while(字节码长度>0)
1.从局部变量读取数据到操作数栈
iload/lload/fload 等
2.把操作数栈的数据放回局部变量表
istore/lstore/fstore 等.
3.把常量读取到操作数栈
bipush/sipush/ldc 等
4.扩充局部变量表访问索引的指令 wide
注:iload_n 代表 iload_0 iload_1 iload_2 iload_3 这几条指令.
运算指令:用于把操作数栈上的两个数进行某种特定运算,然后把结果重新放回栈顶的操作.
1.加法
iadd/ladd/dadd 等
2.减法
isub/lsub/dsub
3.乘法
imul/lmul/dmul
4.除法
idiv/ldiv/ddiv
5.取余
irem/drem
6.or
ior/dor
7.and
iand/dand
8.取反
ineg/dneg
9.局部变量自增指令
iinc
10.比较指令
dcmpg 等
我们知道,两个很大的数据相加,最终会得到负数,这个在纯数学领域是不会的,但是在计算机中是会的(由于溢出),java 虚拟机并没有规定这个错误,只是在进行 div 或 rem 运算的时候,如果除数为 0,则会抛出 ArithmeticException 异常,其余任何整型数运算场景都不应该抛出运行时异常.
显式类型转换:i2b,i2c,l2i等.
隐式类型转换:i ->long/float/double
long -> float/double
float -> double
创建对象和访问指令
1.创建一个对象:new
2.创建数组:
① newarray 创建基本数据类型的数组
② anewarray 创建对象类型数组
③ multianewarray 创建多维数组
3.访问类字段:getfield、putfield、getstatic、putstatic
4.把一个元素数组加载到操作数栈:iaload、baload、laload 等
5.把一个操作数栈中的值存储到数组元素:iastore、lastore 等
6.取数组长度的指令:arraylength
7.检查 instanceof、checkcast
操作数栈管理指令
出栈:pop、pop2
复制栈顶元素:dup、dup2、dup_x1、dup2_x1、dup_x2、dup2_x2
交换:swap
控制转移指令:就是有条件的或无条件的修改 PC 寄存器的值.
1.条件分支:ifeq、iflt 等
2.复合条件分支:tableswitch、lookupswitch
3.无条件分支:goto 等
方法调用和返回指令
(1)invokevirtual 指令用于调用对象的实例方法,根据对象的实际类型进行分派(虚方法分派)
(2)invokeinterface 指令用于调用接口方法,它会在运行时搜索一个实现了这个接口方法的对象,找出合适的方法进行调用.
(3)invokespeical 指令用于调用一些需要特殊处理的实例方法,包括实例初始化方法、私有方法和父类方法.
(4)invokestatic 指令用于调用类方法(static 方法)
(5)invokedynamic 指令用于在运行时动态解析出调用点限定符所引用的方法,并执行该方法,前面4条调用指令的分派逻辑都固化在 Java 虚拟机
内部,而 invokedynamic 指令的分派逻辑是由用户所设定的引导方法决定的.
方法调用指令与数据类型无关,而方法返回指令时根据返回值的类型区分的,包括 ireturn(当返回值是 boolean、byte、char、short、int
类型时使用)、lreturn、freturn、dreturn、areturn,另外还有一条 return 指令供声明为 void 的方法、实例初始化方法以及类和接口
的类初始化方法使用.
异常处理指令
在 Java 程序中显示抛出异常的操作(throw 语句)都由 athrow 指令实现,除了用 throw 语句显式抛出异常情况之外,Java 虚拟机规范还规定
了许多运行时异常会在其他 Java 虚拟机指令检测到异常状况时自动抛出.
在 Java 虚拟机中,处理异常 (catch 语句) 不是由字节码指令来实现的(很久之前曾经使用 jsr 和 ret 指令来实现,现在已经不用了),而
是采用异常表来完成.
同步指令
Java 虚拟机可以支持方法级的同步和方法内部一段指令序列的同步,这两种同步结构都是使用管程(Monitor) 来支持的.
方法级的同步是隐式的,即无需通过字节码指令来控制,它实现在方法调用和返回操作之中. 虚拟机可以从方法常量池的方法表结构中的
ACC_SYNCHRONIZED 访问标志得知一个方法是否是同步方法. 当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置
了,如果设置了,执行线程就要求先成功持有管程,然后才能执行方法,最后当方法完成(无论是正常完成还是非正常完成)时释放管程.
在方法执行期间,执行线程持有了管程,其他任何线程都无法再获取到同一个管程. 如果一个同步方法执行期间抛出了异常,并且在方法内部无法处理
此异常,那么这个同步方法所持有的管程将在异常抛到同步方法之外时自动释放.
关于同步代码块,虚拟机中有 monitorenter 和 monitorexit 两条指令来支持 synchronized 语义. 正确实现 synchrnoized 关键字
需要 javac 编译器与 java 虚拟机两者共同协作支持.
monitorenter 和 monitorexit 指令底层是 通过lock 指令来实现的,在执行 monitorexit 之前,会将数据刷新会主存.
注:编译器必须确保无论方法通过何种方式完成,方法中调用过的每条 monitorenter 指令都必须执行器对应的 monitorexit 指令,而无论这个方法
是正常结束还是异常结束.
换句话说,monitorenter 和 monitorexit 成对出现.
公有设计和私有实现
Java 虚拟机规范描绘了 Java 虚拟机应有的共同程序存储格式:Class 文件格式以及字节码指令集.
虚拟机实现的方式主要有一下两种:
(1)将输入的 Java 虚拟机代码在加载或执行时翻译成另一种虚拟机的指令集(*.java -> *.class)
(2)将输入的 Java 虚拟机代码在加载或执行时翻译成宿主机 CPU 的本地指令集(即 JIT 代码生成技术)(*.java -> 目标指令集)
Class 文件结构的发展
Class 文件格式所具备的平台中立(不依赖于特定硬件及操作系统)、紧凑、稳定和可扩展的特点,是 Java 技术体系实现平台无关、语言无关两项
特性的重压支柱.
字节码指令集(参考:https://www.cnblogs.com/cwane/p/6097838.html):
Opcode
Mnemonics
Note
Constants
0x00
nop
无动作
0x01
aconst_null
把 null 推到操作数栈
0x02
iconst_m1
把 int 常量 –1 推到操作数栈
0x03
iconst_0
把 int 常量 0 推到操作数栈
0x04
iconst_1
把 int 常量 1 推到操作数栈
0x05
iconst_2
把 int 常量 2 推到操作数栈
0x06
iconst_3
把 int 常量 3 推到操作数栈
0x07
iconst_4
把 int 常量 4 推到操作数栈
0x08
iconst_5
把 int 常量 5 推到操作数栈
0x09
lconst_0
把 long 常量 0 推到操作数栈
0x0A
lconst_1
把 long 常量 1 推到操作数栈
0x0B
fconst_0
把 float 常量 0 推到操作数栈
0x0C
fconst_1
把 float 常量 1 推到操作数栈
0x0D
fconst_2
把 float 常量 2 推到操作数栈
0x0E
dconst_0
把 double 常量 0 推到操作数栈
0x0F
dconst_1
把 double 常量 1 推到操作数栈
0x10
bipush
把单字节常量(-128~127)推到操作数栈
0x11
sipush
把 short 常量(-32768~32767)推到操作数栈
0x12
ldc
把常量池中的int,float,String型常量取出并推到操作数栈
0x13
ldc_w
把常量池中的int,float,String型常量取出并推到操作数栈(宽索引)
0x14
ldc2_w
把常量池中的long,double型常量取出并推到操作数栈(宽索引)
Loads
0x15
iload
把 int 型局部变量推到操作数栈
0x16
lload
把 long 型局部变量推到操作数栈
0x17
fload
把 float 型局部变量推到操作数栈
0x18
dload
把 double 型局部变量推到操作数栈
0x19
aload
把引用型局部变量推到操作数栈
0x1A
iload_0
把局部变量第 1 个 int 型局部变量推到操作数栈
0x1B
iload_1
把局部变量第 2 个 int 型局部变量推到操作数栈
0x1C
iload_2
把局部变量第 3 个 int 型局部变量推到操作数栈
0x1D
iload_3
把局部变量第 4 个 int 型局部变量推到操作数栈
0x1E
lload_0
把局部变量第 1 个 long 型局部变量推到操作数栈
0x1F
lload_1
把局部变量第 2 个 long 型局部变量推到操作数栈
0x20
lload_2
把局部变量第 3 个 long 型局部变量推到操作数栈
0x21
lload_3
把局部变量第 4 个 long 型局部变量推到操作数栈
0x22
fload_0
把局部变量第 1 个 float 型局部变量推到操作数栈
0x23
fload_1
把局部变量第 2 个 float 型局部变量推到操作数栈
0x24
fload_2
把局部变量第 3 个 float 型局部变量推到操作数栈
0x25
fload_3
把局部变量第 4 个 float 型局部变量推到操作数栈
0x26
dload_0
把局部变量第 1 个 double 型局部变量推到操作数栈
0x27
dload_1
把局部变量第 2 个 double 型局部变量推到操作数栈
0x28
dload_2
把局部变量第 3 个 double 型局部变量推到操作数栈
0x29
dload_3
把局部变量第 4 个 double 型局部变量推到操作数栈
0x2A
aload_0
把局部变量第 1 个引用型局部变量推到操作数栈
0x2B
aload_1
把局部变量第 2 个引用型局部变量推到操作数栈
0x2C
aload_2
把局部变量第 3 个引用型局部变量推到操作数栈
0x2D
aload_3
把局部变量第 4 个引用 型局部变量推到操作数栈
0x2E
iaload
把 int 型数组指定索引的值推到操作数栈
0x2F
laload
把 long 型数组指定索引的值推到操作数栈
0x30
faload
把 float 型数组指定索引的值推到操作数栈
0x31
daload
把 double 型数组指定索引的值推到操作数栈
0x32
aaload
把引用型数组指定索引的值推到操作数栈
0x33
baload
把 boolean或byte型数组指定索引的值推到操作数栈
0x34
caload
把 char 型数组指定索引的值推到操作数栈
0x35
saload
把 short 型数组指定索引的值推到操作数栈
Stores
0x36
istore
把栈顶 int 型数值存入指定局部变量
0x37
lstore
把栈顶 long 型数值存入指定局部变量
0x38
fstore
把栈顶 float 型数值存入指定局部变量
0x39
dstore
把栈顶 double 型数值存入指定局部变量
0x3A
astore
把栈顶引用型数值存入指定局部变量
0x3B
istore_0
把栈顶 int 型数值存入第 1 个局部变量
0x3C
istore_1
把栈顶 int 型数值存入第 2 个局部变量
0x3D
istore_2
把栈顶 int 型数值存入第 3 个局部变量
0x3E
istore_3
把栈顶 int 型数值存入第 4 个局部变量
0x3F
lstore_0
把栈顶 long 型数值存入第 1 个局部变量
0x40
lstore_1
把栈顶 long 型数值存入第 2 个局部变量
0x41
lstore_2
把栈顶 long 型数值存入第 3 个局部变量
0x42
lstore_3
把栈顶 long 型数值存入第 4 个局部变量
0x43
fstore_0
把栈顶 float 型数值存入第 1 个局部变量
0x44
fstore_1
把栈顶 float 型数值存入第 2 个局部变量
0x45
fstore_2
把栈顶 float 型数值存入第 3 个局部变量
0x46
fstore_3
把栈顶 float 型数值存入第 4 个局部变量
0x47
dstore_0
把栈顶 double 型数值存入第 1 个局部变量
0x48
dstore_1
把栈顶 double 型数值存入第 2 个局部变量
0x49
dstore_2
把栈顶 double 型数值存入第 3 个局部变量
0x4A
dstore_3
把栈顶 double 型数值存入第 4 个局部变量
0x4B
astore_0
把栈顶 引用 型数值存入第 1 个局部变量
0x4C
astore_1
把栈顶 引用 型数值存入第 2 个局部变量
0x4D
astore_2
把栈顶 引用 型数值存入第 3 个局部变量
0x4E
astore_3
把栈顶 引用 型数值存入第 4 个局部变量
0x4F
iastore
把栈顶 int 型数值存入数组指定索引位置
0x50
lastore
把栈顶 long 型数值存入数组指定索引位置
0x51
fastore
把栈顶 float 型数值存入数组指定索引位置
0x52
dastore
把栈顶 double 型数值存入数组指定索引位置
0x53
aastore
把栈顶 引用 型数值存入数组指定索引位置
0x54
bastore
把栈顶 boolean or byte 型数值存入数组指定索引位置
0x55
castore
把栈顶 char 型数值存入数组指定索引位置
0x56
sastore
把栈顶 short 型数值存入数组指定索引位置
Stack
0x57
pop
把栈顶数值弹出(非long,double数值)
0x58
pop2
把栈顶的一个long或double值弹出,或弹出2个其他类型数值
0x59
dup
复制栈顶数值并把数值入栈
0x5A
dup_x1
复制栈顶数值并把数值入栈
0x5B
dup_x2
0x5C
dup2
0x5D
dup2_x1
0x5E
dup2_x2
0x5F
swap
把栈顶端的两个数的值交换
Math
0x60
iadd
把栈顶两个 int 型数值相加并将结果入栈
0x61
ladd
把栈顶两个 long 型数值相加并将结果入栈
0x62
fadd
把栈顶两个 float 型数值相加并将结果入栈
0x63
dadd
把栈顶两个 double 型数值相加并将结果入栈
0x64
isub
把栈顶两个 int 型数值相减并将结果入栈
0x65
lsub
把栈顶两个 long 型数值相减并将结果入栈
0x66
fsub
把栈顶两个 float 型数值相减并将结果入栈
0x67
dsub
把栈顶两个 double 型数值相减并将结果入栈
0x68
imul
把栈顶两个 int 型数值相乘并将结果入栈
0x69
lmul
把栈顶两个 long 型数值相乘并将结果入栈
0x6A
fmul
把栈顶两个 float 型数值相乘并将结果入栈
0x6B
dmul
把栈顶两个 double 型数值相乘并将结果入栈
0x6C
idiv
把栈顶两个 int 型数值相除并将结果入栈
0x6D
ldiv
把栈顶两个 long 型数值相除并将结果入栈
0x6E
fdiv
把栈顶两个 float 型数值相除并将结果入栈
0x6F
ddiv
把栈顶两个 double 型数值相除并将结果入栈
0x70
irem
把栈顶两个 int 型数值模运算并将结果入栈
0x71
lrem
把栈顶两个 long 型数值模运算并将结果入栈
0x72
frem
把栈顶两个 float 型数值模运算并将结果入栈
0x73
drem
把栈顶两个 double 型数值模运算并将结果入栈
0x74
ineg
把栈顶 int 型数值取负并将结果入栈
0x75
lneg
把栈顶 long 型数值取负并将结果入栈
0x76
fneg
把栈顶 float 型数值取负并将结果入栈
0x77
dneg
把栈顶 double 型数值取负并将结果入栈
0x78
ishl
把 int 型数左移指定位数并将结果入栈
0x79
lshl
把 long 型数左移指定位数并将结果入栈
0x7A
ishr
把 int 型数右移指定位数并将结果入栈(有符号)
0x7B
lshr
把 long 型数右移指定位数并将结果入栈(有符号)
0x7C
iushr
把 int 型数右移指定位数并将结果入栈(无符号)
0x7D
lushr
把 long 型数右移指定位数并将结果入栈(无符号)
0x7E
iand
把栈顶两个 int 型数值 按位与 并将结果入栈
0x7F
land
把栈顶两个 long 型数值 按位与 并将结果入栈
0x80
ior
把栈顶两个 int 型数值 按位或 并将结果入栈
0x81
lor
把栈顶两个 long 型数值 按或与 并将结果入栈
0x82
ixor
把栈顶两个 int 型数值 按位异或 并将结果入栈
0x83
lxor
把栈顶两个 long 型数值 按位异或 并将结果入栈
0x84
iinc
把指定 int 型增加指定值
Conversions
0x85
i2l
把栈顶 int 强转 long 并入栈
0x86
i2f
把栈顶 int 强转 float 并入栈
0x87
i2d
把栈顶 int 强转 double 并入栈
0x88
l2i
把栈顶 long 强转 int 并入栈
0x89
l2f
把栈顶 long 强转 float 并入栈
0x8A
l2d
把栈顶 long 强转 double 并入栈
0x8B
f2i
把栈顶 float 强转 int 并入栈
0x8C
f2l
把栈顶 float 强转 long 并入栈
0x8D
f2d
把栈顶 float 强转 double 并入栈
0x8E
d2i
把栈顶 double 强转 int 并入栈
0x8F
d2l
把栈顶 double 强转 long 并入栈
0x90
d2f
把栈顶 double 强转 float 并入栈
0x91
i2b
把栈顶 int 强转 byte 并入栈
0x92
i2c
把栈顶 int 强转 char 并入栈
0x93
i2s
把栈顶 int 强转 short 并入栈
Comparisons
0x94
lcmp
比较栈顶两个long 型数值,把结果入栈(-1 or 0 or 1)
0x95
fcmpl
比较栈顶两个 float 型数值,把结果入栈,若有 NaN,入栈 -1
0x96
fcmpg
比较栈顶两个 float 型数值,把结果入栈,若有 NaN,入栈 1
0x97
dcmpl
比较栈顶两个 double 型数值,把结果入栈,若有 NaN,入栈 -1
0x98
dcmpg
比较栈顶两个 double 型数值,把结果入栈,若有 NaN,入栈 -1
0x99
ifeq
当栈顶 int 型数值等于0时,跳转
0x9A
ifne
当栈顶 int 型数值不等于0时,跳转
0x9B
iflt
当栈顶 int 型数值小于0时,跳转
0x9C
ifge
当栈顶 int 型数值大于等于0时,跳转
0x9D
ifgt
当栈顶 int 型数值大于0时,跳转
0x9E
ifle
当栈顶 int 型数值小于等于0时,跳转
0x9F
if_icmpeq
比较栈顶两个 int 型数值,等于0时,跳转
0xA0
if_icmpne
比较栈顶两个 int 型数值,不等于0时,跳转
0xA1
if_icmplt
比较栈顶两个 int 型数值,小于0时,跳转
0xA2
if_icmpge
比较栈顶两个 int 型数值,大于等于0时,跳转
0xA3
if_icmpgt
比较栈顶两个 int 型数值,大于0时,跳转
0xA4
if_icmple
比较栈顶两个 int 型数值,小于等于0时,跳转
0xA5
if_acmpeq
比较栈顶两个 引用 型数值,相等时跳转
0xA6
if_acmpne
比较栈顶两个 引用 型数值,不相等时跳转
Control
0xA7
goto
无条件跳转
0xA8
jsr
跳转指定16bit偏移位置,并将jsr下一条指令地址入栈
0xA9
ret
返回局部变量指定index指定位置,与jsr,jsr_w配合使用
0xAA
tableswitch
switch跳转,case连续
0xAB
lookupswitch
switch跳转,case不连续
0xAC
ireturn
从当前方法返回 int
0xAD
lreturn
从当前方法返回 long
0xAE
freturn
从当前方法返回 float
0xAF
dreturn
从当前方法返回 double
0xB0
areturn
从当前方法返回 对象引用
0xB1
return
从当前方法返回 void
References
0xB2
getstatic
获取类的静态域,并将值入栈顶
0xB3
putstatic
为类的静态域赋值
0xB4
getfield
获取类的实例域,并将值入栈顶
0xB5
putfield
为类的实例域赋值
0xB6
invokevirtual
调用实例方法
0xB7
invokespecial
调用父类构造方法,实例初始化方法,私有方法
0xB8
invokestatic
调用静态方法
0xB9
invokeinterface
调用接口方法
0xBA
invokedynamic
调用动态链接方法
0xBB
new
创建一个对象,并将引用值入栈
0xBC
newarray
创建一个原始类型数组,并将引用值入栈
0xBD
anewarray
创建一个引用类型数组,并将引用值入栈
0xBE
arraylength
获取数组长度并入栈
0xBF
athrow
抛出栈顶异常
0xC0
checkcast
检验类型转换
0xC1
instanceof
检验是否是类的实例,是1入栈,否0入栈
0xC2
monitorenter
获取对象的monitor,用于同步块或方法
0xC3
monitorexit
释放对象的monitor,用于同步块或方法
Extended
0xC4
wide
扩展访问局部变量表的索引宽度
0xC5
multianewarray
创建多维数组,并将引用值入栈
0xC6
ifnull
为 null 时跳转
0xC7
ifnonnull
非 null 时跳转
0xC8
goto_w
无条件跳转(宽索引)
0xC9
jsr_w
跳转指定32bit偏移位置,并将jsr_w下一条指令地址入栈
Reserved
0xCA
breakpoint
调试时的断点
0xFE
impdep1
用于在特定硬件中使用的语言后门
0xFF
impdep2
用于在特定硬件中使用的语言后门
参考:
① https://www.cnblogs.com/cwane/p/6097838.html
Code 属性的结构:
attribute_name_index -> UTF8(Code)
attribute_length
info{
max_stack
max_locals
code_length
code
LineNumberTable
LocalVariableTable
...
}
java 编译器编译后的字节码指令就存放在 code 中.
那么下面我们重点专注下虚拟机的字节码.
我们知道指令是由操作码和操作数组成的,java 虚拟机定义的操作码由一字节表示,这样意味着最多只有256个操作码. class 文件放弃了操作数对齐,那么对于16字节的无符号数,虚拟机必须在运行时重构出该数据,类似 byte1 << 8 | byte2 这样. 所以其会牺牲一部分性能,但是好处同样是显而易见的.不需要多操作数进行补位填充,节省了空间.
java 虚拟机是面向操作数栈而不是寄存器的,所以它的指令大多数是不带操作数的. java 操作码大多都带有特定含义.
下面说下虚拟机是如何工作的:
do{
PC寄存器自动+1
根据寄存器位置读出操作码
if(判断操作码后是否有操作数) 读取操作数
执行相关功能
}while(字节码长度>0)
1.从局部变量读取数据到操作数栈
iload/lload/fload 等
2.把操作数栈的数据放回局部变量表
istore/lstore/fstore 等.
3.把常量读取到操作数栈
bipush/sipush/ldc 等
4.扩充局部变量表访问索引的指令 wide
注:iload_n 代表 iload_0 iload_1 iload_2 iload_3 这几条指令.
运算指令:用于把操作数栈上的两个数进行某种特定运算,然后把结果重新放回栈顶的操作.
1.加法
iadd/ladd/dadd 等
2.减法
isub/lsub/dsub
3.乘法
imul/lmul/dmul
4.除法
idiv/ldiv/ddiv
5.取余
irem/drem
6.or
ior/dor
7.and
iand/dand
8.取反
ineg/dneg
9.局部变量自增指令
iinc
10.比较指令
dcmpg 等
我们知道,两个很大的数据相加,最终会得到负数,这个在纯数学领域是不会的,但是在计算机中是会的(由于溢出),java 虚拟机并没有规定这个错误,只是在进行 div 或 rem 运算的时候,如果除数为 0,则会抛出 ArithmeticException 异常,其余任何整型数运算场景都不应该抛出运行时异常.
显式类型转换:i2b,i2c,l2i等.
隐式类型转换:i ->long/float/double
long -> float/double
float -> double
创建对象和访问指令
1.创建一个对象:new
2.创建数组:
① newarray 创建基本数据类型的数组
② anewarray 创建对象类型数组
③ multianewarray 创建多维数组
3.访问类字段:getfield、putfield、getstatic、putstatic
4.把一个元素数组加载到操作数栈:iaload、baload、laload 等
5.把一个操作数栈中的值存储到数组元素:iastore、lastore 等
6.取数组长度的指令:arraylength
7.检查 instanceof、checkcast
操作数栈管理指令
出栈:pop、pop2
复制栈顶元素:dup、dup2、dup_x1、dup2_x1、dup_x2、dup2_x2
交换:swap
控制转移指令:就是有条件的或无条件的修改 PC 寄存器的值.
1.条件分支:ifeq、iflt 等
2.复合条件分支:tableswitch、lookupswitch
3.无条件分支:goto 等
方法调用和返回指令
(1)invokevirtual 指令用于调用对象的实例方法,根据对象的实际类型进行分派(虚方法分派)
(2)invokeinterface 指令用于调用接口方法,它会在运行时搜索一个实现了这个接口方法的对象,找出合适的方法进行调用.
(3)invokespeical 指令用于调用一些需要特殊处理的实例方法,包括实例初始化方法、私有方法和父类方法.
(4)invokestatic 指令用于调用类方法(static 方法)
(5)invokedynamic 指令用于在运行时动态解析出调用点限定符所引用的方法,并执行该方法,前面4条调用指令的分派逻辑都固化在 Java 虚拟机
内部,而 invokedynamic 指令的分派逻辑是由用户所设定的引导方法决定的.
方法调用指令与数据类型无关,而方法返回指令时根据返回值的类型区分的,包括 ireturn(当返回值是 boolean、byte、char、short、int
类型时使用)、lreturn、freturn、dreturn、areturn,另外还有一条 return 指令供声明为 void 的方法、实例初始化方法以及类和接口
的类初始化方法使用.
异常处理指令
在 Java 程序中显示抛出异常的操作(throw 语句)都由 athrow 指令实现,除了用 throw 语句显式抛出异常情况之外,Java 虚拟机规范还规定
了许多运行时异常会在其他 Java 虚拟机指令检测到异常状况时自动抛出.
在 Java 虚拟机中,处理异常 (catch 语句) 不是由字节码指令来实现的(很久之前曾经使用 jsr 和 ret 指令来实现,现在已经不用了),而
是采用异常表来完成.
同步指令
Java 虚拟机可以支持方法级的同步和方法内部一段指令序列的同步,这两种同步结构都是使用管程(Monitor) 来支持的.
方法级的同步是隐式的,即无需通过字节码指令来控制,它实现在方法调用和返回操作之中. 虚拟机可以从方法常量池的方法表结构中的
ACC_SYNCHRONIZED 访问标志得知一个方法是否是同步方法. 当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置
了,如果设置了,执行线程就要求先成功持有管程,然后才能执行方法,最后当方法完成(无论是正常完成还是非正常完成)时释放管程.
在方法执行期间,执行线程持有了管程,其他任何线程都无法再获取到同一个管程. 如果一个同步方法执行期间抛出了异常,并且在方法内部无法处理
此异常,那么这个同步方法所持有的管程将在异常抛到同步方法之外时自动释放.
关于同步代码块,虚拟机中有 monitorenter 和 monitorexit 两条指令来支持 synchronized 语义. 正确实现 synchrnoized 关键字
需要 javac 编译器与 java 虚拟机两者共同协作支持.
monitorenter 和 monitorexit 指令底层是 通过lock 指令来实现的,在执行 monitorexit 之前,会将数据刷新会主存.
注:编译器必须确保无论方法通过何种方式完成,方法中调用过的每条 monitorenter 指令都必须执行器对应的 monitorexit 指令,而无论这个方法
是正常结束还是异常结束.
换句话说,monitorenter 和 monitorexit 成对出现.
公有设计和私有实现
Java 虚拟机规范描绘了 Java 虚拟机应有的共同程序存储格式:Class 文件格式以及字节码指令集.
虚拟机实现的方式主要有一下两种:
(1)将输入的 Java 虚拟机代码在加载或执行时翻译成另一种虚拟机的指令集(*.java -> *.class)
(2)将输入的 Java 虚拟机代码在加载或执行时翻译成宿主机 CPU 的本地指令集(即 JIT 代码生成技术)(*.java -> 目标指令集)
Class 文件结构的发展
Class 文件格式所具备的平台中立(不依赖于特定硬件及操作系统)、紧凑、稳定和可扩展的特点,是 Java 技术体系实现平台无关、语言无关两项
特性的重压支柱.
字节码指令集(参考:https://www.cnblogs.com/cwane/p/6097838.html):
Opcode
Mnemonics
Note
Constants
0x00
nop
无动作
0x01
aconst_null
把 null 推到操作数栈
0x02
iconst_m1
把 int 常量 –1 推到操作数栈
0x03
iconst_0
把 int 常量 0 推到操作数栈
0x04
iconst_1
把 int 常量 1 推到操作数栈
0x05
iconst_2
把 int 常量 2 推到操作数栈
0x06
iconst_3
把 int 常量 3 推到操作数栈
0x07
iconst_4
把 int 常量 4 推到操作数栈
0x08
iconst_5
把 int 常量 5 推到操作数栈
0x09
lconst_0
把 long 常量 0 推到操作数栈
0x0A
lconst_1
把 long 常量 1 推到操作数栈
0x0B
fconst_0
把 float 常量 0 推到操作数栈
0x0C
fconst_1
把 float 常量 1 推到操作数栈
0x0D
fconst_2
把 float 常量 2 推到操作数栈
0x0E
dconst_0
把 double 常量 0 推到操作数栈
0x0F
dconst_1
把 double 常量 1 推到操作数栈
0x10
bipush
把单字节常量(-128~127)推到操作数栈
0x11
sipush
把 short 常量(-32768~32767)推到操作数栈
0x12
ldc
把常量池中的int,float,String型常量取出并推到操作数栈
0x13
ldc_w
把常量池中的int,float,String型常量取出并推到操作数栈(宽索引)
0x14
ldc2_w
把常量池中的long,double型常量取出并推到操作数栈(宽索引)
Loads
0x15
iload
把 int 型局部变量推到操作数栈
0x16
lload
把 long 型局部变量推到操作数栈
0x17
fload
把 float 型局部变量推到操作数栈
0x18
dload
把 double 型局部变量推到操作数栈
0x19
aload
把引用型局部变量推到操作数栈
0x1A
iload_0
把局部变量第 1 个 int 型局部变量推到操作数栈
0x1B
iload_1
把局部变量第 2 个 int 型局部变量推到操作数栈
0x1C
iload_2
把局部变量第 3 个 int 型局部变量推到操作数栈
0x1D
iload_3
把局部变量第 4 个 int 型局部变量推到操作数栈
0x1E
lload_0
把局部变量第 1 个 long 型局部变量推到操作数栈
0x1F
lload_1
把局部变量第 2 个 long 型局部变量推到操作数栈
0x20
lload_2
把局部变量第 3 个 long 型局部变量推到操作数栈
0x21
lload_3
把局部变量第 4 个 long 型局部变量推到操作数栈
0x22
fload_0
把局部变量第 1 个 float 型局部变量推到操作数栈
0x23
fload_1
把局部变量第 2 个 float 型局部变量推到操作数栈
0x24
fload_2
把局部变量第 3 个 float 型局部变量推到操作数栈
0x25
fload_3
把局部变量第 4 个 float 型局部变量推到操作数栈
0x26
dload_0
把局部变量第 1 个 double 型局部变量推到操作数栈
0x27
dload_1
把局部变量第 2 个 double 型局部变量推到操作数栈
0x28
dload_2
把局部变量第 3 个 double 型局部变量推到操作数栈
0x29
dload_3
把局部变量第 4 个 double 型局部变量推到操作数栈
0x2A
aload_0
把局部变量第 1 个引用型局部变量推到操作数栈
0x2B
aload_1
把局部变量第 2 个引用型局部变量推到操作数栈
0x2C
aload_2
把局部变量第 3 个引用型局部变量推到操作数栈
0x2D
aload_3
把局部变量第 4 个引用 型局部变量推到操作数栈
0x2E
iaload
把 int 型数组指定索引的值推到操作数栈
0x2F
laload
把 long 型数组指定索引的值推到操作数栈
0x30
faload
把 float 型数组指定索引的值推到操作数栈
0x31
daload
把 double 型数组指定索引的值推到操作数栈
0x32
aaload
把引用型数组指定索引的值推到操作数栈
0x33
baload
把 boolean或byte型数组指定索引的值推到操作数栈
0x34
caload
把 char 型数组指定索引的值推到操作数栈
0x35
saload
把 short 型数组指定索引的值推到操作数栈
Stores
0x36
istore
把栈顶 int 型数值存入指定局部变量
0x37
lstore
把栈顶 long 型数值存入指定局部变量
0x38
fstore
把栈顶 float 型数值存入指定局部变量
0x39
dstore
把栈顶 double 型数值存入指定局部变量
0x3A
astore
把栈顶引用型数值存入指定局部变量
0x3B
istore_0
把栈顶 int 型数值存入第 1 个局部变量
0x3C
istore_1
把栈顶 int 型数值存入第 2 个局部变量
0x3D
istore_2
把栈顶 int 型数值存入第 3 个局部变量
0x3E
istore_3
把栈顶 int 型数值存入第 4 个局部变量
0x3F
lstore_0
把栈顶 long 型数值存入第 1 个局部变量
0x40
lstore_1
把栈顶 long 型数值存入第 2 个局部变量
0x41
lstore_2
把栈顶 long 型数值存入第 3 个局部变量
0x42
lstore_3
把栈顶 long 型数值存入第 4 个局部变量
0x43
fstore_0
把栈顶 float 型数值存入第 1 个局部变量
0x44
fstore_1
把栈顶 float 型数值存入第 2 个局部变量
0x45
fstore_2
把栈顶 float 型数值存入第 3 个局部变量
0x46
fstore_3
把栈顶 float 型数值存入第 4 个局部变量
0x47
dstore_0
把栈顶 double 型数值存入第 1 个局部变量
0x48
dstore_1
把栈顶 double 型数值存入第 2 个局部变量
0x49
dstore_2
把栈顶 double 型数值存入第 3 个局部变量
0x4A
dstore_3
把栈顶 double 型数值存入第 4 个局部变量
0x4B
astore_0
把栈顶 引用 型数值存入第 1 个局部变量
0x4C
astore_1
把栈顶 引用 型数值存入第 2 个局部变量
0x4D
astore_2
把栈顶 引用 型数值存入第 3 个局部变量
0x4E
astore_3
把栈顶 引用 型数值存入第 4 个局部变量
0x4F
iastore
把栈顶 int 型数值存入数组指定索引位置
0x50
lastore
把栈顶 long 型数值存入数组指定索引位置
0x51
fastore
把栈顶 float 型数值存入数组指定索引位置
0x52
dastore
把栈顶 double 型数值存入数组指定索引位置
0x53
aastore
把栈顶 引用 型数值存入数组指定索引位置
0x54
bastore
把栈顶 boolean or byte 型数值存入数组指定索引位置
0x55
castore
把栈顶 char 型数值存入数组指定索引位置
0x56
sastore
把栈顶 short 型数值存入数组指定索引位置
Stack
0x57
pop
把栈顶数值弹出(非long,double数值)
0x58
pop2
把栈顶的一个long或double值弹出,或弹出2个其他类型数值
0x59
dup
复制栈顶数值并把数值入栈
0x5A
dup_x1
复制栈顶数值并把数值入栈
0x5B
dup_x2
0x5C
dup2
0x5D
dup2_x1
0x5E
dup2_x2
0x5F
swap
把栈顶端的两个数的值交换
Math
0x60
iadd
把栈顶两个 int 型数值相加并将结果入栈
0x61
ladd
把栈顶两个 long 型数值相加并将结果入栈
0x62
fadd
把栈顶两个 float 型数值相加并将结果入栈
0x63
dadd
把栈顶两个 double 型数值相加并将结果入栈
0x64
isub
把栈顶两个 int 型数值相减并将结果入栈
0x65
lsub
把栈顶两个 long 型数值相减并将结果入栈
0x66
fsub
把栈顶两个 float 型数值相减并将结果入栈
0x67
dsub
把栈顶两个 double 型数值相减并将结果入栈
0x68
imul
把栈顶两个 int 型数值相乘并将结果入栈
0x69
lmul
把栈顶两个 long 型数值相乘并将结果入栈
0x6A
fmul
把栈顶两个 float 型数值相乘并将结果入栈
0x6B
dmul
把栈顶两个 double 型数值相乘并将结果入栈
0x6C
idiv
把栈顶两个 int 型数值相除并将结果入栈
0x6D
ldiv
把栈顶两个 long 型数值相除并将结果入栈
0x6E
fdiv
把栈顶两个 float 型数值相除并将结果入栈
0x6F
ddiv
把栈顶两个 double 型数值相除并将结果入栈
0x70
irem
把栈顶两个 int 型数值模运算并将结果入栈
0x71
lrem
把栈顶两个 long 型数值模运算并将结果入栈
0x72
frem
把栈顶两个 float 型数值模运算并将结果入栈
0x73
drem
把栈顶两个 double 型数值模运算并将结果入栈
0x74
ineg
把栈顶 int 型数值取负并将结果入栈
0x75
lneg
把栈顶 long 型数值取负并将结果入栈
0x76
fneg
把栈顶 float 型数值取负并将结果入栈
0x77
dneg
把栈顶 double 型数值取负并将结果入栈
0x78
ishl
把 int 型数左移指定位数并将结果入栈
0x79
lshl
把 long 型数左移指定位数并将结果入栈
0x7A
ishr
把 int 型数右移指定位数并将结果入栈(有符号)
0x7B
lshr
把 long 型数右移指定位数并将结果入栈(有符号)
0x7C
iushr
把 int 型数右移指定位数并将结果入栈(无符号)
0x7D
lushr
把 long 型数右移指定位数并将结果入栈(无符号)
0x7E
iand
把栈顶两个 int 型数值 按位与 并将结果入栈
0x7F
land
把栈顶两个 long 型数值 按位与 并将结果入栈
0x80
ior
把栈顶两个 int 型数值 按位或 并将结果入栈
0x81
lor
把栈顶两个 long 型数值 按或与 并将结果入栈
0x82
ixor
把栈顶两个 int 型数值 按位异或 并将结果入栈
0x83
lxor
把栈顶两个 long 型数值 按位异或 并将结果入栈
0x84
iinc
把指定 int 型增加指定值
Conversions
0x85
i2l
把栈顶 int 强转 long 并入栈
0x86
i2f
把栈顶 int 强转 float 并入栈
0x87
i2d
把栈顶 int 强转 double 并入栈
0x88
l2i
把栈顶 long 强转 int 并入栈
0x89
l2f
把栈顶 long 强转 float 并入栈
0x8A
l2d
把栈顶 long 强转 double 并入栈
0x8B
f2i
把栈顶 float 强转 int 并入栈
0x8C
f2l
把栈顶 float 强转 long 并入栈
0x8D
f2d
把栈顶 float 强转 double 并入栈
0x8E
d2i
把栈顶 double 强转 int 并入栈
0x8F
d2l
把栈顶 double 强转 long 并入栈
0x90
d2f
把栈顶 double 强转 float 并入栈
0x91
i2b
把栈顶 int 强转 byte 并入栈
0x92
i2c
把栈顶 int 强转 char 并入栈
0x93
i2s
把栈顶 int 强转 short 并入栈
Comparisons
0x94
lcmp
比较栈顶两个long 型数值,把结果入栈(-1 or 0 or 1)
0x95
fcmpl
比较栈顶两个 float 型数值,把结果入栈,若有 NaN,入栈 -1
0x96
fcmpg
比较栈顶两个 float 型数值,把结果入栈,若有 NaN,入栈 1
0x97
dcmpl
比较栈顶两个 double 型数值,把结果入栈,若有 NaN,入栈 -1
0x98
dcmpg
比较栈顶两个 double 型数值,把结果入栈,若有 NaN,入栈 -1
0x99
ifeq
当栈顶 int 型数值等于0时,跳转
0x9A
ifne
当栈顶 int 型数值不等于0时,跳转
0x9B
iflt
当栈顶 int 型数值小于0时,跳转
0x9C
ifge
当栈顶 int 型数值大于等于0时,跳转
0x9D
ifgt
当栈顶 int 型数值大于0时,跳转
0x9E
ifle
当栈顶 int 型数值小于等于0时,跳转
0x9F
if_icmpeq
比较栈顶两个 int 型数值,等于0时,跳转
0xA0
if_icmpne
比较栈顶两个 int 型数值,不等于0时,跳转
0xA1
if_icmplt
比较栈顶两个 int 型数值,小于0时,跳转
0xA2
if_icmpge
比较栈顶两个 int 型数值,大于等于0时,跳转
0xA3
if_icmpgt
比较栈顶两个 int 型数值,大于0时,跳转
0xA4
if_icmple
比较栈顶两个 int 型数值,小于等于0时,跳转
0xA5
if_acmpeq
比较栈顶两个 引用 型数值,相等时跳转
0xA6
if_acmpne
比较栈顶两个 引用 型数值,不相等时跳转
Control
0xA7
goto
无条件跳转
0xA8
jsr
跳转指定16bit偏移位置,并将jsr下一条指令地址入栈
0xA9
ret
返回局部变量指定index指定位置,与jsr,jsr_w配合使用
0xAA
tableswitch
switch跳转,case连续
0xAB
lookupswitch
switch跳转,case不连续
0xAC
ireturn
从当前方法返回 int
0xAD
lreturn
从当前方法返回 long
0xAE
freturn
从当前方法返回 float
0xAF
dreturn
从当前方法返回 double
0xB0
areturn
从当前方法返回 对象引用
0xB1
return
从当前方法返回 void
References
0xB2
getstatic
获取类的静态域,并将值入栈顶
0xB3
putstatic
为类的静态域赋值
0xB4
getfield
获取类的实例域,并将值入栈顶
0xB5
putfield
为类的实例域赋值
0xB6
invokevirtual
调用实例方法
0xB7
invokespecial
调用父类构造方法,实例初始化方法,私有方法
0xB8
invokestatic
调用静态方法
0xB9
invokeinterface
调用接口方法
0xBA
invokedynamic
调用动态链接方法
0xBB
new
创建一个对象,并将引用值入栈
0xBC
newarray
创建一个原始类型数组,并将引用值入栈
0xBD
anewarray
创建一个引用类型数组,并将引用值入栈
0xBE
arraylength
获取数组长度并入栈
0xBF
athrow
抛出栈顶异常
0xC0
checkcast
检验类型转换
0xC1
instanceof
检验是否是类的实例,是1入栈,否0入栈
0xC2
monitorenter
获取对象的monitor,用于同步块或方法
0xC3
monitorexit
释放对象的monitor,用于同步块或方法
Extended
0xC4
wide
扩展访问局部变量表的索引宽度
0xC5
multianewarray
创建多维数组,并将引用值入栈
0xC6
ifnull
为 null 时跳转
0xC7
ifnonnull
非 null 时跳转
0xC8
goto_w
无条件跳转(宽索引)
0xC9
jsr_w
跳转指定32bit偏移位置,并将jsr_w下一条指令地址入栈
Reserved
0xCA
breakpoint
调试时的断点
0xFE
impdep1
用于在特定硬件中使用的语言后门
0xFF
impdep2
用于在特定硬件中使用的语言后门
参考:
① https://www.cnblogs.com/cwane/p/6097838.html
发表评论
-
虚拟机字节码执行过程
2020-02-29 17:39 577先看个例子吧. 方法定 ... -
tomcat 类加载机制
2020-02-29 16:36 6821.Tomcat 类加载器架构 Tomcat 服务器类加载要 ... -
虚拟机栈帧的结构
2020-02-24 21:30 507执行引擎是 java 虚拟机 ... -
虚拟机之双亲委托模型
2020-02-23 16:58 351类加载器 虚拟机设计 ... -
虚拟机内加载过程之解析和初始化阶段
2020-02-22 19:19 422解析阶段 解析阶段是 ... -
虚拟机类加载过程(二)
2020-02-21 12:52 409类加载过程 也就是加载、验证、准备、解析、初始化这5阶段的具 ... -
虚拟机类的加载机制
2020-02-20 11:19 338上篇文章我们学习了 Cla ... -
Java虚拟机的平台无关性
2020-02-17 19:38 321我们知道,java 语言一次编写,到处运行,那它是如何做到的了 ... -
Java对象大小的计算方式
2020-02-13 12:13 500首先我们需要知道的是 Java 对象是包含三部分数据的: 1. ... -
Java 监控工具介绍
2020-02-13 11:25 5361.jps 查看java 虚拟机进程 2.jstat 查看 ... -
Java内存模型-内存间的交互
2020-02-04 15:30 5123.内存间的交互操作 看 ... -
虚拟机性能监控与故障处理工具
2019-12-01 21:08 469说明:本篇文章是在阅读《深入理解Java虚拟机》过程中的一些笔 ... -
Java 内存管理
2019-11-09 19:35 524说明:本篇文章是在阅 ... -
基于句柄方式访问Java对象为啥会使用两个指针?
2019-11-09 16:12 380基于句柄方式访问Java对象为啥会使用两个指针? 我觉得一个 ...
相关推荐
功能说明: 本系统主要包括以下功能模块:个人中心,通知公告管理,用户管理,工作人员管理,进门登记管理,出门登记管理,出入统计管理,外来登记管理等模块。 环境说明: 开发语言:Java 框架:ssm,mybatis JDK版本:JDK1.8 数据库:mysql 5.7及以上 数据库工具:Navicat11及以上 开发软件:eclipse/idea Maven包:Maven3.3及以上 服务器:tomcat7及以上
PartSegCore_compiled_backend-0.12.0a0-cp36-cp36m-win_amd64.whl.rar
yolo算法-分神驾驶数据集-8674张图像带标签-没有安全带-唤醒-昏昏欲睡-安全带-电话-打哈欠.zip;yolo算法-分神驾驶数据集-8674张图像带标签-没有安全带-唤醒-昏昏欲睡-安全带-电话-打哈欠.zip;yolo算法-分神驾驶数据集-8674张图像带标签-没有安全带-唤醒-昏昏欲睡-安全带-电话-打哈欠.zip;yolo算法-分神驾驶数据集-8674张图像带标签-没有安全带-唤醒-昏昏欲睡-安全带-电话-打哈欠.zip
python whl离线安装包 pip安装失败可以尝试使用whl离线安装包安装 第一步 下载whl文件,注意需要与python版本配套 python版本号、32位64位、arm或amd64均有区别 第二步 使用pip install XXXXX.whl 命令安装,如果whl路径不在cmd窗口当前目录下,需要带上路径 WHL文件是以Wheel格式保存的Python安装包, Wheel是Python发行版的标准内置包格式。 在本质上是一个压缩包,WHL文件中包含了Python安装的py文件和元数据,以及经过编译的pyd文件, 这样就使得它可以在不具备编译环境的条件下,安装适合自己python版本的库文件。 如果要查看WHL文件的内容,可以把.whl后缀名改成.zip,使用解压软件(如WinRAR、WinZIP)解压打开即可查看。 为什么会用到whl文件来安装python库文件呢? 在python的使用过程中,我们免不了要经常通过pip来安装自己所需要的包, 大部分的包基本都能正常安装,但是总会遇到有那么一些包因为各种各样的问题导致安装不了的。 这时我们就可以通过尝试去Python安装包大全中(whl包下载)下载whl包来安装解决问题。
中国省级与地级市财政转移支付数据-最新.zip
OPENCV 常用XML 内涵17个常用XML,包括人脸检测,微笑检测,人眼检测,用于学习模型训练和使用
python whl离线安装包 pip安装失败可以尝试使用whl离线安装包安装 第一步 下载whl文件,注意需要与python版本配套 python版本号、32位64位、arm或amd64均有区别 第二步 使用pip install XXXXX.whl 命令安装,如果whl路径不在cmd窗口当前目录下,需要带上路径 WHL文件是以Wheel格式保存的Python安装包, Wheel是Python发行版的标准内置包格式。 在本质上是一个压缩包,WHL文件中包含了Python安装的py文件和元数据,以及经过编译的pyd文件, 这样就使得它可以在不具备编译环境的条件下,安装适合自己python版本的库文件。 如果要查看WHL文件的内容,可以把.whl后缀名改成.zip,使用解压软件(如WinRAR、WinZIP)解压打开即可查看。 为什么会用到whl文件来安装python库文件呢? 在python的使用过程中,我们免不了要经常通过pip来安装自己所需要的包, 大部分的包基本都能正常安装,但是总会遇到有那么一些包因为各种各样的问题导致安装不了的。 这时我们就可以通过尝试去Python安装包大全中(whl包下载)下载whl包来安装解决问题。
python whl离线安装包 pip安装失败可以尝试使用whl离线安装包安装 第一步 下载whl文件,注意需要与python版本配套 python版本号、32位64位、arm或amd64均有区别 第二步 使用pip install XXXXX.whl 命令安装,如果whl路径不在cmd窗口当前目录下,需要带上路径 WHL文件是以Wheel格式保存的Python安装包, Wheel是Python发行版的标准内置包格式。 在本质上是一个压缩包,WHL文件中包含了Python安装的py文件和元数据,以及经过编译的pyd文件, 这样就使得它可以在不具备编译环境的条件下,安装适合自己python版本的库文件。 如果要查看WHL文件的内容,可以把.whl后缀名改成.zip,使用解压软件(如WinRAR、WinZIP)解压打开即可查看。 为什么会用到whl文件来安装python库文件呢? 在python的使用过程中,我们免不了要经常通过pip来安装自己所需要的包, 大部分的包基本都能正常安装,但是总会遇到有那么一些包因为各种各样的问题导致安装不了的。 这时我们就可以通过尝试去Python安装包大全中(whl包下载)下载whl包来安装解决问题。
python whl离线安装包 pip安装失败可以尝试使用whl离线安装包安装 第一步 下载whl文件,注意需要与python版本配套 python版本号、32位64位、arm或amd64均有区别 第二步 使用pip install XXXXX.whl 命令安装,如果whl路径不在cmd窗口当前目录下,需要带上路径 WHL文件是以Wheel格式保存的Python安装包, Wheel是Python发行版的标准内置包格式。 在本质上是一个压缩包,WHL文件中包含了Python安装的py文件和元数据,以及经过编译的pyd文件, 这样就使得它可以在不具备编译环境的条件下,安装适合自己python版本的库文件。 如果要查看WHL文件的内容,可以把.whl后缀名改成.zip,使用解压软件(如WinRAR、WinZIP)解压打开即可查看。 为什么会用到whl文件来安装python库文件呢? 在python的使用过程中,我们免不了要经常通过pip来安装自己所需要的包, 大部分的包基本都能正常安装,但是总会遇到有那么一些包因为各种各样的问题导致安装不了的。 这时我们就可以通过尝试去Python安装包大全中(whl包下载)下载whl包来安装解决问题。
开发语言:Java 框架:SSM JDK版本:JDK1.8 服务器:tomcat8.5 数据库:mysql 5.7 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea 浏览器:谷歌浏览器
yolo系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值
中国各省GDP及农业主要指标数据集(1999-2019).zip
功能说明: 系统主要包括系统主页、个人中心、用户管理、图书信息管理、图书分类管理、图书购买管理、图书借阅管理、图书续借管理、图书归还管理、留言板管理、系统管理等功能模块。 环境说明: 开发语言:Java 框架:ssm,mybatis JDK版本:JDK1.8 数据库:mysql 5.7及以上 数据库工具:Navicat11及以上 开发软件:eclipse/idea Maven包:Maven3.3及以上 服务器:tomcat7及以上
2023-04-06-项目笔记-第三百二十阶段-课前小分享_小分享1.坚持提交gitee 小分享2.作业中提交代码 小分享3.写代码注意代码风格 4.3.1变量的使用 4.4变量的作用域与生命周期 4.4.1局部变量的作用域 4.4.2全局变量的作用域 4.4.2.1全局变量的作用域_1 4.4.2.318局变量的作用域_318- 2024-11-17
Saturn_PCB_Toolkit_V7.00_ 是一款功能强大的PCB参数计算工具,本人PCB设计15年一直使用的这款计算器,利用其给出的设计数据(如线距、线宽线厚),布出的PCB实际使用未曾出现过问题 以下是其主要功能特点: 1. **过孔特性计算**:能够计算过孔的寄生电容、寄生电感、过孔阻抗、过孔直流电阻、共振频率、阶跃响应、功耗等参数。 2. **导线载流能力计算**:可以计算不同线宽下的载流能力,根据环境温度和温升条件,提供不同条件下的载流值。 3. **串扰计算**:计算两相互耦合信号线间的串扰,这对于高速PCB设计尤为重要。 4. **波长计算**:提供波长的计算功能,这对于射频和高速数字PCB设计非常关键。 5. **导体阻抗计算**:计算导体的阻抗,这对于阻抗匹配和信号完整性至关重要。 6. **单位换算**:提供单位换算功能,方便不同单位制之间的转换。 7. **差分对计算**:针对差分信号的计算,这对于高速数据传输和降低噪声非常重要。
yolo系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值
## 一、数据介绍 数据名称:中国各、省、市、县、乡镇基尼系数数据 数据年份:1992-2023年 样本数量:92064条 数据格式:面板数据 ## 二、指标说明 共计10个指标:年份、省、省代码、市、市代码、县、县代码、乡镇、乡镇代码、夜间灯光基尼系数 ## 三、数据文件 中国各乡镇基尼系数(基于夜间灯光数据)2000-2023年.dta; 中国各区县基尼系数(基于夜间灯光数据)2000-2023年.dta; 中国各城市基尼系数(基于夜间灯光数据)2000-2023年.dta; 中国各省份基尼系数(基于夜间灯光数据)2000-2023年.dta
环境说明: 开发语言:Java 框架:ssm,mybatis JDK版本:JDK1.8 数据库:mysql 5.7及以上 数据库工具:Navicat11及以上 开发软件:eclipse/idea Maven包:Maven3.3及以上 服务器:tomcat7及以上
功能说明: 系统主要包括管理员,商家和用户三大模块 (a) 管理员;管理员进入系统主要功能包括个人中心,用户管理,商家管理,用品分类管理,用品信息管理,系统管理,订单管理等功能并进行操作。 (b) 商家;商家进入系统主要功能包括个人中心,用品信息管理,订单管理等功能并进行操作。 (c) 用户;用户进入系统主要功能包括个人中心,我的收藏管理等功能并进行操作。 环境说明: 开发语言:Java 框架:ssm,mybatis JDK版本:JDK1.8 数据库:mysql 5.7及以上 数据库工具:Navicat11及以上 开发软件:eclipse/idea Maven包:Maven3.3及以上 服务器:tomcat7及以上
最新行政区划、乡镇级矢量地图及2022年道路、水系、建筑轮廓数据(shp格式)-已更至最新.zip