getter 是否会影响 Java 的性能?
考虑到以下两个代码选项,第二个选项相对于第一个选项是否有任何性能优势(在非常大的范围或很长一段时间内)?
选项 1
private Map<Long, Animal> animals = ...;
public Map<Long, Animal> getAnimals() {
return animals;
}
public void useAnimals() {
for (int i=0; i < SOME_LARGE_NUMBER; i++) {
Animal animal = getAnimals().get(id);
}
// Many many calls to getAnimals() are made...
}
选项 2 - 无 getter
private Map<Long, Animal> animals = ...;
public void useAnimals() {
for (int i=0; i < SOME_NUMBER; i++) {
Animal animal = animals.get(id);
}
// No method calls made
}
如果它对性能不利,为什么,以及我应该如何确定它是否值得缓解?
将 getAnimals()
的结果存储为本地会带来好处吗?
- 而且,如果
SOME_NUMBER
是数百或数千, - 如果
SOME_NUMBER
仅在 10 的数量级?
注意:我之前说过“封装”。我把它改成了“getter”,因为目的其实不是字段不能修改而是不能重新赋值。封装只是为了消除子类的分配责任。
Given the following two code options, is there any performance benefit (over a very large scale or a long period of time) of the second over the first?
Option 1
private Map<Long, Animal> animals = ...;
public Map<Long, Animal> getAnimals() {
return animals;
}
public void useAnimals() {
for (int i=0; i < SOME_LARGE_NUMBER; i++) {
Animal animal = getAnimals().get(id);
}
// Many many calls to getAnimals() are made...
}
Option 2 - no getter
private Map<Long, Animal> animals = ...;
public void useAnimals() {
for (int i=0; i < SOME_NUMBER; i++) {
Animal animal = animals.get(id);
}
// No method calls made
}
If it is bad for performance, why, and how should I determine whether it is worth mitigating?
And, would storing the result of getAnimals()
as a local provide a benefit...
- if
SOME_NUMBER
is hundreds or thousands? - if
SOME_NUMBER
is only in the order of magnitude of 10?
Note: I previously said "encapsulation". I changed it to "getter" because the purpose is actually not that the field can't be modified but that it can't be reassigned. The encapsulation is simply to remove responsibility for assignment from subclasses.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
嗯,我不认为 JVM 会内联函数调用。所以可能会影响性能。更好的方法是创建局部变量并将类字段动物分配给它。
Well, I don't think that JVM will inline the function call. So probably it may affect performance. The better way is to create local variable and assign class field animals to it.
第二个片段比第一个片段更多封装。第一个允许任何人访问内部映射,而第二个将其封装在类中。
两者都会带来可比较的性能。
编辑:既然你改变了问题,我也会改变答案。
如果您使用 getter,并且该 getter 不是最终的,则意味着子类可能返回另一个映射,而不是您在类中保存的映射。选择是否希望您的方法在子类的映射或类的映射上运行。两者都可以接受,具体取决于上下文。
不管怎样,假设你的子类总是制作地图的防御性副本,如果你不将 getter 的结果缓存在
useAnimals
的本地变量中,你最终会拥有许多副本。可能需要始终处理子类映射的最新值,但我怀疑情况确实如此。如果没有子类,或者子类没有重写该方法,或者通过始终返回相同的映射来重写它,则两者都会导致相当的性能,并且您不应该关心它。
The second snippet is more encapsulated than the first one. The first one gives access to the internal map to anyone, whereas the second keeps it encapsulated in the class.
Both will lead to comparable performance.
EDIT: since you change the question, I'll also change the answer.
If you go through a getter, and the getter is not final, it means that subclasses may return another map than the one you hold in the class. Choose whether you want your method to operate on the subclass's map or on the class's map. Both could be acceptable, depending on the context.
Anyway, suppose your subclass always makes a defensive copy of the map, you'll end up having many copies if you don't cache the result of the getter in a local variable of
useAnimals
. It might be required to always work on the latest value of the subclass's map, but I doubt it's the case.If there is no subclass, or the subclass doesn't override the method, or override it by always returning the same map, both will lead to comparable performance and you shouldn't care about it.
您是否对此进行了分析,看看它是否重要,对于现代 JIT,我猜测它会被优化掉,特别是如果
animals
被标记为final
但没有什么可以阻止您自己进行测试。不管怎样,我 100% 认为这永远不会成为您应用程序中的瓶颈。
Have you profiled this to see if it matter, for a modern JIT I would guess it would get optomized away, especially if
animals
was markedfinal
but there is nothing stopping you from testing this yourself.Either way, I am 100% this would NEVER be your bottle neck in an application.
JVM 很可能会在紧密循环中内联 getAnimals() 调用,从而有效地退回到选项 1。所以不用担心,这确实是一种微(纳米?)优化。
另一件事是从字段访问迁移到局部变量。这听起来不错,因为您不必每次在堆栈上都有引用时都遍历
this
引用(两次内存访问与一次内存访问)。但是我相信(如果我错了,请纠正我),由于animals
是私有的且非易失性
,JVM 会在运行时为您执行此优化。Most likely JVM will inline
getAnimals()
invocation in tight loop effectively falling back to Option 1. So don't bother, this is really a micro (nano?) optimization.Another thing is migrating from field access to local variable. This sounds good since instead of traversing through
this
reference every time you always have a reference on the stack (two memory accesses vs. one). However I believe (correct me if I'm wrong) that sinceanimals
is private and non-volatile
again JVM will perform this optimization for you at runtime.