Exemplo n.º 1
0
void
recursivelyReachable(GCShared* p, GCObjectSet& o)
{
    if(o.find(p) == o.end())
    {
        assert(p);
        o.insert(p);
        GCCountMap tmp;
        p->__gcReachable(tmp);
        for(GCCountMap::const_iterator i = tmp.begin(); i != tmp.end(); ++i)
        {
            recursivelyReachable(i->first, o);
        }
    }
}
Exemplo n.º 2
0
bool
GCObject::collect(IceUtilInternal::MutexPtrLock<IceUtil::Mutex>& lock)
{
    GCCountMap counts;

    //
    // Go through the object graph and decrease reference counts for
    // all the objects from the graph. Cycles which can be collected
    // should lead to objects with a zero reference count.
    //
    DecreaseRefCounts(counts).visit(this);
    assert(counts.find(this) != counts.end());
    if(counts[this] > 0)
    {
        return false; // Object is still reachable, we're done.
    }

    //
    // Go the graph again and check for objects which are still
    // reachable. If there are any, we restore the reference counts
    // for the sub-graph of the reachable object and we remove the
    // reachable objects from the counts map. At the end, if the
    // counts map is empty, it indicates that this object graph isn't
    // collectable yet.
    //
    RestoreRefCountsIfReachable(counts).visit(this);
    if(counts.empty())
    {
        return false;
    }

    assert(counts.find(this) != counts.end()); // This object must be collectable.

    //
    // At this point, we can release the lock. The objects from counts
    // are un-reachable from the user code and clearing the members of
    // the collectable objects requires acquiring the mutex to
    // decrement their reference counts.
    //
    lock.release();

    //
    // Break all the cyclic reference counts of objects which are
    // remaining in the counts map by clearing members.
    //
    // We first go through the list to mark all the objects as
    // non-deletable and we also disable collection for all those
    // objects since we already know they are collectable.
    //
    // After clearing members, we delete all the collectable
    // objects. We can't just delete the objects since those objects
    // likely point to each other.
    //
    for(GCCountMap::const_iterator p = counts.begin(); p != counts.end(); ++p)
    {
        p->first->__setFlag(NoDelete);
        p->first->__clearFlag(CycleMember); // Disable cycle collection.
    }
    for(GCCountMap::const_iterator p = counts.begin(); p != counts.end(); ++p)
    {
        p->first->_iceGcVisitMembers(clearMembers);
    }
    for(GCCountMap::const_iterator p = counts.begin(); p != counts.end(); ++p)
    {
        delete p->first;
    }
    return true;
}
Exemplo n.º 3
0
void
IceInternal::GC::collectGarbage()
{
    //
    // Do nothing if the collector is running already.
    //
    {
        Monitor<Mutex>::Lock sync(*this);

        if(_collecting)
        {
            return;
        }
        _collecting = true;
    }

    RecMutex::Lock sync(gcRecMutex); // Prevent any further class reference count activity.

    Time t;
    GCStats stats;

    if(_statsCallback)
    {
        t = Time::now();
        stats.examined = static_cast<int>(gcObjects.size());
    }

    GCCountMap counts;

    {
        //
        // gcObjects contains the set of class instances that have at least one member of class type,
        // that is, gcObjects contains all those instances that can point at other instances.
        //
        // Create a map that, for each object in gcObjects, contains an <object, refcount> pair.
        // In addition, for each object in gcObjects, add the objects that are immediately (not
        // recursively) reachable from that object to a reachable map that counts how many times
        // the object is pointed at.
        //
        GCCountMap reachable;
        {
            for(GCObjectSet::const_iterator i = gcObjects.begin(); i != gcObjects.end(); ++i)
            {
                counts.insert(GCCountMap::value_type(*i, (*i)->__getRefUnsafe()));
                (*i)->__gcReachable(reachable);
            }
        }

        //
        // Decrement the reference count for each object in the counts map by the count in the reachable
        // map. This drops the reference count of each object in the counts map by the number of times that
        // the object is pointed at by other objects in the counts map.
        //
        {
            for(GCCountMap::const_iterator i = reachable.begin(); i != reachable.end(); ++i)
            {
                GCCountMap::iterator pos = counts.find(i->first);
                assert(pos != counts.end());
                pos->second -= i->second;
            }
        }
    }

    {
        //
        // Any instances in the counts map with a ref count > 0 are referenced from outside the objects in
        // gcObjects (and are therefore reachable from the program, for example, via Ptr variable on the stack).
        // The set of live objects therefore are all the objects with a reference count > 0, as well as all
        // objects that are (recursively) reachable from these objects.
        //
        GCObjectSet liveObjects;
        {
            for(GCCountMap::const_iterator i = counts.begin(); i != counts.end(); ++i)
            {
                if(i->second > 0)
                {
                    recursivelyReachable(i->first, liveObjects);
                }
            }
        }

        //
        // Remove all live objects from the counts map.
        //
        {
            for(GCObjectSet::const_iterator i = liveObjects.begin(); i != liveObjects.end(); ++i)
            {
#ifndef NDEBUG
                size_t erased =
#endif
                    counts.erase(*i);
                assert(erased != 0);
            }
        }
    }

    //
    // What is left in the counts map can be garbage collected.
    //
    {
        GCCountMap::const_iterator i;
        for(i = counts.begin(); i != counts.end(); ++i)
        {
            //
            // For classes with members that point at potentially-cyclic instances, __gcClear()
            // decrements the reference count of the pointed-at instances as many times as they are
            // pointed at and clears the corresponding Ptr members in the pointing class.
            // For classes that cannot be part of a cycle (because they do not contain class members)
            // and are therefore true leaves, __gcClear() assigns 0 to the corresponding class member,
            // which either decrements the ref count or, if it reaches zero, deletes the instance as usual.
            //
            i->first->__gcClear();
        }
        for(i = counts.begin(); i != counts.end(); ++i)
        {
            gcObjects.erase(i->first); // Remove this object from candidate set.
            delete i->first; // Delete this object.
        }
    }

    if(_statsCallback)
    {
        stats.time = Time::now() - t;
        stats.collected = static_cast<int>(counts.size());
        _statsCallback(stats);
    }

    //
    // We clear explicitly under protection of the lock, instead of waiting for the
    // counts destructor. This avoids lots of lock contention later because, otherwise,
    // the destructor of each object in the counts set would acquire and release
    // gcRecMutex._m.
    //
    counts.clear();

    {
        Monitor<Mutex>::Lock sync(*this);

        _collecting = false;
    }
}