스마트 포인터가있는 경우 가비지 콜렉션이 필요한 이유


67

요즘에는 많은 언어가 가비지 수집됩니다. 타사에서 C ++로도 사용할 수 있습니다. 그러나 C ++에는 RAII 및 스마트 포인터가 있습니다. 가비지 수집을 사용하는 요점은 무엇입니까? 여분의 일을하고 있습니까?

그리고 C #과 같은 다른 언어에서 모든 참조가 사양 및 구현에 의해 스마트 포인터 (RAII를 따로 유지)로 취급된다면 가비지 수집기가 여전히 필요합니까? 아니라면 왜 그렇지 않습니까?


1
이 질문을 한 후에 내가 이해 한 한 가지- 스마트 포인터는 자동 할당 해제를 관리하기 위해 RAII가 필요합니다 .
굴샨

8
스마트 포인터는 GC에 RAII를 사용하는 것을 의미합니다;)
Dario

C #에는 RAII로 모든 "가비지 수집"을 처리 할 수있는 옵션이 있어야합니다. 응용 프로그램 종료시 순환 참조를 감지 할 수 있습니다. Program.cs-class가 할당 해제 된 후에도 여전히 메모리에 어떤 할당이 있는지 확인하기 만하면됩니다. 그런 다음 순환 참조를 일종의 주 참조로 대체 할 수 있습니다.
AareP 2019

답변:


67

가비지 수집을 사용하는 이유는 무엇입니까?

