假设我有一个对象A可以调用getB()调用getC()调用getD()调用doSomething ... 现在我想在我的应用程序中使用D multipe次的一些方法,即
A.getB().getC().getD().doSomething1();
A.getB().getC().getD().doSomething2();
A.getB().getC().getD().doSomething3();
。我知道最好创建一个变量d = A.getB().getC().getD()
,然后再使用它。但我想知道链接这样的多种方法是否有任何性能问题。
class A {
private B b;
B getB() {
return b;
}
}
class B {
private C c;
C getC() {
return c;
}
}
class C {
private D d;
D getD() {
return d;
}
}
class D {
void doSomething1(){};
void doSomething2(){};
void doSomething3(){};
}
答案 0 :(得分:2)
如果对象被实例化一次,则不会出现任何性能问题。当你调用getB()。getC()...你只需要在对象上获得非常快速和简单操作的链接。 但实际上,这样的代码看起来并不是很好。
答案 1 :(得分:1)
虽然你提到你已经知道了:你应该避免这么长的方法链,原因如下:
a.getB().getC().getD().doSomething()
之类的行引发了NullPointerException
,那么调试此内容就没有任何乐趣...... 当然,你必须考虑方法正在做什么。虽然get
- 方法通常只返回一个值,但您不知道它是否真的这样做了。甚至像
List<T> getList() {
// Return an unmodifiable view to the caller
return Collections.unmodifiableList(internalList);
}
(当然 是一种好习惯)可能会改变结果。
话虽如此,并且考虑到这些get
- 方法实际上只是简单的,愚蠢的 Getters :
它在实践中对性能没有影响。方法调用将由JIT内联。
例如,请考虑以下程序:
class ChainA {
private ChainB b = new ChainB();
ChainB getB() {
return b;
}
}
class ChainB {
private ChainC c = new ChainC();
ChainC getC() {
return c;
}
}
class ChainC {
private ChainD d = new ChainD();
ChainD getD() {
return d;
}
}
class ChainD {
private int result = 0;
int getResult() { return result; }
void doSomething1(){ result += 1; }
void doSomething2(){ result += 2; }
void doSomething3(){ result += 3; }
}
class Chaining
{
public static void main(String args[])
{
for (int n=100; n<10000; n+=100)
{
ChainA a0 = new ChainA();
runChained(a0, n);
System.out.println(a0.getB().getC().getD().getResult());
ChainA a1 = new ChainA();
runUnChained(a1, n);
System.out.println(a1.getB().getC().getD().getResult());
}
}
private static void runChained(ChainA a, int n)
{
for (int i=0; i<n; i++)
{
a.getB().getC().getD().doSomething1();
a.getB().getC().getD().doSomething2();
a.getB().getC().getD().doSomething3();
}
}
private static void runUnChained(ChainA a, int n)
{
ChainD d = a.getB().getC().getD();
for (int i=0; i<n; i++)
{
d.doSomething1();
d.doSomething2();
d.doSomething3();
}
}
}
它会像你描述的那样调用它们,一次作为链式调用,一次作为一个非链接版本。
使用
运行它启用HotSpot-Disassembler的JVM上的java -server -XX:+ UnlockDiagnosticVMOptions -XX:+ TraceClassLoading -XX:+ LogCompilation -XX:+ PrintAssembly Chaining
会产生以下输出(无需读取它,仅供参考)
<强> runChained:强>
Decoding compiled method 0x0000000002885d50:
Code:
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} {0x0000000055360550} 'runChained' '(LChainA;I)V' in 'Chaining'
# parm0: rdx:rdx = 'ChainA'
# parm1: r8 = int
# [sp+0x30] (sp of caller)
0x0000000002885e80: mov %eax,-0x6000(%rsp)
0x0000000002885e87: push %rbp
0x0000000002885e88: sub $0x20,%rsp ;*synchronization entry
; - Chaining::runChained@-1 (line 47)
0x0000000002885e8c: mov %rdx,%r9
0x0000000002885e8f: mov %r8d,%ebx
0x0000000002885e92: test %r8d,%r8d
0x0000000002885e95: jle 0x0000000002885f8a ;*if_icmpge
; - Chaining::runChained@4 (line 47)
0x0000000002885e9b: mov 0xc(%rdx),%r11d ;*getfield b
; - ChainA::getB@1 (line 4)
; - Chaining::runChained@8 (line 49)
; implicit exception: dispatches to 0x0000000002885f96
0x0000000002885e9f: mov 0xc(%r11),%r10d ;*getfield c
; - ChainB::getC@1 (line 10)
; - Chaining::runChained@11 (line 49)
; implicit exception: dispatches to 0x0000000002885f96
0x0000000002885ea3: mov 0xc(%r10),%edx ;*getfield d
; - ChainC::getD@1 (line 16)
; - Chaining::runChained@14 (line 49)
; implicit exception: dispatches to 0x0000000002885f96
0x0000000002885ea7: mov 0xc(%rdx),%r11d ;*getfield result
; - ChainD::doSomething1@2 (line 23)
; - Chaining::runChained@17 (line 49)
; implicit exception: dispatches to 0x0000000002885f96
0x0000000002885eab: xor %r10d,%r10d
0x0000000002885eae: xor %esi,%esi
0x0000000002885eb0: xor %r8d,%r8d
0x0000000002885eb3: xor %ecx,%ecx ;*aload_0
; - Chaining::runChained@7 (line 49)
0x0000000002885eb5: add %r11d,%esi ;*getfield result
; - ChainD::doSomething1@2 (line 23)
; - Chaining::runChained@17 (line 49)
0x0000000002885eb8: add %ecx,%r8d
0x0000000002885ebb: mov %esi,%eax
0x0000000002885ebd: add $0x6,%eax ;*iadd
; - ChainD::doSomething3@6 (line 25)
; - Chaining::runChained@43 (line 51)
0x0000000002885ec0: mov %eax,0xc(%rdx) ;*putfield result
; - ChainD::doSomething3@7 (line 25)
; - Chaining::runChained@43 (line 51)
0x0000000002885ec3: mov %r10d,%edi
0x0000000002885ec6: inc %edi ;*iinc
; - Chaining::runChained@46 (line 47)
0x0000000002885ec8: cmp $0x1,%edi
0x0000000002885ecb: jge 0x0000000002885eee ;*if_icmpge
; - Chaining::runChained@4 (line 47)
0x0000000002885ecd: mov %r10d,%ecx
0x0000000002885ed0: shl %ecx
0x0000000002885ed2: mov %r8d,%esi
0x0000000002885ed5: add $0x6,%esi
0x0000000002885ed8: mov %ecx,%r8d
0x0000000002885edb: add $0x2,%r8d
0x0000000002885edf: shl $0x2,%r10d
0x0000000002885ee3: mov %r10d,%ecx
0x0000000002885ee6: add $0x4,%ecx
0x0000000002885ee9: mov %edi,%r10d
0x0000000002885eec: jmp 0x0000000002885eb5
0x0000000002885eee: mov %ebx,%r11d
0x0000000002885ef1: add $0xfffffff1,%r11d
0x0000000002885ef5: mov $0x80000000,%r9d
0x0000000002885efb: cmp %r11d,%ebx
0x0000000002885efe: cmovl %r9d,%r11d
0x0000000002885f02: cmp %r11d,%edi
0x0000000002885f05: jge 0x0000000002885f3c
0x0000000002885f07: sub %r8d,%esi
0x0000000002885f0a: nopw 0x0(%rax,%rax,1) ;*aload_0
; - Chaining::runChained@7 (line 49)
0x0000000002885f10: mov %edi,%r8d
0x0000000002885f13: shl %r8d
0x0000000002885f16: mov %edi,%r9d
0x0000000002885f19: shl $0x2,%r9d
0x0000000002885f1d: add %r9d,%r8d
0x0000000002885f20: add %esi,%r8d ;*getfield result
; - ChainD::doSomething1@2 (line 23)
; - Chaining::runChained@17 (line 49)
0x0000000002885f23: mov %r8d,%eax
0x0000000002885f26: add $0x60,%eax ;*iadd
; - ChainD::doSomething3@6 (line 25)
; - Chaining::runChained@43 (line 51)
0x0000000002885f29: add $0x5a,%r8d
0x0000000002885f2d: mov %r8d,0xc(%rdx)
0x0000000002885f31: mov %eax,0xc(%rdx) ;*putfield result
; - ChainD::doSomething3@7 (line 25)
; - Chaining::runChained@43 (line 51)
0x0000000002885f34: add $0x10,%edi ;*iinc
; - Chaining::runChained@46 (line 47)
0x0000000002885f37: cmp %r11d,%edi
0x0000000002885f3a: jl 0x0000000002885f10 ;*getfield result
; - ChainD::doSomething1@2 (line 23)
; - Chaining::runChained@17 (line 49)
0x0000000002885f3c: cmp %ebx,%edi
0x0000000002885f3e: jge 0x0000000002885f8a
0x0000000002885f40: mov %edi,%r10d
0x0000000002885f43: shl $0x2,%r10d
0x0000000002885f47: mov %edi,%r11d
0x0000000002885f4a: shl %r11d
0x0000000002885f4d: mov %r11d,%r8d
0x0000000002885f50: add %r10d,%r8d
0x0000000002885f53: sub %r8d,%eax
0x0000000002885f56: xchg %ax,%ax ;*aload_0
; - Chaining::runChained@7 (line 49)
0x0000000002885f58: add %r11d,%r10d
0x0000000002885f5b: add %eax,%r10d
0x0000000002885f5e: add $0x6,%r10d
0x0000000002885f62: mov %r10d,0xc(%rdx) ;*putfield result
; - ChainD::doSomething3@7 (line 25)
; - Chaining::runChained@43 (line 51)
0x0000000002885f66: mov %edi,%r8d
0x0000000002885f69: inc %r8d ;*iinc
; - Chaining::runChained@46 (line 47)
0x0000000002885f6c: cmp %ebx,%r8d
0x0000000002885f6f: jge 0x0000000002885f8a
0x0000000002885f71: mov %edi,%r10d
0x0000000002885f74: shl $0x2,%r10d
0x0000000002885f78: shl %edi
0x0000000002885f7a: add $0x4,%r10d
0x0000000002885f7e: mov %edi,%r11d
0x0000000002885f81: add $0x2,%r11d
0x0000000002885f85: mov %r8d,%edi
0x0000000002885f88: jmp 0x0000000002885f58 ;*if_icmpge
; - Chaining::runChained@4 (line 47)
0x0000000002885f8a: add $0x20,%rsp
0x0000000002885f8e: pop %rbp
0x0000000002885f8f: test %eax,-0x26c5f95(%rip) # 0x00000000001c0000
; {poll_return}
0x0000000002885f95: retq
0x0000000002885f96: mov $0xffffff86,%edx
0x0000000002885f9b: mov %r9,%rbp
0x0000000002885f9e: mov %r8d,(%rsp)
0x0000000002885fa2: nop
0x0000000002885fa3: callq 0x00000000027b7320 ; OopMap{rbp=Oop off=296}
;*aload_0
; - Chaining::runChained@7 (line 49)
; {runtime_call}
0x0000000002885fa8: int3 ;*aload_0
; - Chaining::runChained@7 (line 49)
0x0000000002885fa9: hlt
0x0000000002885faa: hlt
0x0000000002885fab: hlt
0x0000000002885fac: hlt
0x0000000002885fad: hlt
0x0000000002885fae: hlt
0x0000000002885faf: hlt
0x0000000002885fb0: hlt
0x0000000002885fb1: hlt
0x0000000002885fb2: hlt
0x0000000002885fb3: hlt
0x0000000002885fb4: hlt
0x0000000002885fb5: hlt
0x0000000002885fb6: hlt
0x0000000002885fb7: hlt
0x0000000002885fb8: hlt
0x0000000002885fb9: hlt
0x0000000002885fba: hlt
0x0000000002885fbb: hlt
0x0000000002885fbc: hlt
0x0000000002885fbd: hlt
0x0000000002885fbe: hlt
0x0000000002885fbf: hlt
[Exception Handler]
[Stub Code]
0x0000000002885fc0: jmpq 0x00000000028694a0 ; {no_reloc}
[Deopt Handler Code]
0x0000000002885fc5: callq 0x0000000002885fca
0x0000000002885fca: subq $0x5,(%rsp)
0x0000000002885fcf: jmpq 0x00000000027b6f40 ; {runtime_call}
0x0000000002885fd4: hlt
0x0000000002885fd5: hlt
0x0000000002885fd6: hlt
0x0000000002885fd7: hlt
<强> runUnChained:强>
Decoding compiled method 0x00000000028893d0:
Code:
[Entry Point]
[Verified Entry Point]
[Constants]
# {method} {0x0000000055360628} 'runUnChained' '(LChainA;I)V' in 'Chaining'
# parm0: rdx:rdx = 'ChainA'
# parm1: r8 = int
# [sp+0x30] (sp of caller)
0x0000000002889500: mov %eax,-0x6000(%rsp)
0x0000000002889507: push %rbp
0x0000000002889508: sub $0x20,%rsp ;*synchronization entry
; - Chaining::runUnChained@-1 (line 57)
0x000000000288950c: mov 0xc(%rdx),%r11d ;*getfield b
; - ChainA::getB@1 (line 4)
; - Chaining::runUnChained@1 (line 57)
; implicit exception: dispatches to 0x0000000002889612
0x0000000002889510: mov 0xc(%r11),%r10d ;*getfield c
; - ChainB::getC@1 (line 10)
; - Chaining::runUnChained@4 (line 57)
; implicit exception: dispatches to 0x000000000288961d
0x0000000002889514: mov 0xc(%r10),%ebx ;*getfield d
; - ChainC::getD@1 (line 16)
; - Chaining::runUnChained@7 (line 57)
; implicit exception: dispatches to 0x0000000002889629
0x0000000002889518: mov %r8d,%esi
0x000000000288951b: test %r8d,%r8d
0x000000000288951e: jle 0x0000000002889606 ;*if_icmpge
; - Chaining::runUnChained@15 (line 58)
0x0000000002889524: mov 0xc(%rbx),%r10d ;*getfield result
; - ChainD::doSomething1@2 (line 23)
; - Chaining::runUnChained@19 (line 60)
; implicit exception: dispatches to 0x0000000002889635
0x0000000002889528: xor %r8d,%r8d
0x000000000288952b: xor %edx,%edx
0x000000000288952d: xor %r9d,%r9d
0x0000000002889530: xor %r11d,%r11d ;*aload_2
; - Chaining::runUnChained@18 (line 60)
0x0000000002889533: add %r10d,%edx ;*getfield result
; - ChainD::doSomething1@2 (line 23)
; - Chaining::runUnChained@19 (line 60)
0x0000000002889536: add %r11d,%r9d
0x0000000002889539: mov %edx,%edi
0x000000000288953b: add $0x6,%edi ;*iadd
; - ChainD::doSomething3@6 (line 25)
; - Chaining::runUnChained@27 (line 62)
0x000000000288953e: mov %edi,0xc(%rbx) ;*putfield result
; - ChainD::doSomething3@7 (line 25)
; - Chaining::runUnChained@27 (line 62)
0x0000000002889541: mov %r8d,%ecx
0x0000000002889544: inc %ecx ;*iinc
; - Chaining::runUnChained@30 (line 58)
0x0000000002889546: cmp $0x1,%ecx
0x0000000002889549: jge 0x000000000288956e ;*if_icmpge
; - Chaining::runUnChained@15 (line 58)
0x000000000288954b: mov %r8d,%r11d
0x000000000288954e: shl %r11d
0x0000000002889551: mov %r9d,%edx
0x0000000002889554: add $0x6,%edx
0x0000000002889557: mov %r11d,%r9d
0x000000000288955a: add $0x2,%r9d
0x000000000288955e: shl $0x2,%r8d
0x0000000002889562: mov %r8d,%r11d
0x0000000002889565: add $0x4,%r11d
0x0000000002889569: mov %ecx,%r8d
0x000000000288956c: jmp 0x0000000002889533
0x000000000288956e: mov %esi,%r10d
0x0000000002889571: add $0xfffffff1,%r10d
0x0000000002889575: mov $0x80000000,%r11d
0x000000000288957b: cmp %r10d,%esi
0x000000000288957e: cmovl %r11d,%r10d
0x0000000002889582: cmp %r10d,%ecx
0x0000000002889585: jge 0x00000000028895bc
0x0000000002889587: sub %r9d,%edx
0x000000000288958a: nopw 0x0(%rax,%rax,1) ;*aload_2
; - Chaining::runUnChained@18 (line 60)
0x0000000002889590: mov %ecx,%r11d
0x0000000002889593: shl %r11d
0x0000000002889596: mov %ecx,%r8d
0x0000000002889599: shl $0x2,%r8d
0x000000000288959d: add %r8d,%r11d
0x00000000028895a0: add %edx,%r11d ;*getfield result
; - ChainD::doSomething1@2 (line 23)
; - Chaining::runUnChained@19 (line 60)
0x00000000028895a3: mov %r11d,%edi
0x00000000028895a6: add $0x60,%edi ;*iadd
; - ChainD::doSomething3@6 (line 25)
; - Chaining::runUnChained@27 (line 62)
0x00000000028895a9: add $0x5a,%r11d
0x00000000028895ad: mov %r11d,0xc(%rbx)
0x00000000028895b1: mov %edi,0xc(%rbx) ;*putfield result
; - ChainD::doSomething3@7 (line 25)
; - Chaining::runUnChained@27 (line 62)
0x00000000028895b4: add $0x10,%ecx ;*iinc
; - Chaining::runUnChained@30 (line 58)
0x00000000028895b7: cmp %r10d,%ecx
0x00000000028895ba: jl 0x0000000002889590 ;*getfield result
; - ChainD::doSomething1@2 (line 23)
; - Chaining::runUnChained@19 (line 60)
0x00000000028895bc: cmp %esi,%ecx
0x00000000028895be: jge 0x0000000002889606
0x00000000028895c0: mov %ecx,%r11d
0x00000000028895c3: shl $0x2,%r11d
0x00000000028895c7: mov %ecx,%r8d
0x00000000028895ca: shl %r8d
0x00000000028895cd: mov %r8d,%r9d
0x00000000028895d0: add %r11d,%r9d
0x00000000028895d3: sub %r9d,%edi
0x00000000028895d6: xchg %ax,%ax ;*aload_2
; - Chaining::runUnChained@18 (line 60)
0x00000000028895d8: add %r8d,%r11d
0x00000000028895db: add %edi,%r11d
0x00000000028895de: add $0x6,%r11d
0x00000000028895e2: mov %r11d,0xc(%rbx) ;*putfield result
; - ChainD::doSomething3@7 (line 25)
; - Chaining::runUnChained@27 (line 62)
0x00000000028895e6: mov %ecx,%edx
0x00000000028895e8: inc %edx ;*iinc
; - Chaining::runUnChained@30 (line 58)
0x00000000028895ea: cmp %esi,%edx
0x00000000028895ec: jge 0x0000000002889606
0x00000000028895ee: mov %ecx,%r11d
0x00000000028895f1: shl $0x2,%r11d
0x00000000028895f5: shl %ecx
0x00000000028895f7: add $0x4,%r11d
0x00000000028895fb: mov %ecx,%r8d
0x00000000028895fe: add $0x2,%r8d
0x0000000002889602: mov %edx,%ecx
0x0000000002889604: jmp 0x00000000028895d8 ;*if_icmpge
; - Chaining::runUnChained@15 (line 58)
0x0000000002889606: add $0x20,%rsp
0x000000000288960a: pop %rbp
0x000000000288960b: test %eax,-0x26c9611(%rip) # 0x00000000001c0000
; {poll_return}
0x0000000002889611: retq
0x0000000002889612: mov $0xfffffff6,%edx
0x0000000002889617: callq 0x00000000027b7320 ; OopMap{off=284}
;*invokevirtual getB
; - Chaining::runUnChained@1 (line 57)
; {runtime_call}
0x000000000288961c: int3 ;*invokevirtual getB
; - Chaining::runUnChained@1 (line 57)
0x000000000288961d: mov $0xfffffff6,%edx
0x0000000002889622: nop
0x0000000002889623: callq 0x00000000027b7320 ; OopMap{off=296}
;*invokevirtual getC
; - Chaining::runUnChained@4 (line 57)
; {runtime_call}
0x0000000002889628: int3 ;*invokevirtual getC
; - Chaining::runUnChained@4 (line 57)
0x0000000002889629: mov $0xfffffff6,%edx
0x000000000288962e: nop
0x000000000288962f: callq 0x00000000027b7320 ; OopMap{off=308}
;*invokevirtual getD
; - Chaining::runUnChained@7 (line 57)
; {runtime_call}
0x0000000002889634: int3 ;*invokevirtual getD
; - Chaining::runUnChained@7 (line 57)
0x0000000002889635: mov $0xffffff86,%edx
0x000000000288963a: mov %ebx,%ebp
0x000000000288963c: mov %r8d,(%rsp)
0x0000000002889640: data32 xchg %ax,%ax
0x0000000002889643: callq 0x00000000027b7320 ; OopMap{rbp=NarrowOop off=328}
;*aload_2
; - Chaining::runUnChained@18 (line 60)
; {runtime_call}
0x0000000002889648: int3 ;*aload_2
; - Chaining::runUnChained@18 (line 60)
0x0000000002889649: hlt
0x000000000288964a: hlt
0x000000000288964b: hlt
0x000000000288964c: hlt
0x000000000288964d: hlt
0x000000000288964e: hlt
0x000000000288964f: hlt
0x0000000002889650: hlt
0x0000000002889651: hlt
0x0000000002889652: hlt
0x0000000002889653: hlt
0x0000000002889654: hlt
0x0000000002889655: hlt
0x0000000002889656: hlt
0x0000000002889657: hlt
0x0000000002889658: hlt
0x0000000002889659: hlt
0x000000000288965a: hlt
0x000000000288965b: hlt
0x000000000288965c: hlt
0x000000000288965d: hlt
0x000000000288965e: hlt
0x000000000288965f: hlt
[Exception Handler]
[Stub Code]
0x0000000002889660: jmpq 0x00000000028694a0 ; {no_reloc}
[Deopt Handler Code]
0x0000000002889665: callq 0x000000000288966a
0x000000000288966a: subq $0x5,(%rsp)
0x000000000288966f: jmpq 0x00000000027b6f40 ; {runtime_call}
0x0000000002889674: hlt
0x0000000002889675: hlt
0x0000000002889676: hlt
0x0000000002889677: hlt
通过比较输出,可以看出它们基本相同。在这两种情况下都有内联电话。
现在可以说doSomething
方法非常简单,它们也被内联,并且对于更复杂的doSomething
方法,结果可能会有所不同。这可能是真的。但是使用像
int doSomething(int i)
{
List<Integer> list = new ArrayList<Integer>(
Arrays.asList(1,2,3,4,5,6,7,8,9,10));
Collections.sort(list);
return list.get(i);
}
表明链接调用的实际内联仍然发生,并且当“内部”方法变得更加复杂时,由于链接调用而导致可能发生的任何潜在开销将变得可忽略不计在doSomething
方法中完成了什么。
答案 2 :(得分:0)
什么是“慢”?
显然比读取局部变量的值慢,但除此之外,方法调用非常快,你不必担心。
另一方面,像A.getB().getC().getD().doSomething1()
这样的链接会为重构而尖叫。至少在课程A
中添加一个新方法,该方法会返回D
或直接调用doSomething1()
。
答案 3 :(得分:0)
每个方法调用,操作,赋值或大多数其他方法都是针对处理器的。您提供的示例可能运行得非常快;但是每个针对处理器的滴答都会开始减慢速度。游戏开发者非常关注这一点,因为他们需要能够保持帧速率;但是一般和商业应用程序并不关心这一点。
重点是:如果链接很多或链接方法需要做很多工作,链接将开始影响性能。