注意:这个问题是向Smalltalk、Self、Javascript、Java、C#等语言的VM实现者提出的。
JIT编译器带来的一个常见功能是自动窥视系统事件。这既用于处理UI事件,也用于触发GC。
通常,这种事件窥视包括在帧构建(当调用方法时)和循环后跳处进行检查。我想知道,
执行此检查的不同方法是什么?它们的优点和缺点是什么?你知道有什么论文描述这些技术吗?
你能告诉我它是如何在特定的虚拟机中实现的吗?(Pharo、HPS、HotSpot、V8、SpiderMonkey、CLR)
根据我所读到的内容,我想每个实现都会做以下的一些变化:
-
指定一个硬件寄存器专门用于计数,并在达到某个阈值时进行窥视
-
有一个定期发出信号的定时器(如何实现?)然后:
- 移动
stack_limit
变量,然后在帧构建和后跳时检查该变量 - 将堆栈标记为只读或在硬件级别不可访问(即分页),从而导致帧构建失败,并在后跳之前添加对堆栈的写入编辑:好处:由于数据依赖性,写入内存比读取快。缺点:如果计数器被频繁触发,检查会变慢,因为页面错误捕获很慢。安全点被污染:任何在标记页面后使用堆栈的指令都会触发检查,该指令可能是也可能不是安全点
- 移动
正如您和@melkyades提到的,一种方法是分配一些寄存器(例如EBX
),通常从图像初始化为某个最大值。然后,JIT在每个本机化的方法中内联代码,这些代码在方法的开头和其中发生的每个后跳(#whileTrue:/False:
)处递减计数器(顺便说一句,这就是为什么有后跳字节码的原因。)当计数器变为零时,这段代码会检查三件事:(1)是否有任何操作系统事件要查看,(2)对象存储器的状态(可能最终触发一些GC操作)和(3)堆栈指针(预测堆栈结束条件)
可以实现的一个有趣的技巧是:
@1: call CheckEventsEtc
...
cmp ESP, [TOS]
dec EBX
jbe @1
假定dec
不修改进位标志,则如果ESP
变得太小或如果EBX
处的计数器变为零,则jbe
指令将跳转到检查例程。如果出现上述任何情况,检查程序将必须查明原因并采取相应行动。