람다 식은 실행될 때마다 힙에 객체를 생성합니까?


182

Java 8의 새로운 구문 설탕을 사용하여 컬렉션을 반복 할 때

myStream.forEach(item -> {
  // do something useful
});

이것은 아래의 '구문 구문'스 니펫과 같지 않습니까?

myStream.forEach(new Consumer<Item>() {
  @Override
  public void accept(Item item) {
    // do something useful
  }
});

이것은 Consumer컬렉션을 반복 할 때마다 힙에 새로운 익명 객체가 생성 된다는 것을 의미합니까 ? 얼마나 많은 힙 공간이 필요합니까? 성능에 어떤 영향을 미칩니 까? 큰 멀티 레벨 데이터 구조를 반복 할 때 루프에 대해 이전 스타일을 사용해야한다는 것을 의미합니까?


48
짧은 대답 : 아닙니다. 상태가없는 람다 (어휘 문맥에서 아무것도 캡처하지 않는 람다)의 경우 하나의 인스턴스 만 (게으름) 생성되어 캡처 사이트에 캐시됩니다. (이것은 구현이 작동하는 방식입니다; 스펙은이 접근법을 허용하지만 요구하지는 않도록 신중하게 작성되었습니다.)
Brian Goetz

답변:


158

동일하지만 동일하지 않습니다. 간단히 말해서, 람다식이 값을 캡처하지 않으면 모든 호출에서 재사용되는 싱글 톤이 될 것입니다.

동작이 정확하게 지정되지 않았습니다. JVM은 그것을 구현하는 방법에 큰 자유를 부여받습니다. 현재 Oracle의 JVM은 람다 식당 하나 이상의 인스턴스를 생성하지만 (즉, 서로 다른 동일한 식간에 인스턴스를 공유하지 않음) 값을 캡처하지 않는 모든 식에 대해 싱글 톤을 생성합니다.

자세한 내용은 이 답변 을 읽으십시오 . 더 자세한 설명뿐만 아니라 현재 동작을 관찰하기위한 테스트 코드도 제공했습니다.


이에 대한 내용은 Java® 언어 사양, 장“ 15.27.4. Lambda 식의 런타임 평가

요약 :

이러한 규칙은 다음과 같은 측면에서 Java 프로그래밍 언어 구현에 유연성을 제공하기위한 것입니다.

  • 모든 평가에 새로운 객체를 할당 할 필요는 없습니다.

  • 다른 람다 식으로 생성 된 객체는 다른 클래스에 속할 필요가 없습니다 (예를 들어 본문이 동일한 경우).

  • 평가에 의해 생성 된 모든 객체가 동일한 클래스에 속할 필요는 없습니다 (예 : 캡처 된 로컬 변수가 인라인 될 수 있음).

  • "기존 인스턴스"가 사용 가능한 경우 이전 람다 평가에서 작성하지 않아도됩니다 (예를 들어, 클래스를 초기화하는 동안 할당되었을 수 있음).


24

람다를 나타내는 인스턴스가 민감하게 생성되는 시점은 람다 본문의 정확한 내용에 따라 다릅니다. 즉, 핵심 요소는 람다 가 어휘 환경에서 캡처 하는 것입니다. 생성에서 생성까지 가변적 인 상태를 캡처하지 않으면 for-each 루프가 입력 될 때마다 인스턴스가 생성되지 않습니다. 대신 컴파일시 합성 메소드가 생성되고 람다 사용 사이트는 해당 메소드에 위임되는 싱글 톤 오브젝트 만 수신합니다.

또한이 측면은 구현에 따라 다르며 향후 더 높은 효율성을 향한 HotSpot 개선 및 개선을 기대할 수 있습니다. 예를 들어, 전체 대응 클래스가없는 경량 객체를 만들려는 일반적인 계획이 있으며, 단일 메소드로 전달하기에 충분한 정보 만 있습니다.

다음은 주제에 관한 접근하기 좋은 심층 기사입니다.

http://www.infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood


0

forEach메소드에 새 인스턴스를 전달하고 있습니다. 매번 반복 할 때마다 새 객체를 만들지 만 새 객체를 만들지는 않습니다. forEach루프로 완료 될 때까지 동일한 '콜백'객체 인스턴스를 사용하여 메소드 내부에서 반복이 수행됩니다 .

따라서 루프가 사용하는 메모리는 컬렉션의 크기에 의존하지 않습니다.

이것은 '이전 구문'스 니펫과 동일하지 않습니까?

예. 매우 낮은 수준에서 약간의 차이가 있지만 걱정하지 않아도됩니다. Lamba 표현식은 익명 클래스 대신 invokedynamic 기능을 사용합니다.


이를 지정하는 문서가 있습니까? 꽤 흥미로운 최적화입니다.
A. 라마

고맙지 만 트리 데이터 구조에서 깊이 우선 검색을 수행 할 때와 같이 컬렉션 컬렉션 컬렉션이 있으면 어떻게됩니까?
Bastian Voigt

2
@ A.Rama 죄송합니다. 최적화가 보이지 않습니다. 람다 유무와 forEach 루프 유무에 관계없이 동일합니다.
aalku

1
실제로 동일하지는 않지만 한 번에 중첩 수준 당 최대 하나의 개체가 필요하므로 무시할 수 있습니다. 내부 루프를 새로 반복 할 때마다 새 객체가 만들어지며, 아마도 아마도 외부 루프에서 현재 항목을 캡처합니다. 이로 인해 GC 압력이 발생하지만 실제로 걱정할 것은 없습니다.
Marko Topolnik

4
@ aalku : " 매번 새 객체를 만들 때마다 ": Holger의 답변Brian Goetz의 의견에 따르면 아닙니다 .
Lii
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.