最近,我注意到声明包含64个元素的数组比声明具有65个元素的相同类型的数组要快得多(> 1000倍)。
这是我用来测试的代码:
public class Tests{ public static void main(String args[]){ double start = System.nanoTime(); int job = 100000000;//100 million for(int i = 0; i < job; i++){ double[] test = new double[64]; } double end = System.nanoTime(); System.out.println("Total runtime = " + (end-start)/1000000 + " ms"); } } 这将运行在大约6毫秒,如果我更换new double[64]用new double[65]它需要大约7秒。如果作业分布在越来越多的线程中,那么这个问题就会成倍地恶化,这就是我的问题所在。
不同类型的数组(例如int[65]或)也会发生此问题String[65]。大字符串不会发生此问题:String test = "many characters";,但将其更改为时,确实会开始发生String test = i + "";
我想知道为什么会这样,是否有可能规避这个问题。 问题来源于stack overflow
您正在观察到由Java VM的JIT编译器进行的优化所引起的行为。此行为在最多包含64个元素的标量数组中触发时可重现,而在大于64个数组中则不会触发。
在进入细节之前,让我们仔细看一下循环的主体:
double[] test = new double[64]; 身体没有作用(可观察到的行为)。这意味着无论是否执行该语句,在程序执行之外都没有区别。整个循环也是如此。因此,代码优化器可能会将循环转换为具有相同功能和不同定时行为的某种东西(或什么都没有)。
对于基准测试,您至少应遵守以下两个准则。如果您这样做,则差异会小得多。
通过多次执行基准测试来预热JIT编译器(和优化器)。 使用每个表达式的结果并将其打印在基准测试的末尾。 现在让我们详细介绍。毫不奇怪,对于不大于64个元素的标量数组会触发优化。优化是转义分析的一部分。它将小对象和小数组放到堆栈上,而不是在堆上分配它们-甚至更好地完全优化它们。您可以在下面由Brian Goetz在2005年撰写的文章中找到有关此信息的信息:
回顾城市绩效传奇:分配比您想象的要快,而且越来越快 可以使用命令行选项禁用优化-XX:-DoEscapeAnalysis。标量数组的魔术值64也可以在命令行上更改。如果按以下方式执行程序,则包含64和65个元素的数组之间将没有区别:
java -XX:EliminateAllocationArraySizeLimit=65 Tests 话虽如此,我强烈不鼓励使用此类命令行选项。我怀疑这在实际应用中会带来巨大的不同。如果我完全确信其必要性,而不是基于某些伪基准测试的结果,我只会使用它。
版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。