오브젝트 부활

Object resurrection

가비지 컬렉션이 있는 객체 지향 프로그래밍 언어에서 객체 부활피니셔 실행의 부작용으로 객체 파괴 프로세스 중에 객체가 다시 살아나는 것을 말합니다.

오브젝트 부활은 많은 문제를 일으킵니다.특히 오브젝트 부활의 가능성은 발생하지 않더라도 가비지 수집이 상당히 복잡해지고 속도가 느려지기 때문에 최종자가 권장되지 않습니다.언어는 다양한 방식으로 사물의 부활을 다룬다.드물게 오브젝트 부활은 특정 설계 패턴(특히 오브젝트 [1]풀)을 구현하기 위해 사용되며, 다른 상황에서는 피니셔의 오류로 인해 발생하는 바람직하지 않은 버그이며, 일반적으로 부활은 [2]권장되지 않는다.

과정

오브젝트 부활은 다음 프로세스를 통해 이루어집니다.첫 번째로 프로그램에서 오브젝트에 도달할 수 없게 되면 오브젝트가 가비지가 되어 수집(파괴 및 할당 해제)될 수 있습니다.그 후 오브젝트 파괴 중에 가비지 컬렉터가 오브젝트의 할당을 해제하기 전에 피니셔 메서드가 실행되어 피니셔에 대한 참조를 작성함으로써 오브젝트 또는 다른 가비지 오브젝트(피니셔에서 도달 가능)가 다시 도달 가능하게 되는 경우가 있습니다.이는 피니셔가 임의의 코드를 포함할 수 있기 때문입니다.이 경우 참조된 오브젝트(완료된 오브젝트라고는 할 수 없음)는 가비지가 되지 않으며 할당 해제될 수 없습니다.이렇게 하지 않으면 참조에 대한 참조가 행잉되어 사용 시 오류가 발생할 수 있으며 일반적으로 프로그램크래시 또는 예측 불가능한 동작이 발생합니다.대신 기억의 안전을 유지하기 위해 그 물체를 되살리거나 부활시킨다.

이것을 검출하기 위해서, 가비지 콜렉터는 보통 파이널라이저가 있는 경우, 2상 수집을 실시합니다.먼저 파이널라이저가 있는 가비지(또는 파이널라이저가 있는 오브젝트에서 도달 가능한 모든 가비지)를 다시 체크합니다.이로 인해 오버헤드가 증가하고 메모리 회수가 지연됩니다.

부활한 객체

부활한 물체는 다른 물체와 동일하게 취급하거나 특별 취급할 수 있다.많은 언어, 특히 C#, Java 및 Python(Python 3.4의 경우)에서는 오브젝트가 반복적으로 부활하거나 파괴되지 않도록 오브젝트가 한 번만 완성됩니다.C# 오브젝트에서는 기본적으로 피니셔가 있는 오브젝트는 한 번만 완성되지만 최종화를 위해 다시 등록할 수 있습니다.다른 경우에 부활된 오브젝트는 Objective-C에서 오류로 간주되거나 Python 3.4보다 이전 Python에서 다른 오브젝트와 동일하게 취급됩니다.

부활한 오브젝트를 좀비 오브젝트 또는 좀비라고 부르기도 하지만, 이 용어는 오브젝트 파괴와 관련된 다양한 오브젝트 상태를 나타내며, 언어와 작성자에 따라 사용법이 달라집니다.그러나 "좀비 객체"는 Objective-C에서 특별한 의미를 가지며, 이는 아래에 자세히 설명되어 있다.좀비 오브젝트는 종료 상태가 변경되어 할당 해제에 가깝다는 점에서 좀비 프로세스와 다소 유사하지만 세부 사항은 크게 다릅니다.

변종

에서.NET Framework, 특히 C# 및 VB.NET, 대신 "객체 부활"은 최종화 중 객체의 상태를 말합니다. 객체는 다시 살아나고(액세스 불능에서), 최종자는 실행되며, 그 후 다시 액세스할 수 없게 됩니다(그리고 더 이상 최종화를 위해 등록되지 않습니다)..NET에서는 완성이 필요한 오브젝트는 객체별로 추적되는 것이 아니라 완성이 완료된 "큐"[a]에 저장되기 때문에 이 문서의 의미에서는 부활된 오브젝트의 개념이 아니라 "완성을 위해 큐잉된" 오브젝트를 말합니다.또한 오브젝트를 다시 큐잉하여 최종화할 수 있습니다.GC.ReRegisterForFinalizeenqueue [2]객체를 여러 개 사용하지 않도록 주의하십시오.

메커니즘

오브젝트가 자기 자신 또는 다른 오브젝트를 부활시키는 방법은 크게 두 가지가 있습니다.즉, 도달 가능한 오브젝트에 대한 참조를 작성(가비지는 도달할 수 없지만 가비지는 불필요한 오브젝트를 참조할 수 있음)하거나 환경(글로벌 변수, 경우에 따라서는 정적 변수 또는 폐쇄 의 변수)입니다.Python의 예는 자신을 부활시키는 오브젝트에 대한 것입니다.또한 동일한 메커니즘에 의해 주어진 가비지 수집 사이클에서 둘 다 수집되는 경우 개체가 다른 개체를 부활시킬 수도 있습니다.

