문제

오늘 저의 동료들과 저의 사용법에 대해 토론했습니다. final 쓰레기 수집을 개선하기위한 Java의 키워드.

예를 들어, 다음과 같은 메소드를 작성하는 경우

public Double doCalc(final Double value)
{
   final Double maxWeight = 1000.0;
   final Double totalWeight = maxWeight * value;
   return totalWeight;  
}

메소드의 변수를 선언합니다 final 방법이 종료 된 후 메소드에서 사용되지 않은 변수에서 쓰레기 수집이 메모리를 정리하는 데 도움이됩니다.

이것이 사실입니까?

도움이 되었습니까?

해결책

여기에는 최종 값 유형 로컬 변수가 아닌 최종 참조 유형 필드가있는 약간 다른 예입니다.

public class MyClass {

   public final MyOtherObject obj;

}

MyClass 인스턴스를 만들 때마다 MyOtherObject 인스턴스에 대한 나가는 참조를 만들고 GC는 해당 링크를 따라 라이브 객체를 찾아야합니다.

JVM은 Mark-Sweep GC 알고리즘을 사용합니다.이 알고리즘은 GC "루트"위치 (현재 통화 스택의 모든 객체와 같이)의 모든 라이브 참조를 검사해야합니다. 각 라이브 객체는 살아있는 것으로 표시되며, 살아있는 객체로 언급 된 모든 물체도 살아있는 것으로 표시됩니다.

마크 단계가 완료된 후, GC는 힙을 통해 스윕하여 모든 표시되지 않은 물체의 메모리를 제거하고 (나머지 라이브 객체에 대한 메모리를 압축).

또한 Java Heap 메모리가 "젊은 세대"와 "구 세대"로 분할되어 있음을 인식하는 것이 중요합니다. 모든 물체는 처음에 젊은 세대에 할당됩니다 (때로는 "보육원"이라고도 함). 대부분의 물체는 수명이 짧기 때문에 GC는 젊은 세대로부터 최근 쓰레기를 자유롭게하는 것에 대해 더 공격적입니다. 물체가 젊은 세대의 수집주기에서 살아남는 경우, 구식 (때로는 "임기 세대"라고도 함)으로 이동하여 덜 자주 처리됩니다.

그래서 내 머리 꼭대기에서 "아니오, '최종'모델은 GC가 작업량을 줄이는 데 도움이되지 않습니다"라고 말할 것입니다.

제 생각에, Java에서 메모리 관리를 최적화하기위한 최선의 전략은 가능한 한 빨리 가짜 참조를 제거하는 것입니다. 사용을 마치 자마자 "null"을 객체 참조에 할당하여 그렇게 할 수 있습니다.

또는 더 나은 아직, 각 선언 범위의 크기를 최소화하십시오. 예를 들어, 1000 라인 방법의 시작 부분에서 객체를 선언하고 해당 방법의 범위가 닫힐 때까지 객체가 살아있는 경우 (마지막으로 닫힌 곱슬 브레이스) 물체는 실제로 훨씬 더 오래 살아남을 수 있습니다. 필요한.

12 개 정도의 코드 라인만으로 작은 방법을 사용하는 경우 해당 메소드 내에서 선언 된 개체는 더 빠르게 범위가 떨어지고 GC는 대부분의 작업 내에서 대부분의 작업을 수행 할 수 있습니다. 젊은 세대. 절대적으로 필요하지 않는 한 객체가 구형 세대로 이동하는 것을 원하지 않습니다.

다른 팁

로컬 변수를 선언합니다 final 쓰레기 수집에 영향을 미치지 않으므로 변수를 수정할 수 없다는 것을 의미합니다. 변수를 수정하므로 위의 예제는 컴파일이 아닙니다. totalWeight 표시되었습니다 final. 반면에, 원시를 선언합니다 (double 대신에 Double) final Will을 사용하면 해당 변수가 호출 코드에 상거할 수 있으므로 메모리 및 성능 향상이 발생할 수 있습니다. 이것은 여러분이 여러 가지가있을 때 사용됩니다 public static final Strings 수업에서.

일반적으로 컴파일러와 런타임은 가능한 위치를 최적화합니다. 코드를 적절하게 작성하고 너무 까다 롭지 않도록하는 것이 가장 좋습니다. 사용 final 변수가 수정되기를 원하지 않는 경우. 컴파일러가 쉽게 최적화 할 수 있다고 가정하고 성능 또는 메모리 사용에 대해 걱정하는 경우 프로파일 러를 사용하여 실제 문제를 결정하십시오.

아니요, 그것은 사실이 아닙니다.

기억 final 상수를 의미하지는 않지만 참조를 변경할 수 없다는 것을 의미합니다.

final MyObject o = new MyObject();
o.setValue("foo"); // Works just fine
o = new MyObject(); // Doesn't work.

JVM이 참조를 수정할 필요가 없다는 지식에 근거하여 약간의 최적화가있을 수 있지만 (예 : 변경되었는지 확인하지 않은 것과 같이) 걱정하지 않을 정도로 미미합니다.

Final 컴파일러 최적화가 아닌 개발자에게 유용한 메타 데이터로 생각해야합니다.

정리해야 할 사항 :

  • 참조를 무효화하는 것은 GC에 도움이되지 않아야합니다. 그렇게한다면 변수가 범위를 띠 었음을 나타냅니다. 한 가지 예외는 대상의 네 포티 즘의 경우입니다.

  • Java에서는 아직 온 스택 할당이 없습니다.

  • 변수 최종 선언은 (정상 조건에서) 해당 변수에 새 값을 할당 할 수 없다는 것을 의미합니다. Final은 범위에 대해 아무 말도하지 않기 때문에 GC에 미치는 영향에 대해 아무 말도하지 않습니다.

글쎄, 나는이 경우 "최종"수정 자의 사용 또는 GC에 미치는 영향에 대해 모른다.

그러나 나는 ~할 수 있다 이것을 말하십시오 : 프리미티브가 아닌 박스형 값을 사용하면 (예를 들어, 더블 대신 이중)는 스택 대신 더미에 해당 객체를 할당하고 GC가 청소 해야하는 불필요한 쓰레기를 생성합니다.

기존 API가 필요할 때만 박스형 프리미티브 만 사용하거나 귀중한 프리미터가 필요할 때만 사용합니다.

최초 할당 후 (컴파일러에 의해 시행 됨) 최종 변수는 변경할 수 없습니다.

이것은 동작을 바꾸지 않습니다 쓰레기 수집 따라서. 유일한 것은 이러한 변수를 더 이상 사용하지 않을 때는 무효화 할 수 없다는 것입니다 (메모리 꽉 상황에서 쓰레기 수집에 도움이 될 수 있음).

Final을 사용하면 컴파일러가 최적화 할 내용에 대한 가정을 할 수 있습니다. 코드를 포함하지 않고 도달 할 수없는 것으로 알려진 코드를 포함하지 않습니다.

final boolean debug = false;

......

if (debug) {
  System.out.println("DEBUG INFO!");
}

Println은 바이트 코드에 포함되지 않습니다.

GC는 도달 할 수없는 심판에 작용합니다. 이것은 단지 일회성 과제에 대한 주장 일뿐입니다. 일부 VM의 GC가 "최종"을 사용할 수 있습니까? 어떻게 또는 이유를 알지 못합니다.

세대의 쓰레기 수집가와 잘 알려지지 않은 코너 케이스가 있습니다. (간단한 설명은 답을 읽으십시오 Benjismith 더 깊은 통찰력은 마지막에 기사를 읽으십시오).

세대 GC의 아이디어는 대부분의 시간만이 젊은 세대 만 고려해야한다는 것입니다. 루트 위치는 참조를 위해 스캔 한 다음 젊은 세대 객체를 스캔합니다. 이 더 자주 청소하는 동안 구식의 개체는 확인되지 않습니다.

이제 문제는 객체가 젊은 물체에 대한 참조를 가질 수 없다는 사실에서 비롯됩니다. 오래 살았던 (구식) 물체가 새로운 객체에 대한 참조를 얻을 때, 그 참조는 쓰레기 수집기가 명시 적으로 추적해야합니다 (IBM의 기사 참조. 핫스팟 JVM 수집기), 실제로 GC 성능에 영향을 미칩니다.

오래된 물체가 더 어린 객체를 언급 할 수없는 이유는 오래된 개체가 작은 컬렉션에서 확인되지 않기 때문에 객체에 대한 유일한 참조가 이전 객체에 보관되면 표시되지 않으며 잘못되기 때문입니다. 스윕 스테이지에서 처리되었습니다.