나는 당신이 참조 카운트 된 스마트 포인터를 의미한다고 가정하고 그것들은 (가상적인) 가비지 수집의 형태라는 것을 주목할 것입니다. "나는 참조 카운트 된 스마트 포인터보다 다른 형태의 가비지 콜렉션의 장점은 무엇입니까?" 대신에.

  • 정확도 . 참조 카운트만으로도 사이클이 누출되므로 참조 카운트 스마트 포인터는 다른 기술을 추가하여 사이클을 포착하지 않는 한 일반적으로 메모리를 누설합니다. 이러한 기술이 추가되면 참조 횟수의 단순성 이점이 사라졌습니다. 또한 범위 기반 참조 계산 및 추적 GC는 서로 다른 시간에 값을 수집하며, 때로는 참조 계산이 더 일찍 수집하고 추적 GC가 더 일찍 수집하는 경우도 있습니다.

  • 처리량 . 스마트 포인터는 특히 참조 카운트가 원자 적으로 충돌 할 때 다중 스레드 응용 프로그램과 관련하여 가장 효율적인 가비지 수집 형식 중 하나입니다. 이를 완화하기 위해 설계된 고급 참조 카운팅 기술이 있지만 프로덕션 환경에서 추적 GC는 여전히 선택 알고리즘입니다.

  • 잠복 . 일반적인 스마트 포인터 구현으로 소멸자가 눈에 띄게되어 무한한 일시 정지 시간이 발생합니다. 가비지 수집의 다른 형태는 훨씬 더 점진적이며 심지어 실시간 일 수도 있습니다 (예 : Baker 's treadmill).


23
이 답변이 최고의 답변이라고 생각할 수 없습니다. 그것은 C ++ 스마트 포인터에 대한 이해가 부족하다는 것을 보여 주며 현실과 너무 일치하지 않는 주장을 말도 안되게 만듭니다. 첫째, 잘 설계된 C ++ 코드에서 가장 지배적 인 스마트 포인터는 공유 포인터가 아니라 고유 한 포인터입니다. en.cppreference.com/w/cpp/memory/unique_ptr 둘째, 스마트 포인터보다 비 결정적 가비지 수집의 '성능'장점과 실시간 이점을 실제로 주장한다고 믿을 수 없습니다.
user1703394

4
@ user1703394 응답자가 비 결정적 가비지 수집보다 성능이 떨어지는 포인터를 염두에두고있는 것으로 보입니다 (올바르거나 잘못, OP가 제안하는 것이 확실하지 않습니다).
Nathan Cooper

8
이것들은 모두 짚맨 논쟁이며 실제 질문을 완전히 무시하거나 다른 유형의 스마트 포인터의 실제 사용 패턴을 무시하는 경우에만 유효합니다. 문제는 스마트 포인터에 관한 것이었다. 예 shared_ptr은 스마트 포인터이고, shared_ptr은 가장 비싼 스마트 포인터이지만, 아닙니다. 퍼포먼스 인수를 관련성에 가깝게 만드는 곳 어디에서나 널리 사용되는 실제 인수는 없습니다. 진지하게,이 답변은 참조 횟수에 관한 질문으로 옮겨 져야합니다. 좋은 스마트 포인터 질문에 대한 잘못된 참조 계산 답변입니다.
user1703394

4
"스마트 포인터는 더 넓은 개념이 아닙니다." 당신은 그 진술이 당신이 지금까지 한 모든 유효한 주장을 훼손하는지 전혀 모른다. 아마도 Rust 소유권을 살펴보고 의미를 옮길 수도 있습니다. koerbitz.me/posts/… C ++에 대한 역사적 경험이있는 사람들이 C ++ 11 / C ++ 14의 메모리 모델을 사용하면 스마트하게 개선되었다는 사실을 놓치기 쉽습니다. 포인터와 이동 의미는 이전 모델과 완전히 다른 짐승입니다. Rust가 어떻게 작동하는지 살펴보고 C ++보다 깨끗하며 새로운 관점을 제공합니다.
user1703394

6
@ user1703394 : "소멸자로 인한 무제한 일시 중지는 메모리가 아닌 리소스에 사용되는 RAII의 불행한 속성입니다." 아니요, 이것은 비 메모리 리소스와는 아무런 관련이 없습니다.
Jon Harrop

63

아무도이 각도에서 그것을 보지 않았기 때문에 나는 당신의 질문을 다시 말할 것입니다 : 도서관에서 그것을 할 수 있다면 왜 언어에 무언가를 넣습니까? 특정 구현 및 구문 세부 사항을 무시하고 GC / 스마트 포인터는 기본적으로 해당 질문의 특별한 경우입니다. 라이브러리에서 가비지 컬렉터를 언어로 구현할 수있는 이유는 무엇입니까?

그 질문에 대한 몇 가지 답변이 있습니다. 가장 중요한 첫 번째 :

  1. 모든 코드가이 코드를 사용하여 상호 운용 할 수 있는지 확인하십시오. 이것이 Java / C # / Python / Ruby까지 코드 재사용과 코드 공유가 실제로 시작되지 않은 큰 이유 라고 생각 합니다 . 라이브러리는 통신이 필요하며, 신뢰할 수있는 유일한 공유 언어는 언어 사양 자체 (그리고 표준 라이브러리)에있는 것입니다. C ++에서 라이브러리를 재사용하려고 시도했다면 표준 메모리 의미론으로 인한 끔찍한 고통을 경험했을 것입니다. 구조체를 일부 lib에 전달하고 싶습니다. 참조를 전달합니까? 바늘? scoped_ptr?smart_ptr? 소유권을 전달하고 있습니까? 그것을 나타내는 방법이 있습니까? lib가 할당해야하는 경우 어떻게해야합니까? 할당자를 제공해야합니까? 메모리 관리를 언어의 일부로 만들지 않기 때문에 C ++은 각 라이브러리 쌍이 여기에서 자신의 특정 전략을 협상하도록 강요하므로 모든 사람들이 동의하는 것은 정말 어렵습니다. GC는이를 완전한 비 발행으로 만듭니다.

  2. 주위의 구문을 디자인 할 수 있습니다. C ++은 메모리 관리 자체를 캡슐화하지 않기 때문에 사용자 수준 코드가 모든 세부 사항을 표현할 수 있도록 다양한 구문 후크를 제공해야합니다. 포인터, 참조, const참조 연산자, 간접 연산자, 주소 등이 있습니다. 메모리 관리를 언어 자체로 롤링하면 그 주위에 구문을 설계 할 수 있습니다. 이러한 연산자는 모두 사라지고 언어가 더 깨끗하고 단순 해집니다.

  3. 높은 투자 수익을 얻습니다. 주어진 코드 조각이 생성하는 가치는 그것을 사용하는 사람들의 수로 곱해집니다. 이는 사용자가 많을수록 소프트웨어에 더 많은 비용을 투자 할 수 있음을 의미합니다. 기능을 언어로 이동하면 언어의 모든 사용자가 해당 기능을 사용하게됩니다. 즉, 해당 사용자의 하위 집합에서만 사용하는 라이브러리보다 더 많은 노력을 할당 할 수 있습니다. 그렇기 때문에 Java 및 C #과 같은 언어는 절대적으로 최고의 VM과 환상적인 가비지 수집기를 보유하고 있습니다. 개발 비용은 수백만 명의 사용자에게 상각됩니다.


환상적인 답변! 내가 한 번 이상 공표 할 수 있다면 ...
Dean Harding

10
가비지 수집은 실제로 C # 언어 자체가 아니라 .NET Framework , 특히 CLR (Common Language Runtime) 에서 구현된다는 점에 주목할 가치가 있습니다 .
Robert Harvey

6
@RobertHarvey : 언어로 구현 되지 않았지만 언어의 협력 없이는 작동하지 않습니다 . 예를 들어, 컴파일러는 코드의 모든 지점에서 고정되지 않은 객체에 대한 참조를 보유하는 모든 레지스터 또는 스택 프레임 오프셋의 위치를 ​​지정하는 정보를 포함해야합니다. 그것은 언어의 지원 없이는 견딜 수 없었던 절대 예외 가 아닙니다.
supercat

GC가 언어 및 필수 프레임 워크를 지원할 때 얻을 수있는 주요 이점은 다른 목적으로 할당 될 수있는 메모리에 대한 참조가 존재하지 않는다는 것입니다. Dispose비트 맵을 캡슐화하는 객체를 호출 하면 해당 객체에 대한 참조는 배치 된 비트 맵 객체에 대한 참조가됩니다. 다른 코드에서 계속 사용할 것으로 예상되는 동안 객체가 조기에 삭제 된 경우 비트 맵 클래스는 다른 코드가 예측 가능한 방식으로 실패 할 있습니다. 반대로 해제 된 메모리에 대한 참조를 사용하는 것은 정의되지 않은 동작입니다.
supercat

34

가비지 콜렉션은 기본적으로 할당 된 오브젝트가 더 이상 도달 할 수없는 시점에서 자동으로 해제됨을 의미합니다.

보다 정확하게 말하면, 순환 참조 객체는 절대로 해제되지 않기 때문에 프로그램에 도달 할 수 없을 때 해제됩니다.

스마트 포인터 는 일반 포인터처럼 동작 하지만 추가 기능이 추가 된 모든 구조를 나타 냅니다. 여기 에는 할당 취소뿐만 아니라 쓰기 중 복사, 바운드 검사 등이 포함됩니다.

지금까지 언급했듯이 스마트 포인터 를 사용 하여 가비지 수집 형식을 구현할 수 있습니다 .

그러나 생각의 기차는 다음과 같은 방식으로 진행됩니다.

  1. 가비지 콜렉션은 편리하고 적은 수의 것들을 처리해야하기 때문에 멋진 것입니다.
  2. 따라서 : 나는 언어로 가비지 수집을 원한다
  3. 이제 어떻게 GC를 언어로 할 수 있습니까?

물론 처음부터 이와 같이 디자인 할 수 있습니다. C #은 가비지 수집 되도록 설계되었으므로new 개체와 참조가 범위를 벗어나면 해제됩니다. 이 작업을 수행하는 방법은 컴파일러에 달려 있습니다.

그러나 C ++에서는 가비지 수집이 의도되지 않았습니다. 포인터를 할당하고 int* p = new int;범위를 p벗어나면 스택에서 자체적으로 제거되지만 아무도 할당 된 메모리를 처리하지 않습니다.

이제 시작부터 유일한 것은 결정 론적 소멸자 입니다. 오브젝트가 작성된 범위를 벗어나면 소멸자가 호출됩니다. 템플릿 및 연산자 오버로드와 함께 포인터처럼 동작하지만 소멸자 기능을 사용하여 첨부 된 리소스를 정리하는 래퍼 객체를 디자인 할 수 있습니다 (RAII). 이것을 스마트 포인터 라고 부릅니다 .

이것은 모두 C ++에만 해당됩니다. 연산자 오버로드, 템플릿, 소멸자 ...이 특정 언어 상황에서는 원하는 GC를 제공하는 스마트 포인터를 개발했습니다.

그러나 처음부터 GC를 사용하여 언어를 디자인하는 경우 이는 구현 세부 사항 일뿐입니다. 방금 객체가 정리되고 컴파일러 가이 작업을 수행한다고 말합니다.

C ++과 같은 스마트 포인터는 C #과 같은 언어에서는 가능하지 않을 것입니다 .C #과 같은 언어에서는 전혀 결정적인 파괴가 없습니다 (C #은 .Dispose()특정 객체 를 호출하기 위해 구문 설탕을 제공 하여이 문제를 해결 합니다). 참조되지 않은 리소스는 GC에 의해 최종적 으로 재생되지만 정확히 발생할 때 정의되지 않습니다.

그리고 이것은 GC가보다 효율적으로 작업 할 수있게합니다. .NET GC는 스마트 포인터보다 언어에 더 깊숙이 내장되어 있기 때문에 메모리 작동을 지연시키고 블록 단위로 수행하여 더 저렴하게 만들거나 객체를 얼마나 자주 사용하는지에 따라 효율성을 높이기 위해 메모리를 움직일 수 있습니다. 액세스됩니다.


C #을 통해 결정 파괴의 형태를 가지고 IDisposableusing. 그러나 약간의 프로그래머 노력이 필요하기 때문에 데이터베이스 연결 핸들과 같은 매우 부족한 리소스에만 일반적으로 사용됩니다.
JSB ձոգչ

7
@ JSBangs : 정확합니다. C ++이 GII를 얻기 위해 RAII 주위에 스마트 포인터를 구축하는 것처럼 C #은 다른 방법으로 가서 GC 주위에 "스마트 디스 포저"를 구축하여 RAII를 얻습니다. 안전한 자원 취급. 예를 들어 F #을 간단하게하려고 IDisposable단지 기존의 대체하여 구문 let ident = value으로 use ident = value...
다리오

@Dario : "C #은 다른 방법으로 가고 RAC를 얻기 위해 GC 주변에 '스마트 디스 포저'를 구축합니다." C #의 RAII는 using가비지 수집과 전혀 관련이 없으며 C ++의 소멸자와 같이 변수가 범위를 벗어나면 함수를 호출합니다.
Jon Harrop

1
@Jon Harrop : 제발 무엇? 인용문은 참조 횟수 / 스마트 포인터 / 가비지 수집이없는 일반 C ++ 소멸자에 관한 것입니다.
Dario

1
"가비지 수집은 기본적으로 할당 된 개체가 더 이상 참조되지 않을 때 자동으로 해제됨을 의미합니다. 더 정확하게 말하면 순환 참조 개체는 절대로 해제되지 않으므로 프로그램에 도달 할 수 없게되면 해제됩니다." ... 더 정확한 것은 그것들이 언제가 아니라 어떤 시점에서 자동으로 해제된다고 말하는 것 입니다. 교정이 즉시 발생한다는 것을 암시 할 때 실제로 교정이 종종 나중에 수행 되는 경우에 유의하십시오 .
Todd Lehman

4

메모리 관리에 사용되는 가비지 수집과 스마트 포인터 사이에는 두 가지 큰 차이점이 있습니다.

  1. 스마트 포인터는 순환 가비지를 수집 할 수 없습니다. 쓰레기 수거
  2. 스마트 포인터는 응용 프로그램 스레드에서 참조, 역 참조 및 할당 해제 시점에 모든 작업을 수행합니다. 가비지 수집이 필요하지 않습니다

전자는 스마트 포인터가 가지지 않는 GC가 가비지를 수집한다는 것을 의미합니다. 스마트 포인터를 사용하는 경우 이런 종류의 쓰레기를 만들지 말고 수동으로 처리 할 준비를해야합니다.

후자는 스마트 포인터가 아무리 스마트해도 작동이 프로그램의 작업 스레드를 느리게한다는 것을 의미합니다. 가비지 콜렉션은 작업을 지연시키고 다른 스레드로 이동할 수 있습니다. 이를 통해 전반적으로 더 효율적이 될 수 있습니다 (실제로 현대 GC의 런타임 비용은 스마트 포인터의 추가 오버 헤드가 없어도 정상적인 malloc / free 시스템보다 적습니다). 응용 프로그램 스레드의 방법.

이제 프로그래밍 방식의 구성 요소 인 스마트 포인터는 가비지 수집의 범위를 완전히 벗어난 다른 흥미로운 모든 종류의 작업을 수행하는 데 사용할 수 있습니다 (Dario의 답변 참조). 그렇게하려면 스마트 포인터가 필요합니다.

그러나 메모리 관리를 위해 가비지 수집을 대체하는 스마트 포인터의 전망은 보이지 않습니다. 그들은 단순히 그것에 능숙하지 않습니다.


6
@Tom : 스마트 포인터에 대한 자세한 내용은 Dario의 답변을 살펴보십시오. 스마트 포인터의 장점과 관련하여 결정적인 할당 해제는 메모리뿐만 아니라 리소스를 제어하는 ​​데 사용될 때 막대한 이점이 될 수 있습니다. 실제로 이것은 Microsoft가 후속 버전의 C #에서 블록을 도입 할만 중요한 것으로 입증되었습니다 using. 또한 GC의 비 결정적 동작은 실시간 시스템에서 금지 될 수 있습니다 (GC가 사용되지 않는 이유). 또한 GC가 너무 복잡하여 메모리가 실제로 누출되어 매우 비효율적이라는 사실을 잊지 마십시오 (예 : Boehm…).
Konrad Rudolph

6
GC의 비결정론은 약간의 청어입니다. 데스크탑과 서버 VM에서 볼 수는 없지만 실시간 사용에 적합한 GC 시스템이 있습니다 (IBM의 Recycler와 같은). 또한 스마트 포인터를 사용한다는 것은 malloc / free를 사용하는 것을 의미하며, malloc의 기존 구현은 무료 목록을 검색해야하기 때문에 비 결정적입니다. 움직이는 GC 시스템은 malloc / free 시스템보다 결정적인 할당 시간을 갖지만, 결정적인 할당 해제 시간은 더 적습니다.
Tom Anderson

3
복잡성에 관해서는 그렇습니다 .GC는 복잡하지만 "실제로 메모리가 누출되고 상당히 비효율적"이라는 것을 알지 못합니다. 그렇지 않으면 다른 증거를보고 싶습니다. Boehm은 매우 원시적 인 구현이기 때문에 증거가 아니며, 언어 안전을 제공하기 위해 만들어졌으며 형식 안전 부족으로 인해 정확한 GC가 근본적으로 불가능합니다. 용감한 노력이며 전혀 효과가 없다는 것이 인상적이지만 GC의 모범으로 받아 들일 수는 없습니다.
Tom Anderson

8
@ 존 : 결정적으로 헛소리 하지 않습니다 . bugzilla.novell.com/show_bug.cgi?id=621899 또는보다 일반적으로 : flyingfrogblog.blogspot.com/2009/01/… 이것은 잘 알려져 있으며 모든 보수적 GC의 재산입니다.
Konrad Rudolph

3
"현대 GC의 런타임 비용은 일반적인 malloc / free 시스템보다 적습니다." 여기 빨간 청어. 이것은 전통적인 malloc이 엄청나게 비효율적 인 알고리즘이기 때문입니다. 다른 블록 크기에 여러 버킷을 사용하는 최신 할당자는 할당 속도가 훨씬 빠르며 힙 조각화가 훨씬 적으며 여전히 빠른 할당 해제를 제공합니다.
메이슨 휠러

3

가비지 수집이라는 용어는 수집 할 가비지가 있음을 의미합니다. C ++에서 스마트 포인터는 여러 가지 특징, 특히 가장 중요한 unique_ptr로 제공됩니다. unique_ptr은 기본적으로 단일 소유권 및 범위 지정 구문입니다. 잘 설계된 코드에서 대부분의 힙 할당 항목은 unique_ptr 스마트 포인터 뒤에 있으며 이러한 자원의 소유권은 항상 잘 정의됩니다. unique_ptr에는 오버 헤드가 거의 없으며 unique_ptr은 전통적으로 사람들을 관리되는 언어로 이끌었던 수동 메모리 관리 문제를 대부분 제거합니다. 더 많은 코어가 동시에 실행되면서 일반화되고 있으므로 특정 시점에 고유하고 잘 정의 된 소유권을 사용하도록 코드를 구동하는 디자인 원칙이 성능에있어 더욱 중요해졌습니다.

잘 설계된 프로그램, 특히 다중 스레드 환경에서도 공유 데이터 구조없이 모든 것을 표현할 수있는 것은 아니며 스레드가 실제로 필요한 데이터 구조의 경우 스레드가 통신해야합니다. c ++의 RAII는 단일 스레드 설정의 수명 문제에 대해 잘 작동합니다. 다중 스레드 설정의 경우 개체 수명이 계층 적으로 완전히 정의되지 않을 수 있습니다. 이러한 상황에서 shared_ptr을 사용하면 솔루션의 상당 부분을 제공합니다. 리소스의 공유 소유권을 만들면 C ++에서 이것이 가비지로 볼 수있는 유일한 곳이지만, 소량으로 적절한 디자인의 c ++ 프로그램을 본격적인 가비지 수집보다 shared-ptr로 '리터'수집을 구현하는 데 더 고려해야합니다. 다른 언어로 구현됩니다. C ++에는 '쓰레기'가 많지 않습니다.

다른 사람들이 언급했듯이 참조 횟수 스마트 포인터는 가비지 수집의 한 형태이며 하나의 주요 문제가 있습니다. 대부분 참조 카운트 된 가비지 수집 양식의 단점으로 사용되는 예는 서로에 대한 스마트 포인터로 연결된 고립 된 데이터 구조를 작성하여 서로 수집되지 않도록하는 오브젝트 클러스터를 작성하는 데 문제가 있습니다. 액터 모델 계산에 따라 설계된 프로그램에서 데이터 구조는 일반적으로 대규모 스레드에서 주로 사용되는 것처럼 다중 스레드 프로그래밍에 광범위한 공유 데이터 접근 방식을 사용할 때 이러한 수집 불가능 클러스터가 C ++에서 발생하는 것을 허용하지 않습니다. 업계에서 이러한 고아 클러스터는 빠르게 현실이 될 수 있습니다.

공유 포인터 사용법에 의해 다중 스레드 프로그래밍을위한 액터 모델의 계산 접근법과 shared_ptr의 제한된 사용과 결합 된 unique_ptr의 광범위한 사용을 의미한다면, 다른 형태의 가비지 콜렉션은 당신을 사지 않습니다. 추가 혜택. 그러나 모든 것을 공유하는 접근 방식으로 인해 모든 곳에서 shared_ptr을 사용하게되면 동시성 모델을 전환하거나 더 넓은 소유권 공유 및 데이터 구조에 대한 동시 액세스에보다 적합한 관리되는 언어로 전환하는 것을 고려해야합니다.


1
Rust가비지 수집이 필요하지 않다는 것을 의미합니까 ?
굴샨

1
@Gulshan Rust는 안전한 고유 포인터를 지원하는 몇 안되는 언어 중 하나입니다.
코드 InChaos

2

대부분의 스마트 포인터는 참조 횟수를 사용하여 구현됩니다. 즉, 객체를 참조하는 각 스마트 포인터는 객체 참조 카운트를 증가시킵니다. 그 수가 0이되면 개체가 해제됩니다.

순환 참조가 있으면 문제가 있습니다. 즉, A에는 B에 대한 참조가 있고 B에는 C에 대한 참조가 있고 C에는 A에 대한 참조가 있습니다. 스마트 포인터를 사용하는 경우 A, B 및 C와 관련된 메모리를 해제하려면 수동으로해야합니다. weak_ptrC ++에서 사용하는 순환 참조를 "중단"하십시오 .

가비지 콜렉션 (일반적으로)은 상당히 다르게 작동합니다. 요즘 대부분의 가비지 수집기는 도달 가능성 테스트를 사용합니다 . 즉, 모든 그 참조를 참조하는 것이 객체 추적, 물체 다음 스택에 참조 세계적으로 액세스 할 수있는 사람을 모두 살펴보고있다 , 다른 등 모든 쓰레기가 있습니다 참조하십시오.

이런 식으로 순환 참조는 더 이상 중요하지 않습니다. A, B 및 C에 도달 할 수 없으면 메모리를 회수 할 수 있습니다.

"실제"가비지 콜렉션에는 다른 장점이 있습니다. 예를 들어, 메모리 할당은 매우 저렴합니다. 메모리 블록의 "끝"에 대한 포인터를 늘리면됩니다. 할당 해제에는 상각 된 상각 비용도 있습니다. 물론 C ++과 같은 언어를 사용하면 원하는 방식으로 메모리 관리를 거의 구현할 수 있으므로 더 빠른 할당 전략을 수립 할 수 있습니다.

물론 C ++에서 힙 할당 메모리의 양은 일반적으로 C # /. NET과 같이 참조가 많은 언어보다 적습니다. 그러나 그것은 실제로 가비지 수집 대 스마트 포인터 문제가 아닙니다.

어쨌든 문제는 잘랐으며 건조하지 않는 것입니다. 그들은 각각 장단점이 있습니다.


2

성능 에 관한 것 입니다. 메모리를 할당 해제하려면 많은 관리가 필요합니다. 할당이 백그라운드에서 실행되면 포 그라운드 프로세스의 성능이 향상됩니다. 불행히도, 메모리 할당은 게으르지 않으며 (할당 된 객체는 다음 순간에 사용될 것입니다), 릴리즈하는 객체는 가능합니다.

C ++에서 (GC없이) 많은 객체를 할당하고 "hello"를 인쇄 한 다음 삭제하십시오. 객체를 해제하는 데 시간이 얼마나 걸리는지 놀랄 것입니다.

또한 GNU libc는 메모리 할당 해제를위한보다 효과적인 도구를 제공합니다 ( obstacks 참조) . 나는 obstacks에 대한 경험이 없으며 결코 사용하지 않았다는 것을 알아야합니다.


원칙적으로 당신은 요점을 가지고 있지만 이것은 매우 간단한 해결책이있는 문제입니다. 풀 할당 기 또는 작은 객체 할당기를 사용하여 할당 해제를 번들로 묶으십시오. 그러나 이것은 백그라운드에서 GC를 실행하는 것보다 약간의 노력이 필요합니다.
Konrad Rudolph

물론, GC는 훨씬 더 편안합니다. (특히 초보자의 경우 : 소유권 문제가없고 삭제 연산자도 없습니다.)
ern0

3
@ ern0 : 아니요. 스마트 포인터 (참조 카운팅)의 요점은 소유권 문제가없고 삭제 연산자가 없다는 것입니다.
Konrad Rudolph

3
@Jon : 솔직히 대부분의 시간입니다. 다른 스레드간에 객체 상태를 공유하지 않으면 완전히 다른 문제가 발생합니다. 많은 사람들이 그런 식으로 프로그램한다는 것을 인정하지만, 이것은 최근까지 존재했던 잘못된 스레딩 추상화의 결과이며 멀티 스레딩을 수행하는 좋은 방법은 아닙니다.
Konrad Rudolph

1
할당 해제는 종종 "백그라운드에서"수행되지 않고 모든 포 그라운드 스레드를 일시 중지합니다. 배치 모드 가비지 수집은 포 그라운드 스레드의 일시 중지에도 불구하고 일반적으로 성능이 뛰어납니다. 사용되지 않는 공간을 통합 할 수 있기 때문입니다. 가비지 수집 및 힙 압축 프로세스를 분리 할 수 ​​있지만, 특히 핸들이 아닌 직접 참조를 사용하는 프레임 워크에서 프로세스는 "세계를 막는 (stop-the-world)"프로세스 인 경향이 있으며 종종 가장 실용적입니다. 함께.
supercat

2

가비지 콜렉션은보다 효율적일 수 있습니다. 기본적으로 메모리 관리 오버 헤드를 '배치'하여 한 번에 수행합니다. 일반적으로 이렇게하면 메모리 할당 해제시 전체 CPU 소비가 줄어들지 만 어느 시점에서 할당 해제 활동이 크게 늘어납니다. GC가 올바르게 설계되지 않은 경우 GC가 메모리를 할당 해제하려고 시도하는 동안 사용자에게 '일시 중지'로 표시 될 수 있습니다. 대부분의 최신 GC는 가장 불리한 조건을 제외하고는 사용자에게 보이지 않게하는 데 매우 능숙합니다.

스마트 포인터 (또는 모든 참조 계산 체계)는 코드를 볼 때 예상 할 때 정확하게 발생한다는 이점이 있습니다 (스마트 포인터가 범위를 벗어나면 물건이 삭제됩니다). 당신은 여기저기서 할당 해제가 거의 일어나지 않습니다. 전체적으로 할당 해제에 더 많은 CPU 시간을 사용할 수 있지만 프로그램에서 발생하는 모든 일에 분산되어 있기 때문에 (일부 몬스터 데이터 구조의 할당 해제를 금지) 사용자에게 표시 될 가능성은 적습니다.

응답 성이 중요한 작업을 수행하는 경우 스마트 포인터 / 참조 계산을 사용하면 상황이 언제 발생하는지 정확하게 알 수 있으므로 사용자가 볼 수있는 것을 코딩하는 동안 알 수 있습니다. GC 설정에서는 가비지 수집기를 가장 많이 제어 할 수 있으며 문제를 해결하기 만하면됩니다.

반면 전체 처리량이 목표라면 메모리 관리에 필요한 리소스를 최소화하기 때문에 GC 기반 시스템이 훨씬 더 나은 선택 일 수 있습니다.

주기 : 나는주기 문제를 중요한 것으로 생각하지 않습니다. 스마트 포인터가있는 시스템에서는주기가없는 데이터 구조를 선호하는 경향이 있거나 단순히 그러한 것들을 어떻게 놓아 두는지주의해야합니다. 필요한 경우, 소유 한 물체의주기를 끊는 방법을 알고있는 골키퍼 물체를 사용하여 자동으로 적절한 파괴를 보장 할 수 있습니다. 일부 프로그래밍 영역에서는 이것이 중요 할 수 있지만 대부분의 일상 작업에서는 관련이 없습니다.


1
"어느 시점에서 많은 할당 해제 활동이 발생합니다." 베이커의 런닝 머신은 아름답게 증분 된 가비지 수집기의 한 예입니다. memorymanagement.org/glossary/t.html#treadmill
Jon Harrop

1

스마트 포인터의 제한 사항 중 하나는 순환 참조에 항상 도움이되는 것은 아닙니다. 예를 들어, 오브젝트 B에 대한 스마트 포인터를 저장하는 오브젝트 A가 있고 오브젝트 B가 오브젝트 A에 대한 스마트 포인터를 저장하고 있습니다. 포인터 중 하나를 재설정하지 않고 함께 남겨두면 할당 해제되지 않습니다.

이는 스마트 포인터가 두 시나리오 모두 프로그램에 도달 할 수 없기 때문에 위 시나리오에서 다루지 않는 특정 조치를 수행해야하기 때문에 발생합니다. 가비지 콜렉션이 처리됩니다. 프로그램에 도달 할 수없는 오브젝트를 올바르게 식별하여 수집됩니다.


1

스펙트럼 입니다.

성능에 얽매이지 않고 갈기를 준비하고 있다면, 올바른 결정을 내리고 그 자유를 누릴 수있는 모든 책임을지게되면 결국 회의 나 c가 될 것입니다. , 그것을 엉망으로 만드는 모든 자유 :

"내가해야 할 일을 말해 줄게, 넌 믿어."

가비지 콜렉션은 스펙트럼의 다른 끝입니다. 당신은 통제력이 거의 없지만 그것을 돌 보았습니다.

"내가 원하는 것을 말해 줄 것이다.

여기에는 많은 장점이 있습니다. 대부분 자원이 더 이상 필요하지 않은 시점을 정확히 알 때 신뢰할 수있을 필요는 없지만 (여기서 떠 다니는 일부 대답이 있음에도 불구하고) 성능에는 좋지 않습니다. 성능의 예측 가능성. (모든 것을 제어 할 수 있고 어리석은 짓을하면 결과가 더 나빠질 수 있습니다. 그러나 컴파일 타임에 메모리를 확보 할 수있는 조건이 무엇인지 아는 것은 성능 승리로 사용할 수 없습니다. 순진함).

RAII, 범위, 참조 횟수 은 모두 스펙트럼을 따라 더 멀리 이동할 수 있도록 도와주는 도우미입니다. 이 모든 것들은 여전히 ​​적극적으로 사용해야합니다. 그들은 여전히 ​​가비지 수집이하지 않는 방식으로 메모리 관리와 상호 작용하도록 허용하고 요구합니다.


0

결국 모든 것이 CPU 실행 명령으로 귀결된다는 것을 기억하십시오. 내가 아는 한, 모든 소비자 등급 CPU에는 지정된 위치에 데이터를 메모리에 저장해야하고 해당 데이터에 대한 포인터가 있어야하는 명령 세트가 있습니다. 이것이 기본 수준의 전부입니다.

가비지 수집, 이동되었을 수있는 데이터에 대한 참조, 힙 압축 등의 모든 것들은 위의 "주소 포인터가있는 메모리 청크"패러다임에 의해 주어진 제한 내에서 작업을 수행하고 있습니다. 스마트 포인터와 같은 것-실제 하드웨어에서 코드를 실행해야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.