도달할 수 있는 객체에 참조를 생성하여 자동으로 부활합니다.

학급 끈적끈적하다:     방어하다 __init__(자신, 레퍼런스=없음.) -> 없음.:         자신.레퍼런스 = 레퍼런스              방어하다 __del__(자신):         한다면 자신.레퍼런스:             자신.레퍼런스.레퍼런스 = 자신         인쇄물("날 떠나지 마!")  a = 끈적끈적하다(끈적끈적하다())  # 2-element 링크 목록을 만듭니다.                       에 의해 참조된 번호 a.레퍼런스.레퍼런스 = a  # 사이클 작성 a.레퍼런스 = 없음.  # 첫 번째 노드에서 참조 클리어               # 1초면 두번째 쓰레기가 돼 a.레퍼런스 = 없음. 

글로벌 환경에서 참조를 생성하여 자체 부활:

c = 없음. 학급 불후의:     방어하다 __del__(자신):         세계적인 c         c = 자신         인쇄물("난 아직 죽지 않았어.")  c = 불후의() c = 없음.  # c를 클리어하면 오브젝트가 가비지가 됩니다. c = 없음. 

위의 예에서는 3.4 이전 CPython에서는 이들 파이널라이저가 반복적으로 실행되며 오브젝트는 가비지 수집되지 않습니다.한편 CPython 3.4 이후에서는 파이널라이저가1번 호출되고 오브젝트는 두 번째로 도달할 수 없게 되었을 때 가비지 수집됩니다.

문제

오브젝트 부활은 많은 문제를 일으킨다.

가비지 수집을 복잡하게 하다
오브젝트 부활 가능성은 가비지 컬렉터가 실제로 발생하지 않더라도 최종 처리 후 부활된 오브젝트를 확인해야 함을 의미하며, 이로 인해 가비지 수집이 복잡해지고 속도가 느려집니다.
파괴할 수 없는 물체
경우에 따라서는 오브젝트는 파괴할 수 없는 경우가 있습니다.개체가 자신의 피니셔(또는 피니셔의 결과로 서로 부활하는 오브젝트 그룹)에서 부활하고 피니셔가 오브젝트를 파괴할 때 항상 호출되면 오브젝트를 파괴할 수 없고 메모리를 회수할 수 없습니다.
우발적인 부활 및 누출
셋째, 오브젝트 부활은 의도하지 않은 것일 수 있으며, 결과 오브젝트는 의미적인 가비지일 수 있으며, 따라서 실제로 수집되지 않아 논리 메모리 누설이 발생할 수 있습니다.
일관성이 없는 상태 및 재초기화
피니셔가 실행되어 불규칙한 상태가 되었기 때문에 부활된 오브젝트는 부정합 상태이거나 클래스 불변성을 위반할 수 있다.따라서 부활된 개체는 일반적으로 수동으로 다시 [1]초기화해야 합니다.
독자적인 최종화 또는 재최종화
일부 언어(Java 및 Python 3.4+ 등)에서는 오브젝트당 정확히 한 번 완성이 보장되므로 부활된 오브젝트는 피니셔를 호출하지 않습니다.따라서 부활된 오브젝트는 피니셔 외부에서 필요한 정리 코드를 실행해야 합니다.일부 다른 언어에서는 프로그래머가 반복적으로 완료하도록 강제할 수 있습니다. 특히 C#은GC.ReRegisterForFinalize를 클릭합니다.[1]

솔루션

언어는 오브젝트 부활에 대처하기 위해 몇 가지 다른 방법을 채택했습니다.대부분은 최종자가 존재하는 상황에서 2상 가비지 수집을 함으로써 매달리는 참조를 방지하고 오브젝트를 한 번만 완성함으로써 오브젝트를 (특히 플래그를 통해) 파괴할 수 있는 오브젝트를 마킹함으로써 오브젝트를 완성하는 것입니다.

Java는 오브젝트가 다시 도달할 수 없음을 증명할 때까지 오브젝트를 해방하지 않지만 피니셔를 두 [3]번 이상 실행하지 않습니다.

Python 3.4 이전의 Python에서는 표준 CPython 구현이 부활된 오브젝트를 다른 오브젝트(완료되지 않은 오브젝트)와 동일하게 취급하여 파괴할 수 없는 오브젝트를 [4]가능하게 했습니다.또한 오브젝트 부활에 관한 문제를 피하기 위해 오브젝트가 포함된 수집 사이클을 finalizer로 폐기하지 않습니다.Python 3.4부터 동작은 Java와 [b]거의 동일합니다. 객체는 한 번만 완료되고(이미 완료되었다고 표시됨), 사이클의 가비지 컬렉션은 두 단계로 나누어지며, 두 번째 단계에서는 부활된 [5][6]객체가 있는지 확인합니다.

