关于 Java 中 JIT 的问题

2018-08-14 17:38:01 +08:00
 cc959798

小白咨询大家一个问题,轻喷呀

1.JIT 会把部分热点代码直接编译成本机能够执行的机器码,那为什么不把所有的代码都直接在运行的时候编译成机器码呢?这样整个程序会更快些,由于内存吗?现在的机器内存都不太稀缺,对于服务端程序来说内存应该是可承受的吧 2.JIT 中把部分程序编译成机器码,部分不是,直接解释执行,请问两者之间有什么具体的区别,就算是解释执行的话机器应该也只是执行机器码吗?

真心求教

2741 次点击
所在节点    Java
14 条回复
wwqgtxx
2018-08-14 17:48:03 +08:00
全部编译那就是 aot 了,主要还是编译消耗 cpu 资源,你看看 android5.0 那慢到窒息的 app 安装速度,到 android6.0 就改回 jit 了
另外一方面是 jit 可以分层优化,特别是可以边运行边优化,在代码运行到一定次数的时候,能动态把不必要的循环,递归都优化掉

解析执行的意思是一行一行的翻译执行,编译成机器码就是不在需要翻译了,当然最后执行的都是机器码
zjp
2018-08-14 17:57:54 +08:00
得看场景,JVM 分客户端模式和服务器模式。一般客户端在意启动速度,运行时间不长。可以假设一种极端情况,解析执行的执行完了,编译还没完成。
ekoeko
2018-08-14 18:02:39 +08:00
@wwqgtxx 请问,服务端更新应该不频繁,启动慢点无所谓吧,是不是可以把服务端的程序全部编译好再执行, 后面运行的效率会更高吧?这样会不会更好?
loqixh
2018-08-14 18:16:06 +08:00
策略不同而已 .net 就是方法第一次执行时编译成机器码, 不存在解释执行
aidoudou
2018-08-14 18:18:16 +08:00
个人理解:一般是采用并存的分层编译。热点代码要根据运行情况的性能监控来通过 JIT 优化;非热点代码,解释执行可以节约内存,并且没有通过 JIT 的必要。这其中应该有一个大致的平衡点(所以需要监控热点代码),单方面的全部 JIT 或者全部解释执行应该都不可取。
lovedebug
2018-08-14 18:22:18 +08:00
1. 很多代码只执行一次或很少执行,热编译反而代价大
2.JIT 要考虑到多线程同步问题和内存模型
ZSeptember
2018-08-14 18:31:28 +08:00
JIT 比较激进。如果发生 deoptimization 代价挺大的,所以不会全部 JIT。
lurenw
2018-08-14 18:31:51 +08:00
1. 在运行前编译叫做 AOT ( ahead-of-time ), 在运行前编译的好处是节省整个运行环节的时间,毕竟都是机器码了。缺点就是 AOT 不能根据运行时的状态做对应的优化,所以可能会做许多多余的优化
2. JIT 有个 PGO,在程序运行的时候采集信息(比如热点代码),然后可以做针对性的优化
3. 解释执行和编译执行到最后都是机器码。比如有些代码只跑一次,此时就没啥必要编译之后再跑,直接分析语法,最后转成机器码会快很多。
momocraft
2018-08-14 18:35:48 +08:00
JIT 编译不一定只发生一次 ,也不一定启动就能做(一些优化需要运行时信息)
luozic
2018-08-14 19:04:32 +08:00
可以手动指定编译方式。并且 hotspot 是 JiT 的一种方式,并不是所有的 JIT 使用这种方式实现
kaneg
2018-08-14 19:21:44 +08:00
很多代码在运行时才知道会不会执行到,举个极端的例子:classpath 有上百个 jar,但在 main 方法里只打印了 hello world 就结束了,这种情况下提前优化就没有意义。
cc128
2018-08-14 19:34:26 +08:00
编译的机器码体积要大很多。全部编译占用内存是一定的。jit 也会占用很多系统资源。而且 jit 是有一个阀值的。这里说的是 android,dalvok 和 art 的 jit 又不太一样。之前 android6.0 里 jit cache 是限制在 2m,6.0 没 release,7.0 又改了很多。结合了 aot+jit,并且把 jit 延迟到用户充电这种情况下。并且编译结果写入到一个文件。所以如果全部编译时间和空间都是一个大问题。还不如 aot。Aot 的话机器码是在磁盘上,是映射到内存。

编译出来的机器码是包含了虚拟机相关操作的。比如调用一个方法,并不是直接编译成 blx 或 call 指令。解释执行等于是查字典。每一条字节码指令对应一段虚拟机中行的代码。android 上是 switch case,goto 这种样子的。比如你调用函数,就会执行 INVOKE _VIRTUAL 的指令。

解释执行和机器码之间相互调用是通过汇编和 c 关联起来的。还涉及 manager stack 数据和 native stwck 数据的交换转移。

总的说细节还是很多。。。😁😁😁
LukeChien
2018-08-15 08:17:34 +08:00
可能是技术还不成熟,Java9 已经可以完全 JIT 编译成原生代码了
wwqgtxx
2018-08-15 12:13:07 +08:00
@LukeChien 我记得并不可以完全编译,AOT 仅限于标准库

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/479759

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX