상태 비 저장 람다 또는 상태 저장 람다에 대해 동일한 call-site 의 빈번한 실행과 동일한 메서드 에 대한 메서드 참조 의 빈번한 사용 (다른 호출 사이트 별)을 구분해야합니다.
다음 예를보십시오.
Runnable r1=null;
for(int i=0; i<2; i++) {
Runnable r2=System::gc;
if(r1==null) r1=r2;
else System.out.println(r1==r2? "shared": "unshared");
}
여기에서 동일한 호출 사이트가 두 번 실행되어 상태 비 저장 람다를 생성하고 현재 구현은 "shared"
.
Runnable r1=null;
for(int i=0; i<2; i++) {
Runnable r2=Runtime.getRuntime()::gc;
if(r1==null) r1=r2;
else {
System.out.println(r1==r2? "shared": "unshared");
System.out.println(
r1.getClass()==r2.getClass()? "shared class": "unshared class");
}
}
두 번째 예에서는, 동일한 통화 사이트는 참조 함유 람다하여 두 번 실행 Runtime
인스턴스 인쇄 될 현재 구현 "unshared"
하지만이 "shared class"
.
Runnable r1=System::gc, r2=System::gc;
System.out.println(r1==r2? "shared": "unshared");
System.out.println(
r1.getClass()==r2.getClass()? "shared class": "unshared class");
반면에, 마지막 실시 예에서 동등한 방법 참조 제조 개의 다른 통화 사이트이지만 현재로 1.8.0_05
그것을 출력한다 "unshared"
그리고 "unshared class"
.
각 람다 표현식 또는 메서드 참조에 대해 컴파일러는 invokedynamic
클래스에서 JRE 제공 부트 스트랩 메서드를 참조 하는 명령 LambdaMetafactory
과 원하는 람다 구현 클래스를 생성하는 데 필요한 정적 인수를 내 보냅니다. 메타 팩토리가 생성하는 것은 실제 JRE에 맡겨져 있지만 첫 번째 호출에서 생성 된 인스턴스 invokedynamic
를 기억하고 재사용 하는 것은 명령 의 지정된 동작입니다 CallSite
.
현재 JRE는 상태 비 저장 람다에 대한 상수 객체에 ConstantCallSite
포함을 생성합니다 MethodHandle
(그리고 다르게 수행 할 상상할 수있는 이유가 없습니다). 메서드에 대한 메서드 참조 static
는 항상 상태 비 저장입니다. 따라서 상태 비 저장 람다 및 단일 호출 사이트의 경우 대답은 다음과 같아야합니다. 캐시하지 마십시오. JVM이 수행하고 그렇지 않은 경우 대응해서는 안되는 강력한 이유가 있어야합니다.
매개 변수 this::func
가 있고 this
인스턴스에 대한 참조가있는 람다의 경우 상황이 약간 다릅니다. JRE는 그것들을 캐싱 할 수 있지만 이것은 Map
실제 매개 변수 값과 결과 람다 사이에 일종의 유지를 의미 하므로 단순한 구조화 된 람다 인스턴스를 다시 만드는 것보다 더 많은 비용이들 수 있습니다. 현재 JRE는 상태가있는 람다 인스턴스를 캐시하지 않습니다.
그러나 이것이 람다 클래스가 매번 생성된다는 것을 의미하지는 않습니다. 이는 해결 된 호출 사이트가 첫 번째 호출에서 생성 된 람다 클래스를 인스턴스화하는 일반 개체 구성처럼 동작 함을 의미합니다.
다른 호출 사이트에서 만든 동일한 대상 메서드에 대한 메서드 참조에도 유사한 사항이 적용됩니다. JRE는 이들간에 단일 람다 인스턴스를 공유 할 수 있지만 현재 버전에서는 그렇지 않습니다. 아마도 캐시 유지 관리가 효과가 있는지 여부가 명확하지 않기 때문일 것입니다. 여기에서는 생성 된 클래스도 다를 수 있습니다.
따라서 예제와 같은 캐싱은 프로그램이없는 것과 다른 일을 할 수 있습니다. 그러나 반드시 더 효율적인 것은 아닙니다. 캐시 된 개체가 항상 임시 개체보다 더 효율적인 것은 아닙니다. 람다 생성으로 인한 성능 영향을 실제로 측정하지 않는 한 캐싱을 추가해서는 안됩니다.
캐싱이 유용 할 수있는 몇 가지 특별한 경우 만 있다고 생각합니다.
- 우리는 동일한 방법을 참조하는 다양한 호출 사이트에 대해 이야기하고 있습니다.
- 람다는 생성자 / 클래스 초기화에서 생성됩니다. 나중에 사용 사이트에서
- 여러 스레드에서 동시에 호출
- 첫 번째 호출의 성능이 저하됨