물론, 많은 사람들이 지적한 바와 같이, 최종 키워드는 쓰레기 수집기에 실제로 영향을 미치지 않지만,이 개체가 작은 컬렉션에서 살아남고 오래된 힙으로 만들면 참조가 더 어린 객체로 변경되지 않을 것을 보장합니다.

조항:

쓰레기 수집에 대한 IBM : 역사,에서 핫스팟 JVM 그리고 성능. 2003/04 년에 거슬러 올라가기 때문에 더 이상 완전히 유효하지 않을 수 있지만 GC에 대한 통찰력을 쉽게 읽을 수 있습니다.

태양 튜닝 쓰레기 수집

final 로컬 변수와 매개 변수에서 생성 된 클래스 파일에 차이가 없으므로 런타임 성능에 영향을 줄 수 없습니다. 클래스에 서브 클래스가없는 경우 핫스팟은 해당 클래스를 어쨌든 최종 인 것처럼 취급합니다 (가정을 중단하는 클래스가로드되면 나중에 실행 취소 할 수 있음). 나는 믿는다 final 방법은 클래스와 거의 동일합니다. final 정적 필드에서 변수를 "컴파일 타임 상수"로 해석하고 그 기준으로 JAVAC에 의해 최적화를 수행 할 수 있습니다. final 필드에서 JVM은 자유를 무시할 수 있습니다 앞서 발생합니다 처지.

추측을 방황하는 많은 답이있는 것 같습니다. 진실은, 바이트 코드 수준에서 로컬 변수에 대한 최종 수정자는 없습니다. 가상 머신은 로컬 변수가 최종적으로 정의되었다는 것을 결코 알지 못할 것입니다.

귀하의 질문에 대한 답은 강조적입니다.

모든 메소드와 변수는 하위 클래스에서 ByDefault를 재정의 할 수 있습니다. SuperClass의 멤버에서 하위 클래스를 저장하려면 키워드 최종 결승을 사용하여 최종으로 선언 할 수 있습니다. 예를 들어 final int a=10; final void display(){......} 메소드를 최종적으로 만들면 슈퍼 클래스에 정의 된 기능이 어쨌든 변경되지 않도록합니다. 마찬가지로 최종 변수의 값은 절대 변경 될 수 없습니다. 최종 변수는 클래스 변수와 같습니다.

내가 생각할 수있는 유일한 것은 컴파일러가 최종 변수를 최적화하고 코드에 상수로 인라인 할 수 있다는 것입니다. 따라서 메모리가 할당되지 않아도됩니다.

물론, 메모리 관리의 큰 이점을 얻는 객체의 수명이 짧아지는 한, 최근에 우리는 한 번의 테스트에서 인스턴스 변수를 갖는 수출 기능과 방법 레벨 로컬 변수를 갖는 다른 테스트를 조사했습니다. 로드 테스트 중에 JVM은 첫 번째 테스트에서 OutofMemoryError를 던지고 JVM이 중단되었습니다. 그러나 두 번째 테스트에서는 더 나은 메모리 관리로 인해 보고서를 성공적으로 얻을 수 있습니다.

로컬 변수를 최종으로 선언하는 유일한 시간은 다음과 같습니다.

  • 가지다 익명의 클래스와 공유 할 수 있도록 최종적으로 만들기 위해 (예 : 데몬 스레드 생성 및 Enclosing Method에서 일부 값에 액세스하도록하십시오)

  • 원하다 최종적으로 만들려면 (예 : 실수로 무시되지 않는 일부 가치)

그들은 빠른 쓰레기 수집에 도움이됩니까?
Afaik A 객체는 GC 컬렉션의 후보가됩니다.이 경우에도 강력한 참조가 있고이 경우 즉시 쓰레기가 수집 될 것이라는 보장은 없습니다. 일반적으로, 강력한 참조는 범위를 벗어나거나 사용자가 명시 적으로 널 참조로 재 할 때 죽는다 고 말하면, 최종적으로 최종 의미는 메소드가 존재할 때까지 (그 범위가 명시 적으로 좁아지지 않는 한) 참조가 계속 존재한다는 것을 선언합니다. 최종 변수를 재 할당 할 수 없기 때문에 특정 내부 블록 {}). 그래서 나는 WRT 쓰레기 수집 '최종'이라고 생각합니다. 5월 원치 않는 가능한 지연을 도입하십시오.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top