2

여기에 포함 된 인스턴스는 동봉 된 인스턴스가 살아있는 한 가비지 수집 대상이 아닙니다.가비지 콜렉션에 대해 인스턴스를 둘러 쌀 수있는 해결 방법

interface Foo<T> { 
    T compute(); 

    default Foo<T> memoize() { 
     return new Foo<>() { 
      T result = null; 

      boolean cached = false; 

      @Override 
      public T compute() { 
       if (!cached) { 
        result = Foo.this.compute(); 
        cached = true; 
       } 
       return result; 
      } 
     }; 
    } 
} 

이렇게하면 문제가 해결됩니까?

interface Foo<T> { 
    T compute(); 

    default Foo<T> memoize() { 
     return Foo.memoize(this); 
    } 

    private static <T> Foo<T> memoize(Foo<T> original) { 
     class Bar implements Foo<T> { 
      T result = null; 

      Foo<T> foo; 

      Bar(Foo<T> original) { 
       foo = original; 
      } 

      @Override 
      public T compute() { 
       if (foo != null) { 
        result = foo.compute(); 
        foo = null; 
       } 
       return result; 
      } 
     } 
     return new Bar(original); 
    } 
} 
+0

이것이 [최소] (https://stackoverflow.com/help/mcve) 확신합니까? – Michael

+1

@ 마이클 나는 그렇게 믿는다. – gdejohn

답변

1

첫 번째 경우 익명 내부 클래스는 Foo.this을 사용하여 동봉하는 Foo에 대한 참조를 '캡처'합니다. 익명 클래스는이 참조를 무기한 보관하므로 익명 클래스도 자격이 될 때까지 동봉 된 Foo은 가비지 수집 대상이 아닙니다.

두 번째 예에서는 compute이 한 번 호출 된 후이 참조가 삭제되므로 이후에 그 둘러싸는 클래스가 가비지 수집 대상입니다.

람다 표현식으로, 람다 표현식을 사용하는 대안이 있다고 Accessing Members of an Enclosing Class

0

아니, 왜 그럴까요?

memoize() 함수 내의 Bar 인스턴스에는 Foo 객체를 보유하는 필드가 있습니다. 범위 지정을 통해 또는 암시 적 필드 할당을 통해 보유 여부는 가비지 수집과 관련이 없습니다.

가비지 수집 될 수있는 개체에 대한 참조를 유지하려면 WeakReference/SoftReference을 사용하십시오.

+1

Bar에서'foo = null' 명령을 놓쳤습니다. –

+0

잠시 전에 거기에 없었 음을 맹세 했겠습니까?) 네, 맞습니다. 이 경우 필드가 null로 설정되면 'Foo'가 가비지 수집 될 수 있습니다. 정적 내부 클래스는 그 점에서 외부 클래스와 똑같이 작동합니다. –

3

참고를 참조하십시오 필요한, 예를 들어, 같은 단지 캡처 값

interface Foo<T> { 
    T compute(); 

    default Foo<T> memoize() { 
     AtomicReference<Foo<T>> holder = new AtomicReference<>(); 
     holder.set(() -> { 
       T result = compute(); 
       holder.set(() -> result); 
       return result; 
      } 
     ); 
     return() -> holder.get().compute(); 
    } 
} 

holder

처음 첫 평가를 compute 호출되지만, 그 후, 그 새로운 람다 Foo<T>을 자체 구현 대체 의지로 람다 원래 Foo<T> 인스턴스에 대한 참조가 Foo<T> 구현 포함하는 불변 것 계산 된 값을 반환합니다. 새로운 람다는 값이 이미 계산 된 사실을 암시하기 때문에 조건부를 평가할 필요조차 없습니다.

홀더 객체는 여기에 AtomicReference 일 필요는 없지만 표준적인 사용법이 간단하다는 점에 유의하십시오. 일반 배열을 만들 수 없으므로 마음에 들어오는 유일한 대안은 크기가 1 인 (변경 가능) 목록입니다 (예 : Arrays.asList(null)을 통해 생성되었습니다.

+0

내 신이 독창적이다! 나는 이것을 "돌연변이"람다라고 부르는 방법을 "스스로 돌연변이하는"람다라고 부르기도하지 않습니까? 너무 똑똑한 waaaay – Eugene