avatar of 发明者量化-小小梦 发明者量化-小小梦
집중하다 사신
4
집중하다
1271
수행원

C++ 전략 쓰기 지식 포인트: C++ 메모리 재활용

만든 날짜: 2017-12-29 11:00:02, 업데이트 날짜: 2017-12-29 11:25:11
comments   0
hits   3111

C++ 전략 쓰기 지식 포인트: C++ 메모리 재활용

C++ 전략을 작성하기 전에 몇 가지 기본 지식이 필요하지만, 콩이식적인 숙련을 요구하지 말고, 적어도 이 규칙을 알아야 한다. 다음의 영상들은

  • #### C++ 메모리 객체 대전

  만약 어떤 사람이 자기 자신을 프로그래머라고 말하지만 메모리에 대해 아무것도 모른다고 한다면, 나는 그가 자랑하고 있다고 말할 수 있다. C나 C++로 프로그램을 작성할 때, 메모리에 더 많은 관심을 기울여야 한다. 이것은 단순히 메모리의 분배가 합리적이냐는 것이 프로그램의 효율성과 성능에 직접적인 영향을 미치기 때문이 아니라, 더 중요한 것은, 우리가 메모리를 조작할 때 실수로 문제가 발생할 수 있다는 것이다.

우리는 C++가 메모리를 세 가지 논리적 영역으로 나눈다는 것을 알고 있습니다. 스택, , 그리고 정적 저장 영역. 그렇게 되면, 나는 그 사이에 있는 객체를 각각 스택 오브젝트, 오브젝트, 그리고 정적 오브젝트라고 부른다. 그렇다면 이 다른 메모리 오브젝트의 차이점은 무엇입니까? 스택 오브젝트와 오브젝트의 장단점은 무엇입니까? 스택 오브젝트 또는 오브젝트를 만드는 것을 금지하는 방법은 무엇입니까?

  • 1 기본 개념

    먼저 을 살펴보자. 은 일반적으로 지역 변수나 객체를 저장하는 데 사용되는데, 함수 정의에서 다음과 같은 문장과 같은 문장으로 선언한 객체와 같다.

    Type stack_object ; 
    

    stack_object은 오브젝트이며, 그것의 수명은 정의점으로부터 시작되며, 위치 함수가 반환될 때, 수명은 끝난다.

    또한, 거의 모든 임시 객체는 객체이다. 예를 들어, 다음과 같은 함수 정의:

    Type fun(Type object);
    

    이 함수는 적어도 두 개의 임시 객체를 생성합니다. 첫째, 이 함수는 값으로 전달되므로 복사 구성 함수를 호출하여 임시 객체 object_copy1를 생성합니다. 함수 내부에서 사용되는 것은 object이 아니라 object_copy1입니다. 자연스레 object_copy1는 함수가 반환될 때 방출되는 객체입니다. 또한 이 함수는 값으로 반환되며, 만약 반환값 최적화를 고려하지 않는다면 () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () () ()

    Type tt ,result ; //生成两个栈对象
    
    tt = fun(tt); //函数返回时,生成的是一个临时对象object_copy2
    

    위의 두 번째 문장의 구현은, 먼저 함수 fun가 반환될 때 임시 객체 object_copy2를 생성하고, 그 다음으로 함수 부여 연산자를 호출하여 실행합니다.

    tt = object_copy2 ; //调用赋值运算符
    

    우리가 모르는 사이에 컴파일러가 우리를 위해 이렇게 많은 임시 객체를 생성하고, 이 임시 객체들을 생성하는 데 드는 시간과 공간의 지출이 엄청나게 클 수 있기 때문에, 여러분은 왜 대 객체에 대해 함수 변수를 값별로 전달하는 대신 콘스트 참조로 전달하는 것이 더 낫는지 이해할 수 있을 것이다.

    다음으로, stack을 살펴보세요. stack은, 자유 저장 영역이라고도 하며, 그것은 프로그램 실행 과정에서 동적으로 배분되므로, 그것의 가장 큰 특징은 동적이다. C++에서, 모든 stack 객체의 생성 및 파괴는 프로그래머에 의해 책임져야 합니다. 따라서, 잘못 처리하면, 메모리 문제가 발생할 수 있습니다. stack 객체를 배분하고, 풀어 놓는 것을 잊으면, 메모리 누출이 발생할 수 있습니다.

    그렇다면, C++에서는 어떻게 스택 객체를 배분합니까? 유일한 방법은 new를 사용하는 것입니다 (물론 malloc 클래스 명령어로도 C형 스택 메모리를 얻을 수 있습니다). new를 사용하면 스택에 메모리를 배분하고 스택 객체를 가리키는 포인터를 반환합니다.

    다시 한 번 정적 저장 구역을 살펴보자. 모든 정적 객체, 글로벌 객체는 정적 저장 구역에 배분된다. 글로벌 객체에 대해서는 main () 함수가 실행되기 전에 배분된다. 실제로 main () 함수에서 표시 코드가 실행되기 전에 컴파일러에서 생성된 _main () 함수가 호출되며, _main () 함수는 모든 글로벌 객체의 구성 및 초기화를 수행하며, main () 함수가 종료되기 전에 컴파일러에서 생성된 exit 함수가 호출되어 모든 글로벌 객체를 풀어줍니다. 예를 들어 다음과 같은 코드:

    void main(void)
    {
        ... // 显式代码
    }
    
    
    // 实际上转化为这样:
    
    
    void main(void)
    {
        _main(); //隐式代码,由编译器产生,用以构造所有全局对象
        ...      // 显式代码
        ...
        exit() ; // 隐式代码,由编译器产生,用以释放所有全局对象
    }
    

    따라서, 이것을 알고, 여기서 몇 가지 기술을 도출할 수 있습니다. 예를 들어, main () 함수가 실행되기 전에 우리가 어떤 준비 작업을 수행해야 한다고 가정하면, 우리는 이러한 준비 작업을 사용자 정의 된 글로벌 객체의 구성 함수에 작성할 수 있습니다. 따라서, main () 함수의 명시적 코드가 실행되기 전에,이 글로벌 객체의 구성 함수가 호출되어, 예상된 동작을 수행합니다. 이렇게하면 우리의 목적이 달성됩니다.

    정적인 객체도 있는데, 그것은 클래스의 정적인 구성원이다. 이런 상황을 고려할 때, 좀 더 복잡한 문제가 관여한다.

    첫 번째 문제는 클래스의 정적 멤버 객체의 수명이다. 클래스의 정적 멤버 객체는 첫 번째 클래스 객체의 생성과 함께 생성되고, 전체 프로그램의 끝에서 사라진다. 즉, 이러한 상황이 존재하는데, 프로그램에서 우리는 클래스를 정의하고, 클래스의 정적 객체가 멤버로 존재하지만, 프로그램 실행 과정에서, 만약 우리가 그 클래스의 객체를 생성하지 않았다면, 그 클래스가 포함하고 있는 정적 객체가 생성되지 않는다. 또한, 만약 다중 클래스 객체가 생성된다면, 그 모든 객체는 그 정적 객체 멤버를 공유한다.

    두 번째 문제는 다음과 같은 경우입니다.

    class Base
    {
    public:
        static Type s_object ;
    }
    
    
    class Derived1 : public Base / / 公共继承
    {
    
    
        ... // other data 
    
    
    }
    
    
    class Derived2 : public Base / / 公共继承
    {
    
    
        ... // other data 
    
    
    }
    
    
    Base example ;
    
    
    Derivde1 example1 ;
    
    
    Derivde2 example2 ;
    
    
    example.s_object = …… ;
    
    
    example1.s_object = …… ; 
    
    
    example2.s_object = …… ; 
    

    위의 세 개의 blackbody 문장에서, s_object은 동일한 객체에 접근하고 있는 것일까요? 답은 예, 그들은 동일한 객체에 접근하고 있습니다. 사실처럼 들리지는 않죠? 하지만 사실입니다. 간단한 코드를 작성해서 직접 확인해 보세요. 저는 왜 그런지 설명하려고 합니다.

    우리가 Derived1 타입의 객체를 참조하지 않는 Base 타입의 변수를 받아들이는 함수에 전달할 때 절단 (切割) 이 일어나는 것을 생각해 봅시다. 그러면 어떻게 절단 (切割) 하는 걸까요? 이제 당신이 알고 있다고 믿습니다. 그것은 단지 Derived1 타입의 객체에서 subobject를 빼고, Derived1의 모든 다른 데이터 멤버를 무시하고, 이 subobject를 함수에 전달하는 것입니다.

    모든 계승 Base 클래스의 파생 클래스의 객체는 하나의 Base 타입의 subobject을 포함하고 있다 (Base 타입 지표가 Derived1 객체의 키를 가리킬 수 있는 곳이며, 자연히 다중 모드 키이기도 하다), 그리고 모든 subobject과 모든 Base 타입의 객체는 동일한 s_object 객체를 공유한다. 자연히, Base 클래스에서 파생된 전체 계승 시스템 클래스의 모든 인스턴스는 동일한 s_object 객체를 공유한다. 위에서 언급한 example, example1, example2의 객체 배열은 다음과 같이 나타난다:

  • 2 세 가지 메모리 객체의 비교

    객체의 장점은 적당한 때에 자동으로 생성되고, 적당한 때에 자동으로 파괴된다는 점이며, 프로그래머의 신경을 쓰지 않아도 된다. 또한, 객체의 생성 속도는 일반적으로 객체보다 빠르다. 객체를 배분할 때, operator new 동작을 호출한다. operator new는 어떤 메모리 공간 검색 알고리즘을 사용한다. 이 검색 과정은 시간이 많이 걸리지만, 객체를 생성하는 것은 그다지 문제가 없다. 그것은 단지 꼭대기 지점을 움직일 뿐이다. 그러나 주의해야 할 것은, 일반적으로 공간 용량이 상대적으로 작으며, 일반적으로 1MB 2MB, 그래서 크기가 비교적 큰 객체는 에서 배분하기에 적합하지 않다.

    스택 오브젝트는 생성 시점과 파괴 시점을 프로그래머가 정밀하게 정의해야 한다. 즉, 프로그래머는 스택 오브젝트의 삶에 대한 완전한 통제권을 가지고 있다. 우리는 종종 이런 오브젝트가 필요하다. 예를 들어, 우리는 여러 함수들이 접근할 수 있는 오브젝트를 만들 필요가 있지만, 그것을 범용화하기를 원하지 않는다. 이 때 스택 오브젝트를 만드는 것은 좋은 선택일 것이다. 그리고 나서 각 함수들 사이에 이 스택 오브젝트의 포인터를 전달하면, 그 오브젝트의 공유를 실현할 수 있다. 또한, 공간에 비해 스택의 용량은 훨씬 더 크다. 실제로, 물리적 메모리가 충분하지 않을 때, 이 때 새로운 스택 오브젝트를 생성해야 하는 경우, 일반적으로 실행 시에는 오류가 발생하지 않으며, 가상 메모리를 사용하여 실제 물리적 메모리를 확장한다.

    다음으로 static 객체에 대해 살펴봅시다.

    먼저, 범용 객체 ᄂ. 범용 객체는 클래스 간 통신과 함수 간 통신을 위한 가장 간단한 방법을 제공하지만, 이 방법은 우아하지 않다. 일반적으로, C#와 같은 완전히 객체 지향 언어에서는 범용 객체가 존재하지 않는다. 범용 객체는 안전하지 않고 고조합을 의미하기 때문에, 프로그램에서 범용 객체를 너무 많이 사용하는 것은 프로그램의 튼튼성, 안정성, 유지보수성 및 복용성을 크게 떨어뜨린다. C++도 범용 객체를 완전히 제거할 수 있지만, 결국에는 그렇지 않다. C와 호환하기 위한 이유 중 하나가 아닌가 싶다.

    다음으로 클래스의 정적 멤버입니다. 위에서 언급한 바와 같이, 기본 클래스와 그 파생 클래스의 모든 객체는 이 정적 멤버 객체를 공유합니다. 따라서 이러한 클래스 또는 이러한 클래스 객체 사이에 데이터 공유 또는 통신이 필요할 때, 이러한 정적 멤버는 의심의 여지없이 좋은 선택입니다.

    다음으로, 정적인 로컬 객체, 주로 객체가 있는 함수가 반복적으로 호출되는 동안의 중간 상태를 보존하는 데 사용할 수 있습니다. 그 중 가장 대표적인 예는 회귀 함수입니다. 우리는 회귀 함수가 자기 자신을 호출하는 함수라는 것을 알고 있습니다. 만약 회귀 함수에서 비정형 로컬 객체를 정의한다면, 회귀 번수가 상당히 많을 때 발생하는 비용도 엄청납니다. 이것은 비정형 로컬 객체가 객체이기 때문입니다.

    회귀 함수 설계에서, nonstatic 로컬 오브젝트를 대신하여 static 오브젝트를 사용할 수 있다. 이것은 회귀 호출과 반환마다 nonstatic 오브젝트를 생성하고 풀어내는 비용을 줄일 수 있을 뿐만 아니라, static 오브젝트는 회귀 호출의 중간 상태를 보존할 수 있으며 각 호출 계층에 대해 접근할 수 있다.

  • 3 을 사용하는 대상의 우연한 수확

    앞서 소개한 바와 같이, 객체는 적절한 시기에 생성되고, 적절한 때에 자동으로 풀어집니다. 즉, 객체는 자동 관리 기능이 있습니다. 그렇다면 객체는 어떤 경우에 자동으로 풀어집니다? 첫째, 그것의 수명이 끝날 때; 둘째, 그것의 함수가 비정상적으로 발생했을 때. 당신은 아마도, 이 모든 것이 정상이라고 말할 것입니다, 큰 문제가 없습니다. 네, 큰 문제가 없습니다. 그러나 우리가 조금 더 깊이 들어가면, 예상치 못한 수확이있을 수 있습니다.

    객체, 자동으로 풀릴 때, 자체 해독 함수를 호출한다. 만약 우리가 객체에 자원을 봉쇄하고, 또한 객체의 해독 함수에서 자원을 풀어주는 동작을 수행한다면, 자원의 누출 확률을 크게 줄일 수 있다. 왜냐하면 객체가 자원을 자동으로 풀 수 있기 때문이다. 심지어는 그 함수가 비정상적인 경우에도. 실제 과정은 다음과 같다. 함수가 비정상적인 경우, stack_unwinding () 이라고 불리는 일이 발생한다. 즉, 객체가 자연의 에 존재하기 때문에, 객체의 해독 함수가 실행되고, 그 봉쇄된 자원을 풀어줍니다.

  • 4 스택 객체 생성 금지

    위에서 언급한 바와 같이, 어떤 종류의 스택 객체들을 생성하는 것을 금지하기로 결정했을 때, 여러분은 스스로 자원의 패키지 클래스를 만들 수 있습니다. 이 클래스는 단지 에서 생성될 수 있습니다. 이렇게 하면 비정상적인 경우에 자동으로 패키지된 자원을 풀어낼 수 있습니다.

    그렇다면 어떻게 스택 객체를 생성하는 것을 금지합니까? 우리는 스택 객체를 생성하는 유일한 방법은 new 동작을 사용하는 것으로 알고 있습니다. new 동작을 사용하지 않는 경우 new 동작을 사용할 수 없습니다. 더 나아가 new 동작이 실행될 때 new 연산자를 호출하고 new 연산자는 다시 로드 할 수 있습니다. 방법이 있습니다. new 연산자를 사적으로 만들 수 있습니다. 대칭을 위해 delete 연산자를 다시 로드하는 것이 좋습니다.

    #include <stdlib.h> //需要用到C式内存分配函数
    
    
    class Resource ; //代表需要被封装的资源类
    
    
    class NoHashObject
    {
    private: 
        Resource* ptr ;//指向被封装的资源
    
    
        ... ... //其它数据成员
    
    
        void* operator new(size_t size) //非严格实现,仅作示意之用
        { 
            return malloc(size) ; 
        } 
    
    
        void operator delete(void* pp) //非严格实现,仅作示意之用
        { 
            free(pp) ; 
        } 
    
    
    public: 
        NoHashObject() 
        { 
            //此处可以获得需要封装的资源,并让ptr指针指向该资源
    
    
            ptr = new Resource() ; 
        } 
    
    
        ~NoHashObject() 
        { 
    
    
            delete ptr ; //释放封装的资源
        } 
    }; 
    

    이제 NoHashObject는 다음과 같은 코드를 작성하면 스택 객체를 금지하는 클래스입니다.

    NoHashObject* fp = new NoHashObject() ; // 컴파일 오류!

    delete fp ;

    위의 코드는 컴파일 오류를 발생시킵니다. 좋아요, 이제 당신은 스택 객체를 금지하는 클래스를 설계하는 방법을 알고 있습니다. 당신은 아마도 나처럼 이런 의문이있을 것입니다. NoHashObject의 정의를 변경할 수 없는 상태에서, 그런 종류의 스택 객체를 생성할 수 없는 것은 아닐까요?

    void main(void)
    {
        char* temp = new char[sizeof(NoHashObject)] ; 
    
    
        //强制类型转换,现在ptr是一个指向NoHashObject对象的指针
    
    
        NoHashObject* obj_ptr = (NoHashObject*)temp ; 
    
    
        temp = NULL ; //防止通过temp指针修改NoHashObject对象
    
    
        //再一次强制类型转换,让rp指针指向堆中NoHashObject对象的ptr成员
    
    
        Resource* rp = (Resource*)obj_ptr ; 
    
    
        //初始化obj_ptr指向的NoHashObject对象的ptr成员
    
    
        rp = new Resource() ; 
    
    
        //现在可以通过使用obj_ptr指针使用堆中的NoHashObject对象成员了
    
    
        ... ... 
    
    
        delete rp ;//释放资源
    
    
        temp = (char*)obj_ptr ; 
    
    
        obj_ptr = NULL ;//防止悬挂指针产生
    
    
        delete [] temp ;//释放NoHashObject对象所占的堆空间。
    
    
        } 
    

    위의 구현은 번거롭고, 이 구현은 실제로 거의 사용되지 않습니다, 하지만 나는 그것을 이해하는 것이 C++ 메모리 객체를 이해하는 데 도움이 되기 때문에 방법을 썼습니다. 위의 많은 강제 유형 변환의 가장 기본적인 것은 무엇입니까? 우리는 다음과 같이 이해할 수 있습니다:

    어떤 메모리의 데이터는 변하지 않고, 타입은 우리가 착용한 안경입니다. 우리가 안경을 착용하면, 우리는 상응하는 타입을 사용하여 메모리의 데이터를 해석합니다. 따라서 다른 해석은 다른 정보를 얻습니다.

    강제적인 타입 전환은 실제로 다른 안경을 착용하고 같은 메모리를 다시 보는 것입니다.

    또한, 다른 컴파일러가 객체의 멤버 데이터에 대한 배열 배열을 다르게 할 수 있다는 것을 상기시켜 주어야 합니다. 예를 들어, 대부분의 컴파일러는 NoHashObject의 ptr 포인터 멤버를 객체 공간의 첫 번째 4 바이트에 배치합니다. 이렇게 하면 아래의 문장의 변환 동작이 우리가 예상하는 대로 수행될 수 있습니다.

    Resource* rp = (Resource*)obj_ptr ;

    하지만 모든 컴파일러가 그런 것은 아닙니다.

    만약 우리가 어떤 종류의 스택 객체를 생성하는 것을 금지할 수 있다면, 객체를 생성할 수 없는 클래스를 설계할 수 있을까요? 물론 그럴 수 있습니다.

  • 5 을 만들면 안된다

    앞서 언급한 바와 같이, 객체를 생성할 때, 의 적절한 크기의 공간을 으로 뽑기 위해 꼭대기 포인터를 이동하고, 이 공간에서 직접 대응하는 구성 함수를 호출하여 객체를 형성합니다. 그리고 함수가 돌아왔을 때, 해독 함수를 호출하여 이 객체를 방출하고, 다시 의 메모리를 회수합니다. 이 과정에서 new/delete 연산자가 필요하지 않으므로, new/delete 연산자를 비공개로 설정하는 것은 목적을 달성할 수 없습니다. 물론 위의 설명에서, 당신은 아마도 다음과 같이 생각했을 것입니다: 구성 함수 또는 구성 함수 설정을 비공개로 설정하면, 시스템은 구성/해독 함수를 호출할 수 없으며, 당연히 에서 객체를 생성할 수 없습니다.

    그렇게 할 수 있고, 저도 그렇게 할 생각입니다. 하지만, 그 전에, 한가지 명확하게 생각해 볼 필요가 있습니다. 즉, 만약 우리가 구성 함수를 사적으로 설정한다면, 우리는 new를 사용하여 직접적으로 스택 객체를 생성할 수 없습니다. 왜냐하면 new는 객체에 공간을 할당한 후에 그것의 구성 함수를 호출할 것이기 때문입니다. 그래서, 나는 해독 함수를 사적으로 설정할 계획입니다. 더 나아가, 해독 함수를 사적으로 설정하는 것은 객체 생성을 제한하는 것 외에도 다른 영향을 미치나요? 네, 이것은 또한 상속을 제한합니다.

    만약 클래스가 기본 클래스가 되려고 하지 않는다면, 일반적으로 사용되는 방법은 그 해체 함수를 사적으로 선언하는 것이다.

    객체를 제한하지만 상속을 제한하지 않기 위해, 우리는 분해 함수를 protected로 선언할 수 있습니다. 이 방법은 두 가지 모두 잘 됩니다. 아래의 코드는 다음과 같습니다:

    class NoStackObject
    {
    protected: 
    
    
        ~NoStackObject() { } 
    
    
    public: 
    
    
        void destroy() 
    
    
        { 
    
    
            delete this ;//调用保护析构函数
    
    
        } 
    }; 
    

    다음으로, NoStackObject 클래스를 다음과 같이 사용할 수 있습니다.

    NoStackObject* hash_ptr = new NoStackObject() ;

    … … // hash_ptr 지향 객체에 대한 작업

    hash_ptr->destroy() ; , 좀 이상하게 느껴지나요, new로 객체를 만들면서 delete로 삭제하는 게 아니라 destroy로 처리하는 걸로요. 분명히, 사용자는 이런 기묘한 사용 방식에 익숙하지 않습니다. 그래서, 저는 구성 함수를 private 또는 protected로 설정하기로 결정했습니다. 이것은 위에서 피하려고 했던 문제로 다시 돌아옵니다. new를 사용하지 않고, 어떤 방법으로 객체를 생성할까요? 우리는 간접적인 방법으로, 즉, 이 클래스가 이 타입의 스택 객체를 생성하기 위해 전용으로 정적인 멤버 함수를 제공하도록 할 수 있습니다.

    class NoStackObject
    {
    protected: 
    
    
        NoStackObject() { } 
    
    
        ~NoStackObject() { } 
    
    
    public: 
    
    
        static NoStackObject* creatInstance() 
        { 
            return new NoStackObject() ;//调用保护的构造函数
        } 
    
    
        void destroy() 
        { 
    
    
            delete this ;//调用保护的析构函数
    
    
        } 
    };
    

    이제 NoStackObject 클래스를 사용할 수 있습니다.

    NoStackObject* hash_ptr = NoStackObject::creatInstance() ;

    … … // hash_ptr 지향 객체에 대한 작업

    hash_ptr->destroy() ;

    hash_ptr = NULL ; // 기저귀를 사용하지 않도록

    이제 더 나은 느낌입니다. 객체 생성과 개체 방출의 동작이 일치합니다.

  • C++의 가시적인 쓰레기 재활용 방법

