비 결정적 자원 관리가 유출 된 추상화인가?


9

내가 볼 수 있듯이, 결정 론적 파괴와 명백한 두 가지 형태의 자원 관리가 있습니다. 전자의 예는 C ++ 소멸자 및 스마트 포인터 또는 Perl의 DESTROY 하위이고 후자의 예는 Ruby의 관리 대상 자원 패러다임 또는 .NET의 IDispose 인터페이스입니다.

최신 언어는 후자를 선택하는 것으로 보이며, 비 참조 계수 품종의 가비지 수집 시스템을 사용하는 부작용 일 수 있습니다.

내 질문은 이것입니다 : 스마트 포인터 또는 참조 계산 가비지 수집 시스템의 소멸자 (거의 동일한 것)는 암시적이고 투명한 리소스 파괴를 허용한다는 점에서 명시 적에 의존하는 비 결정적 유형보다 덜 누출 적입니다. 표기법?

구체적인 예를 들어 보겠습니다. 단일 수퍼 클래스에 세 개의 C ++ 서브 클래스가있는 경우 특정 소멸이 필요하지 않은 구현이있을 수 있습니다. 아마도 그것은 다른 방식으로 마술을 수행합니다. 특별한 파괴가 필요하지 않다는 사실은 관련이 없습니다. 모든 서브 클래스는 여전히 똑같은 방식으로 사용됩니다.

또 다른 예는 Ruby 블록을 사용합니다. 두 개의 서브 클래스가 자원을 해제해야하므로 수퍼 클래스는 생성자를 사용하는 인터페이스를 선택합니다. 다른 특정 서브 클래스는 특별한 소멸이 필요하지 않으므로 필요하지 않을 수도 있습니다.

후자는 자원 파괴에 대한 구현 세부 사항을 유출하지만 전자는 그렇지 않습니까?

편집 : 비교하자면, 루비 투 펄 (Ruby to Perl)은 결정 론적 파괴가 있고 다른 하나는 결정적이지 않기 때문에 더 공정 할 수 있지만 둘 다 가비지 수집됩니다.


5
나는 "예"라고 말하고 싶은 마음이 들지만 다른 사람들이 이것에 대해 말하는 것을 듣고 싶습니다.
Bart van Ingen Schenau

투명한 자원 파괴? 일반 포인터 대신 스마트 포인터를 사용해야한다는 사실 외에도? 나는 이것이 객체에 액세스하기위한 하나의 메커니즘 (참조)을 갖는 것보다 더 투명하다고 생각하지 않습니다 (C ++에서는 적어도 4 또는 5가 있습니다).
Giorgio

@Giorgio : "객체에 접근하는 방법"은 매우 모호합니다. 읽거나 쓰는 것을 의미합니까? Const / Volatile 자격? 포인터는 실제로 "객체에 액세스하는 방법"이 아닙니다. 거의 모든 표현으로 인해 객체가 생겨 포인터를 역 참조하는 것은 그다지 특별하지 않습니다.
MSalters

1
@ Giorgio : OOP 의미에서 C ++ 포인터에 메시지를 보낼 수 없습니다. 메시지를 보내 (*ptr).Message()거나 이에 상응 하는 포인터를 역 참조해야합니다 ptr->Message(). 마찬가지로 허용되는 표현식의 무한 세트 ((*ptr))->Message가 있습니다. 그러나 그들은 모두 expressionIdentifyingAnObject.Message()
귀결

1
재 계산을 사용하면 서클을 피하는 데주의해야합니다. 추상화도 다른 방식으로 누출됩니다.
코드 InChaos

답변:


2

당신의 예가 그 질문에 대답합니다. 투명한 파괴는 명백한 파괴보다 누설이 적습니다. 누출 될 수 있지만 누출이 적습니다.

명백한 파괴는 모든 함정이있는 C의 malloc / free와 유사합니다. 어쩌면 구문 기반 설탕으로 범위 기반으로 보일 수도 있습니다.

