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

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

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

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

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

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



답변

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

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

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


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

요약 :

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

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

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

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

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


답변

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

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

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

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


답변

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

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

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

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


답변