많은 C 또는 C++ 프로그래머들은 쓰레기 회수 (garbage recycling) 에 대해 콧물을 뱉으며, 쓰레기 회수는 자신의 동적 메모리를 관리하는 것보다 확실히 비효율적이라고 생각하며, 회수 할 때 반드시 프로그램을 멈추게 할 것이며, 자신이 메모리 관리를 통제하면 할당 및 방출 시간은 안정적이며, 프로그램을 중단시키지 않을 것입니다. 마지막으로, 많은 C / C++ 프로그래머들은 C / C++에서 쓰레기 회수 메커니즘을 구현할 수 없다는 것을 굳게 믿습니다. 이러한 잘못된 견해는 쓰레기 회수 알고리즘을 이해하지 못했기 때문에 만들어졌습니다.

사실 쓰레기 회수 메커니즘은 느리지는 않지만, 심지어는 동적 메모리 배분보다 더 효율적이다. 왜냐하면 우리는 배분하지 않고만 할당할 수 있기 때문에, 배분할 때 메모리는 단지 에서 계속 새로운 메모리를 얻어야만 하고, 꼭대기를 이동하는 지표만 충분하다. 그리고 방출 과정은 생략되고, 자연적으로 속도가 빨라진다. 현대 쓰레기 회수 알고리즘은 많이 발전해 왔으며, 증강적 수집 알고리즘은 쓰레기 회수 프로세스를 분기적으로 수행하여 프로그램의 운영을 방해하지 않도록 해준다.