명시 적 이상 투명 파괴의 장점 중 일부 :
--same 사용 패턴을
알려줄까요 리소스를 해제하는 것을 잊지 수 있습니다.
-세척 정보는 사용 시점의 지형을 어지럽히 지 않습니다.


2

추상화의 실패는 실제로 가비지 수집이 결정적이지 않다는 사실이 아니라, 객체가 참조를 보유하고있는 것에 관심이 있고, 보유하지 않은 것에 관심이 없다는 생각에있다. 참조. 이유를 확인하려면 특정 컨트롤이 얼마나 자주 페인트되는지 카운터를 유지하는 개체의 시나리오를 고려하십시오. 생성시 컨트롤의 "페인트"이벤트를 구독하고 구독을 취소합니다. click 이벤트는 단순히 필드를 증가시키고 메서드 getTotalClicks()는 해당 필드의 값을 반환합니다.

카운터 개체가 만들어지면 모니터링 대상 컨트롤 내에 자체 참조가 저장되도록해야합니다. 컨트롤은 실제로 카운터 개체를 신경 쓰지 않으며 카운터 개체와 해당 참조가 존재하지 않는 경우에도 행복하지만 참조가 존재하는 한 매번 해당 개체의 이벤트 처리기를 호출합니다. 자체 페인트. 이 동작은 컨트롤에 전혀 쓸모가 없지만 getTotalClicks()객체를 호출하는 모든 사람에게 유용 합니다.

예를 들어, 새로운 "페인트 카운터"객체를 생성하고, 컨트롤에 대한 조치를 수행하고, 컨트롤이 다시 페인트 된 횟수를 관찰 한 다음, 페인트 카운터 오브젝트를 버리는 경우, 오브젝트는 이벤트를 구독 한 상태로 유지됩니다. 그러나 객체와 객체에 대한 모든 참조가 단순히 사라지면 아무도 신경 쓰지 않을 것입니다. 그러나 컨트롤 자체가 될 때까지 개체를 수집 할 수 없게됩니다. 이 방법이 컨트롤의 수명 내에서 수천 번 호출 된 방법 인 경우 (그럴듯한 시나리오), 메모리 오버플로가 발생할 수 있지만 N 호출 비용은 O (N ^ 2) 또는 O 일 가능성이 있습니다 (N ^ 3) 서브 스크립 션 처리가 매우 효율적이지 않고 대부분의 작업에 실제로 페인팅이 포함되지 않은 경우.

이 특정 시나리오는 컨트롤이 카운터 개체에 대해 강한 참조가 아닌 약한 참조를 유지하도록하여 처리 할 수 ​​있습니다. 약한 구독 모델은 도움이되지만 일반적인 경우에는 작동하지 않습니다. 단일 컨트롤에서 단일 종류의 이벤트를 모니터링하는 개체 대신 여러 컨트롤을 모니터링하는 이벤트 로거 개체를 원하고 시스템의 이벤트 처리 메커니즘을 사용하여 각 컨트롤에 참조가 필요하다고 가정합니다. 다른 이벤트 로거 개체에. 이 경우, 제어를 이벤트 로거에 연결하는 오브젝트는 둘 다 오래 지속되어야 합니다.모니터링되는 제어 및 이벤트 로거는 여전히 유용합니다. 컨트롤이나 이벤트 로거가 연결 이벤트에 대한 강력한 참조를 보유하지 않으면 여전히 "유용한"경우에도 존재하지 않습니다. 둘 중 하나가 강력한 이벤트를 보유하면 링크 된 오브젝트의 수명이 다른 오브젝트가 사망하더라도 쓸모 없게 연장 될 수 있습니다.

유니버스의 어느 곳에도 개체에 대한 참조가 없으면 해당 개체는 무용지물로 간주되어 존재하지 않을 수 있습니다. 그러나 객체에 대한 참조가 존재한다고해서 해당 객체가 "유용하다"는 것은 아닙니다. 많은 경우, 객체의 실제 유용성은 GC 관점에서 볼 때 다른 객체 에 대한 참조가 있는지 여부에 달려 있습니다.

