    destructor TManagedObject.Destroy;
    begin
      if (f_CpsMask and CPS_ORPHANED_MASK) <> 0
      then inherited Destroy
      else
        if (f_CpsMask and CPS_SCRAPED_MASK) = 0 //if not scraped yet
        then
          if CpsFullManagement
          then Scrape
          else inherited Destroy;
    end;


    procedure TManagedObject.Scrape; //also adds self to the garbage collector graveyard. To be used instead of Free.
    begin
      f_CpsMask:= f_CpsMask or CPS_SCRAPED_MASK;
      GarbageCollector.Add(Self);
    end;

    procedure TManagedObject.Resurrect; //also adds self to the garbage collector graveyard. To be used instead of Free.
    begin
      f_CpsMask:= f_CpsMask xor CPS_SCRAPED_MASK;
      //the garbage collector removes it from its graveyard next time it collects.
    end;

   {$ifdef safeloading}
    procedure TManagedObject._SetCpsMask(w: dword);
    begin
      Assert((w and CPS_RESERVED_MASK) = (f_cpsmask and CPS_RESERVED_MASK),
        'Attempt to modify the reserved bits of Cpsmask');
      f_cpsmask:= w;
    end;
   {$endif}

    function TManagedObject._GetScraped: boolean;
    begin
      Result:= 0 <> (f_cpsmask and CPS_SCRAPED_MASK);
    end;

    constructor TGarbageCollector.Create;
    begin
      if Assigned(GarbageCollector) then Exit; //I'm a singleton!
      inherited;
      f_graveyard:= TAOMO.Create;
      CpsGarbageCollectorCriticalSection:= TCriticalSection.Create;
    end;

    procedure cps_gc_walk_proc(o: TManagedObject);
    begin
      //it's not an orphan!
      o.f_cpsmask:= o.f_cpsmask and not CPS_ORPHANED_MASK;
    end;



    procedure TGarbageCollector.Collect(TimeConstraint: int64 = 0);
    var
      f_endingTSC: int64;
      i: integer;
      function timeout: boolean; //{$ifdef fpc}inline;{$endif}
      var t: int64;
      begin
        if TimeConstraint > 0 then begin
          asm
            pushf
            push edx
            push eax
            rdtsc
            mov dword[t], eax;
            mov dword[t + 4], edx;
            pop eax
            pop edx
            popf
          end;
          Result:= t > f_endingTSC;
        end
        else Result:= false;
      end;
      procedure CollectOne(n: integer);
      begin
        //note: their destructor works only on the orphaned managed objects
        f_graveyard[n].Free;
        inc(ObjectsCollected);
        if n < f_graveyard.High then begin
          f_graveyard[n]:= f_graveyard.Last;
        end;
        f_graveyard.Decrement;
      end;
    label
      SafeExit;
    begin
      if ThreadSafe then CpsGarbageCollectorCriticalSection.Enter;
      if TimeConstraint > 0 then begin
        asm
          pushf
          push edx
          push eax
          rdtsc
          mov dword[f_endingTSC], eax;
          mov dword[f_endingTSC + 4], edx;
          pop eax
          pop edx
          popf
        end;
        f_endingTSC:= f_endingTSC + TimeConstraint;
      end;
      //first, remove orphans not deleted in the previous call
      if f_previousCollectInterrupted then begin
        for i:= min(f_prevcollectind, f_graveyard.high) downto 0 do begin
          if 0 <> (f_graveyard[i].f_cpsmask and CPS_ORPHANED_MASK)
            then CollectOne(i);
          if TimeOut then begin
            f_prevcollectind:= i - 1;
            goto SafeExit;
          end;
        end;
      end;
      f_previousCollectInterrupted:= false;
      if not Assigned(GraphRoot) then goto SafeExit;
      //mark them all as orphans.
      {for i:=0 to f_graveyard.High do
        f_graveyard[i].f_cpsmask:= f_graveyard[i].f_cpsmask or CPS_ORPHANED_MASK;}
      i:=0;
      repeat
        if 0 = f_graveyard[i].f_cpsmask or CPS_SCRAPED_MASK then begin
          //whoopsie, it got resurrected! Get out of my graveyard.
          if i < f_graveyard.High then f_graveyard[i]:= f_graveyard.Last;
          f_graveyard.Decrement;
        end
        else begin
          f_graveyard[i].f_cpsmask:= f_graveyard[i].f_cpsmask or CPS_ORPHANED_MASK; //mark it as orphaned
          inc(i);
        end;
      until i >= f_graveyard.High;
      //then walk the graph and mark all the encountered objects as NOT orphans.
      if not CpsWalkGraph(GraphRoot, @cps_gc_walk_proc) then begin
        if ThreadSafe then CpsGarbageCollectorCriticalSection.Leave;
        Die(RuEn(
          'Крах сборщика мусора при обходе графа.',
          'Garbage collector crashed while walking the graph.'));
      end;
      //lastly, remove all orphans from the existence.
      for i:= f_graveyard.high downto 0 do begin
        if 0 <> (f_graveyard[i].f_cpsmask and CPS_ORPHANED_MASK)
          then CollectOne(i);
        if TimeOut then begin
          f_prevcollectind:= i - 1;
          f_previousCollectInterrupted:= true;
          goto SafeExit;
        end;
      end;
     SafeExit:
      if ThreadSafe then CpsGarbageCollectorCriticalSection.Leave;
    end;

    procedure TGarbageCollector.Add(o: TManagedObject);
    begin
      if ThreadSafe then CpsGarbageCollectorCriticalSection.Enter;
      f_Graveyard.Add(o);
      with Autorun do
        if Enabled and (GraveyardCount > NumObjectsToActivate) and Assigned(GraphRoot) then begin
          if GraveyardCount > NumObjectsToIgnoreTimeConstraint
            then Collect()
            else Collect(TimeConstraint);
        end;
      if ThreadSafe then CpsGarbageCollectorCriticalSection.Leave;
    end;

    function TGarbageCollector.GraveyardCount: integer;
    begin
      Result:= f_graveyard.Length;
    end;

