Java JIT可以做这个字段访问优化吗?

Java JIT可以做这个字段访问优化吗?,java,optimization,garbage-collection,jit,Java,Optimization,Garbage Collection,Jit,免责声明:请不要对过早优化提出任何建议。我只是好奇 想象一下,我想确保字段引用的某些对象可以尽快被垃圾回收。我用的是一个自制的单链表 class BigData { byte[] someBigArray; BigData next; } private BigData bigData; while (bigData != null) { process(bigData); bigData = bigData.next; } 然后像这样迭代 class Bi

免责声明:请不要对过早优化提出任何建议。我只是好奇

想象一下,我想确保字段引用的某些对象可以尽快被垃圾回收。我用的是一个自制的单链表

class BigData {
    byte[] someBigArray;
    BigData next;
}

private BigData bigData;
while (bigData != null) {
    process(bigData);
    bigData = bigData.next;
}
然后像这样迭代

class BigData {
    byte[] someBigArray;
    BigData next;
}

private BigData bigData;
while (bigData != null) {
    process(bigData);
    bigData = bigData.next;
}
JIT是否可以按照如下方式自由更改它

BigData tmp = bigData;
while (tmp != null) {
    process(tmp);
    tmp = tmp.next;
}
bigData = null;
假设没有对
BigData
的任何实例的其他引用。假设
process
是一种简单的内联方法,不访问字段
bigData
。这两个代码段是等效的(假设中间没有抛出异常),唯一的区别是第二个代码段将字段访问从循环移动到外部

重复声明:请不要对过早优化提出任何建议。我只是好奇



回答评论“即使您‘想要’的更改是JIT所做的,为什么这会使GC更快/更快地收集它们?”:如果JIT做了更改,那么所有大对象只能在循环之后收集。如果没有,则每次循环前进时,都会有一个以上的对象符合GC的条件

增编: 实际上,只要JIT可以自由地执行上述转换,它就可以执行以下转换:

BigData tmp = bigData;
bigData = null;
while (tmp != null) {
    process(tmp);
    tmp = tmp.next;
}
我看不出这里有什么缺点,它使所有对象在原始代码中都可以被收集。

好吧,优化器可以进行优化

进入

如果
bigData
字段不是
volatile
,并且
process
方法没有禁止此优化的副作用


但在实践中,代码转换(如果有的话)看起来会完全不同。优化器通常进行循环展开,创建一个代码块,执行一定数量的迭代,并在向后跳转之前执行字段存储操作。因此,垃圾收集器可以跳入某些“保存点”。但是,如果
process
方法包含访问
bigData
字段或可能分配内存等的代码,则字段存储将在每次调用之前执行。

即使您“想要”的更改是JIT所做的,为什么这会使GC更快/更快地收集它们?知道这个问题的答案会如何改变您编写代码的方式?在不同版本的JIT编译器或JVM中可能会有所不同。另外,
bigData
必须从其他引用中分配,因此引用必须仍然存在。在这里使用像
tmp
这样的局部变量来保存字段访问是一种常见的手动优化技术,所以我猜JIT本身不会这样做。@CyrilleKa,人们手动操作并不意味着JIT编译器没有对其进行优化——这也可能意味着人们低估了优化器,这在我的经验中是很常见的。@Jim Garrison:“知道这个问题的答案会如何改变你编写代码的方式?”一点也不。我只是好奇。“在不同版本的JIT编译器或JVM中可能会有所不同。”假设允许JIT这样做。这就是我的问题。“此外,bigData必须从其他引用中分配,因此引用必须仍然存在。”不。我建议您强调的问题不是关于JIT优化,而是关于Java内存模型,它指定了JIT可以做什么。AFAIK,JIT了解process()副作用的唯一方法是内联它。除非process()是内联的,否则JIT将始终在调用process()之前存储对bigData字段的更改。@Alexey Ragozin:内存模型指定了JIT对其他线程可见的副作用的限制。还有一个明显的附加约束,即代码必须保留关于执行它的一个线程的语义。当然,为了让JIT了解可能的副作用,
过程
方法必须是内联的或非多态的。例如,JIT可能会证明一个
静态
私有
方法没有副作用,请记住这一点,因此当遇到调用此方法的循环时,JIT知道的足够多,而不必内联它。