아무도 관심이 없을 때 결정적으로 객체에 통지하면 해당 정보를 사용하여 해당 지식을 활용할 수있는 사람에게 정보를 제공 할 수 있습니다. 그러나 그러한 통지가없는 경우, 존재하는 참조 세트 만 알고 해당 참조에 첨부 된 의미 적 의미를 알지 못하는 경우 "유용한"것으로 간주되는 오브젝트를 판별하는 일반적인 방법은 없습니다. 따라서 GC가 즉시 객체 포기를 감지 할 수 있더라도 참조의 존재 또는 부재가 자동화 된 자원 관리에 충분하다고 가정하는 모든 모델은 파멸 될 것입니다.


0

아니오 "소멸자 또는"이 클래스는 반드시 폐기되어야합니다 "라고 말하는 다른 인터페이스는 해당 인터페이스의 계약입니다. 특수 파괴가 필요하지 않은 하위 유형을 만들면 Liskov 대체 원칙을 위반하는 것으로 간주됩니다. .

C ++과 다른 것들에 관해서는 큰 차이가 없습니다. C ++은 모든 객체에 인터페이스를 강제합니다. 언어에 필요한 추상화는 유출 될 수 없습니다.


4
"특별한 파괴가 필요하지 않은 하위 유형을 만드는 경우"no-op는 유효한 특수 파괴 사례이므로 LSP 위반이 아닙니다. 파기 요구 사항을 파생 클래스에 추가 할 때 문제가 발생합니다.
코드 InChaos

혼란 스러워요. C ++ 서브 클래스에 특수 소멸 코드를 추가해야하는 경우 자동으로 사용 패턴을 전혀 변경하지 않습니다. 즉, 수퍼 클래스와 서브 클래스를 여전히 호환 적으로 사용할 수 있습니다. 그러나 자원 관리에 대한 명시 적 표기법을 사용하면 명시 적 소멸이 필요한 서브 클래스에서 사용이 수퍼 클래스와 호환되지 않습니까? (수퍼 클래스가 명시 적으로 파괴 될 필요가 없다고 가정)
Louis Jackman

@CodesInChaos-아, 맞습니다.
Telastyn

@ljackman : 특수 파괴가 필요한 클래스는 생성자를 호출하는 사람에게 생성자가 수행되도록하는 부담을 가중시킵니다. 이 DerivedFooThatRequiresSpecialDestruction코드는 호출하는 코드로만 만들 수 있으므로 LSP 위반이 발생하지 않습니다 new DerivedFooThatRequiresSpecialDestruction(). 반면에, DerivedFooThatRequiresSpecialDestruction파괴가 필요한 것으로 예상되지 않은 코드 를 리턴하는 팩토리 메소드 는 LSP 위반입니다.
supercat

0

내 질문은 이것입니다 : 스마트 포인터 또는 참조 계산 가비지 수집 시스템의 소멸자 (거의 동일한 것)는 암시적이고 투명한 리소스 파괴를 허용한다는 점에서 명시 적에 의존하는 비 결정적 유형보다 덜 누출 적입니다. 표기법?

손으로주기를 관찰해야하는 것은 암시 적이거나 투명하지 않습니다. 유일한 예외는 디자인에 의한 사이클을 금지하는 언어를 가진 참조 카운팅 시스템입니다. Erlang은 그러한 시스템의 예일 수 있습니다.

따라서 두 가지 접근 방식이 모두 유출됩니다. 주요 차이점은 소멸자가 C ++의 모든 곳에서 누출되지만 IDispose.NET에서는 매우 드 rare니다.


1
사이클은 매우 드물며 명시 적으로 순환되는 데이터 구조를 제외하고는 실제로 발생하지 않습니다. 주요 차이점은 C ++의 소멸자가 모든 곳에서 올바르게 처리되지만 IDispose는 .NET의 문제를 거의 처리하지 않는다는 것입니다.
DeadMG

"사이클을 제외하고는 매우 드물다". 현대 언어로? 나는 그 가설에 도전 할 것이다.
Jon Harrop
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.