Objective-C 2.0은 부활한 오브젝트를 "좀비" 상태로 만듭니다.여기서 오브젝트에게 전송된 모든 메시지를 기록하지만 다른 작업은 [7]하지 않습니다.약한 참조의 처리에 대해서는, 「자동 참조 카운트: 약한 참조 제로 설정」을 참조해 주세요.

에서.NET Framework, 특히 C# 및 VB.NET, 객체 최종화는 객체 파괴 시 체크되는 최종화 "큐"[a]에 의해 결정됩니다.파이널라이저가 있는 오브젝트는 작성 시 이 큐에 배치되며 파이널라이저가 호출될 때 큐를 해제하지만 (파이널라이제이션 전에) 수동으로 큐를 해제할 수 있습니다.SuppressFinalize또는 로 다시 큐에 넣을 수 있습니다.ReRegisterForFinalize따라서 디폴트로는 피니셔가 있는 오브젝트는 한 번에 완료되지만, 이 피니셔를 억제하거나 오브젝트가 부활(재접근 가능)한 후 다시 큐잉하여 피니셔를 할 경우 오브젝트를 여러 번 완료할 수 있습니다.또한 약한 참조는 기본적으로 부활을 추적하지 않습니다. 즉, 물체가 부활해도 약한 참조는 업데이트되지 않습니다. 이러한 참조를 짧은 약한 참조라고 하며, 부활을 추적하는 약한 참조를 긴 [8]약한 참조라고 합니다.

적용들

오브젝트 부활은 일반적으로 사용되는 오브젝트의 오브젝트 풀을 처리하는 데 유용하지만 코드를 흐리게 하여 혼란을 [3]가중시킵니다.자주 사용할 수 있는 물체와 시공/파괴에 시간이 많이 걸리는 물체에 대해서만 사용해야 합니다.예를 들어 다수의 난수가 단시간에 생성 및 파괴되지만 실제로는 소수의 수만이 동시에 사용되는 난수 배열이 있습니다.객체 부활과 함께, 공동화 기법은 창조와 파괴의 불필요한 오버헤드를 줄일 것이다.여기서 풀 매니저는 오브젝트가 현재 파기되어 있는 경우 오브젝트에 대한 참조 형식으로 오브젝트 스택 정보에 액세스합니다.풀 관리자는 나중에 [9]재사용할 수 있도록 개체를 유지합니다.

「 」를 참조해 주세요.

메모들

  1. ^ a b 이것은 엄밀하게는 큐가 아닙니다.요소는 중간에서 삭제할 수 있기 때문입니다.GC.SuppressFinalization.
  2. ^ CPython은 별도의 사이클 디텍터를 사용하여 비사이클 가비지에 참조 카운트를 사용하는 반면 Java 구현의 대부분은 추적 가비지 컬렉터를 사용합니다.

레퍼런스

  1. ^ a b c Goldshtein, Zurbalev & Flatow 2012, 페이지 129.
  2. ^ a b 리히터 2000년
  3. ^ a b "What is resurrection (in garbage collection)?". http://www.xyzws.com/: XYZWS. Retrieved 2011-08-01. An object that has been eligible for garbage collection may stop being eligible and return to normal life. Within a finalize() method, you can assign this to a reference variable and prevent that object's collection, an act many developers call resurrection. /The finalize() method is never called more than once by the JVM for any given object. The JVM will not invoke finalize() method again after resurrection (as the finalize() method already ran for that object). {{cite web}}:외부 링크 location=(도움말)
  4. ^ "파이썬에서 개체당 __del__을 몇 번 호출할 수 있습니까?"에 대한 팀 피터스의 답변입니다.
  5. ^ Python 3.4신기능, PEP 442: Safe Object Finalization
  6. ^ Pitrou, Antoine (2013). "PEP 442 -- Safe object finalization".
  7. ^ 최종 방법의 실장
  8. ^ Goldshtein, Zurbalev & Flatow 2012, 페이지 131.
  9. ^ "Object resurrection" (PDF). http://www.hesab.net/: Hesab.net. Retrieved 2011-08-01. Object resurrection is an advanced technique that’s likely to be useful only in unusual scenarios, such as when you’re implementing a pool of objects whose creation and destruction is time-consuming. ... The ObjectPool demo application shows that an object pool manager can improve performance when many objects are frequently created and destroyed. Assume that you have a RandomArray class, which encapsulates an array of random numbers. The main program creates and destroys thousands of RandomArray objects, even though only a few objects are alive at a given moment. Because the class creates the random array in its constructor method (a timeconsuming operation), this situation is ideal for a pooling technique. ... The crucial point in the pooling technique is that the PoolManager class contains a reference to unused objects in the pool (in the PooledObjects Stack object), but not to objects being used by the main program. In fact, the latter objects are kept alive only by references in the main program. When the main program sets a RandomArray object to Nothing (or lets it go out of scope) and a garbage collection occurs, the garbage collector invokes the object’s Finalize method. The code inside the RandomArray’s Finalize method has therefore an occasion to resurrect itself by storing a reference to itself in the PoolManager’s PooledObjects structure. So when the NewRandomArray function is called again, the PoolManager object can return a pooled object to the client without going through the time-consuming process of creating a new one. {{cite web}}:외부 링크 location=(도움말)