쓰레기 회수 (garbage recovery) 의 알고리즘의 기초는 일반적으로 현재 사용할 수 있는 모든 메모리 블록을 스캔하고 표기하여, 이미 배분된 모든 메모리 중에서 표기되지 않은 메모리를 회수하는 데에 있다. C/C++에서 쓰레기 회수를 실현할 수 없다는 관점은 일반적으로 사용할 수 있는 모든 메모리 블록을 제대로 스캔할 수 없다는 데에 근거하고 있지만, 불가능한 것처럼 보이는 것은 실제로 실현되기는 하지만 복잡하지 않다. 우선, 메모리를 스캔함으로써, 상층적으로 배분된 메모리를 가리키는 포인터는 쉽게 식별될 수 있으며, 식별 오류가 있을 경우, 일부 비 포인터 데이터만 포인터로 표시할 수 있으며, 포인터는 비 포인터 데이터로 표시되지 않는다. 따라서, 쓰레기 회수 과정은 오류를 회수하는 것에 지나지 않으며, 오류는 회수되지 않는 메모리를 청소하지 않는다.

쓰레기 회수 시, bss 단위, data 단위 및 현재 사용 중인 공간을 스캔하여 동적 메모리 지표가 될 수 있는 양을 찾아서, 참조된 메모리 회귀 스캔을 하면 현재 사용 중인 모든 동적 메모리를 얻을 수 있다.

