Java和C++在运转办法上的区别?
Java代码有很多种不同的运转办法,比如在开发工具中运转、双击履行jar文件运转、在命令行中运转,甚至能够在网页中运转。
Java的运转离不开JRE(Java运转时环境),JRE仅包含运转Java程序的必需组件,包含Java虚拟机以及Java核心类库等。当然咱们程序员更经常接触到的是JDK(Java开发工具包),包
含了JRE,并且还附带了一系列开发、确诊工具。
运转C++代码则无需额定的运转时,往往把代码直接编译成CPU所能理解的机器码即可。
为什么Java要在虚拟机里运转?
Java作为一门高档程序言语,它的语法非常复杂,笼统程度也很高。因此,直接在硬件上运转这种复杂的程序并不实际。
所以能够在Java代码与机器码之间加一层虚拟机,先将Java代码转换成Java字节码,最终才转换成机器码,降低复杂度。
JavaHelloWorld字节码格局
Java虚拟机的长处
经过为各个平台(Linux、Windows、Macos等)提供Java虚拟机的软件实现,在各个平台上都能够将Java代码转换成字节码来运转Java(可移植性),到达“一次编写、处处运转”的意图
Java虚拟机为咱们带来了一个保管环境(ManagedRuntime),能够替咱们处理一些冗长并且容易出错的代码,最重要的比如主动内存办理与废物回收,一起还提供诸如数组越界、动态类型、安全权
限等的动态检测,让咱们能够专心的写事务代码
Java虚拟机详细是怎样运转Java字节码的?
首要从虚拟机的视点来看:
首要将Java代码编译成的class文件
将class文件加载到Java虚拟机中(载入Java内存),加载后的Java类会被存放于办法区(MethodArea)中
运转时,虚拟机履行办法区内的代码
Java虚拟机会将栈细分为面向Java办法的Java办法栈,面向本地办法(用C++写的native办法)的本地办法栈,以及存放各个线程履行位置的PC寄存器。
本地办法,用关键字native润饰,根据JNI(JavaNativeInterface,Java本地接口),它答应Java代码和其他言语写的代码进行交互
在运转进程中,每逢调用进入一个Java办法,Java虚拟机会在当时线程的Java办法栈中生成
一个栈帧,用以存放局部变量以及字节码的操作数。(栈帧是提早计算好且不需连续分布)
当退出当时履行的办法时,不管是正常回来还是反常回来,Java虚拟机均会弹出当时线程的当时栈帧,并将之放弃。
Java虚拟机将字节码翻译成机器码有两种办法:
解说履行:即逐条将字节码翻译成机器码并履行;
即时编译(Just-In-Timecompilation,JIT),即将一个办法中包含的一切字节码编译成机器码后再履行。
解说履行的长处是无须等候编译,即时编译的长处是实际履行速度更快。
HotSpot默许选用混合模式,综合了解说履行和即时编译两者的长处,它会先解说履行字节码,然后将其中重复履行的热门代码,以办法为单位进行即时编译。
关于即时编译
即时编译首要是为了进步Java虚拟机的发动功能以及峰值功能,它建立在程序契合二八规律的假定上,也便是百分之二十的代码占有了百分之八十的计算资源。
关于百分之八十(大部分)不常用的代码,咱们无需消耗时刻将其编译成机器码,而是采取解说履行的办法运转;关于百分之二十(小部分)的热门代码,咱们则能够将其编译成机器码,以到达理想
的运转速度。
为了满足不同用户场景的需要(在编译时刻和生成代码的履行功率之间进行取舍),HotSpot内置了多个即时编译器:C1、C2和Graal
C1,又名Client编译器,面向的是对发动功能有要求的客户端GUI程序,选用的优化手法相对简略,编译时刻较短。
C2,又名Server编译器,面向的是对峰值功能有要求的服务器端程序,选用的优化手法相对复杂,编译时刻较长,但一起生成代码的履行功率较高。
Graal是Java10正式引入的实验性即时编译器
从Java7开始,HotSpot默许选用分层编译的办法:热门办法首要会被C1编译,而后热门办法中的热门会进一步被C2编译。
小作业:观察两个条件判别句子的运转成果
经过观察两个条件判别句子的运转成果,来考虑Java言语和Java虚拟机看待boolean类型的办法是否不同?
点击下载asmtools,并在命令行中运转下述指令
注:ASM是一个Java字节码操控框架
e
c
h
o
′
p
u
b
l
i
c
c
l
a
s
s
F
o
o
p
u
b
l
i
c
s
t
a
t
i
c
v
o
i
d
m
a
i
n
(
S
t
r
i
n
g
[
]
a
r
g
s
)
b
o
o
l
e
a
n
f
l
a
g
=
t
r
u
e
;
i
f
(
f
l
a
g
)
S
y
s
t
e
m
.
o
u
t
.
p
r
i
n
t
l
n
(
"
H
e
l
l
o
,
J
a
v
a
!
"
)
;
i
f
(
f
l
a
g
=
=
t
r
u
e
)
S
y
s
t
e
m
.
o
u
t
.
p
r
i
n
t
l
n
(
"
H
e
l
l
o
,
J
V
M
!
"
)
;
′
>
F
o
o
.
j
a
v
a
echo' publicclassFoo{ publicstaticvoidmain(String[]args){ booleanflag=true; if(flag)System.out.println("Hello,Java!"); if(flag==true)System.out.println("Hello,JVM!"); } }'>Foo.java
echo′ publicclassFoo publicstaticvoidmain(String[]args) booleanflag=true; if(flag)System.out.println("Hello,Java!"); if(flag==true)System.out.println("Hello,JVM!"); ′>Foo.java javacFoo.java
j
a
v
a
F
o
o
javaFoo
javaFoo java-cp/path/to/asmtools.jarorg.openjdk.asmtools.jdis.MainFoo.class>Foo.jasm.1
a
w
k
′
N
R
=
=
1
,
/
i
c
o
n
s
t
1
/
s
u
b
(
/
i
c
o
n
s
t
1
/
,
"
i
c
o
n
s
t
2
"
)
1
′
F
o
o
.
j
a
s
m
.
1
>
F
o
o
.
j
a
s
m
awk'NR==1,/iconst_1/{sub(/iconst_1/,"iconst_2")}1'Foo.jasm.1>Foo.jasm
awk′NR==1,/iconst1/sub(/iconst1/,"iconst2")1′Foo.jasm.1>Foo.jasm java-cp/path/to/asmtools.jarorg.openjdk.asmtools.jasm.MainFoo.jasm
$javaFoo
成果分析:(来自评论区)
jvm把boolean作为int来处理,flag=iconst_1=true
awk把stackframe中的flag改为iconst_2
if(flag)比较时ifeq指令做是否为零判别,常数2仍为true,打印输出
if(true==flag)比较时if_cmpne做整数比较,iconst_1是否等于flag,比较失利,不再打
印输出
Hotspot中的热门代码勘探技能
怎样区分热门代码呢?首要有下面两种办法:
根据采样的热门勘探
根据计数器的热门勘探
计数器办法又可细分为办法调用计数器和汇编计数器
HotSpot的热门代码勘探技能
办法调用计数器计算一个相对的履行频率,即一段时刻内办法被调用的次数,当超过必定的时刻极限,假如办法的调用次数仍然不足以让它提交给即时编译器,那这个办法的调用计数器就会被减少
一半,这个进程称为办法调用计数器热度的衰减,而这段时刻就称为办法计算的半衰周期,进行热度衰减的动作在虚拟机进行废物收集时趁便进行了。一般选用的都是根据计数器的热门勘探,根据计
数器的热门勘探又有两个计数器,办法调用计数器,回边计数器,他们在C1和C2又有不同的阈值
汇编计数器计算一个办法体重循环体代码履行的次数,在字节码中遇到操控流向后跳转的指令称为汇编,显然,建立汇编计数器的意图便是为了触发OSR编译。没有计数热度衰减的进程,因此这个计
数器计算的便是该办法履行循环的肯定次数,当计数器溢出的时分它还会把办法计数器的值也调整到溢出的状态,这样下去在再次进入该办法的时分就会履行规范编译进程。