만약 당신이 당신의 프로젝트에 좋은 쓰레기 회수기를 구현한다면, 메모리 관리 속도를 높이고, 심지어 전체 메모리 소모를 줄일 수도 있다. 관심이 있다면, 온라인에서 쓰레기 회수에 관한 논문과 구현된 라이브러리를 검색할 수 있다.

[영화]HK Zhang

  • #### 왜 지역 변수의 주소가 지표에 부여되면, 지표의 수명이 전체 프로세스가 끝날 때까지 계속될 수 있을까요?
  #include<stdio.h>
  int*fun(){
      int k = 12;
      return &k;
  }
  int main(){
      int *p = fun();    
      printf("%d\n", *p);

      getchar();
      return 0;
  }

이 문서는 접속할 수 있고, 수정할 수도 있지만, 이 접근은 확실하지 않습니다. 로컬 변수의 주소는 프로그램 자체의 스택에 있으며, 권한 변수가 종료된 후에도, 해당 로컬 변수의 메모리 주소를 다른 변수에 부여하지 않는 한, 그 값은 여전히 존재한다. 그러나 변경할 경우, 비교적 위험하다. 왜냐하면 이 메모리 주소는 프로그램의 다른 변수에 부여되어, 지표로 강제 변경할 경우, 프로그램이 붕괴될 수 있기 때문이다.

csdn bbs