⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 garbagecollector.java

📁 Java的面向对象数据库系统的源代码
💻 JAVA
📖 第 1 页 / 共 3 页
字号:
        adds them to the list of {@link #surelyReachableObjectsWhichHaveToBeMarkedAsSuch}
        and then marks the given object as doneReachable.

        This method may be called by any CommandThread.
    */
    protected void processReferencesByThisObject(Transaction transaction,ObjectContainer objectContainer) {
        /*
        CommandThread   thread      =   (CommandThread) Thread.currentThread();
        Transaction     transaction =   thread.transaction();
        */

        /*
            We really only need READ locks, because only read and do not write the object.
            But with READ locks, there may be transactions which have invoked a method on
            this object. We do not want to read the object in this state, because there
            may be references temporarily taken out of the object but onto the stack
            which we would miss.

            That is why we must ensure that nobody has invoked the object currently.
            This should be - in theory - be possible by checking WizardObjectContainer.invokeCount,
            but this is not an option in practice because accessing invokeCount is not
            synchronized. This may lead to the situation that this thread does not see
            an invokeCount!=1 while the other thread which invoked the object already has
            set invokeCount to !=1.

            Additionally, checking invokeCount would not suffice because a transaction thread
            may invoke the object during our proxy identification.

            That is why we must use an exclusive lock, which is a write lock.
        */
//      int previousLockLevel = objectContainer.lock().tryAcquire(transaction,Lock.LEVEL_READ);
        int previousLockLevel = objectContainer.lock().tryAcquire(transaction,Lock.LEVEL_WRITE);

        if (previousLockLevel!=Lock.NOT_ACQUIRED) {
            try {
                env.getStoreManager().updateLockLevel(transaction,objectContainer);
                GarbageCollectorProxyObjectIdentificationObjectOutputStream identificator = new GarbageCollectorProxyObjectIdentificationObjectOutputStream();

                identificator.identifyProxys(objectContainer);

                internalEnsureDoneReachable(objectContainer);
            } catch (IOException e) {
                // only supported from JDK1.4 on
//              throw new RuntimeException("Caught during serialization for proxy object identification: ",e);
                throw new RuntimeException("Caught during serialization for proxy object identification: "+e);
            } finally {
                // The lock is released on transaction commit.
//              objectContainer.lock().release(transaction);
            }
        } else {
            deferProcessingOfObjectContainerDueToLockContention(objectContainer);
        }
    }

    /**
        Defers the processing of the given objectContainer because locking was not possible at current time.
        Because objectContainers may be modified in the meantime
        (in the case they are unloaded and loaded again, we would hold an old copy of objectContainer which
        does not reflect the changes made to the new objectContainer), it's not wise to queue the ObjectContainer.
        Instead, we have to queue the ObjectID of that ObjectContainer.
    */
    protected void deferProcessingOfObjectContainerDueToLockContention(ObjectContainer objectContainer) {
        synchronized (surelyReachableObjectsWhichHaveToBeMarkedAsSuch) {
            surelyReachableObjectsWhichShouldHaveBeenProcessedButWereLockContented.add(objectContainer.id());
        }
    }

    /**
        This method is called by Transactions to indicate that a method of the object callee
        will be called by the transaction.

        This method will always be called, even if the GarbageCollector is not startet.
    */
    public void interceptInvocationPre(Transaction transaction,ObjectContainer callee,Object[] args) {

        /*
            This looks dangerous but is effective.
            The method isRunning() is called without the lock on this GarbageCollector.

            This is safe because this GarbageCollector only really starts to run after all transactions which
            existed during the initiation have completed. All new transactions see the right value of
            isRunning because there is a synchronization on creation of such a transaction.

            The other possibility is that old transactions see this too early. But in this case, all
            what can happen is that the callStack is empty and therefore the caller is null.

            The speed gain is that method invocation may be not disturbed at all on HotSpot VMs, where
            calls to this method may be fully optimized away as long as isRunning() is constant.
        */
        if (isRunning()) {
            SimpleArrayList callStack   =   transaction.getCallStack();

           try {
                if (args!=null) {
                    ObjectContainer caller      =   (ObjectContainer) callStack.peek();

                    if (caller!=null) {

                        /*
                            There is a design decision which heavily affects performance:
                            -   Do we first check for the possibility of an OzoneProxy as parameter or
                            -   Do we first check for the cross of the border of the different sets of database objects?

                            If we use true here, the first option is used.
                            If we use false here, the first option is used.
                        */
                        if (false) {
                            /*
                                This spaghetti code is for speed.
                                -   If we find an OzoneProxy, we have to look deeper and have to synchronize
                                -   If we do not find an OzoneProxy, we do not have to look deeper and to synchronized
                            */
                            checkForPossibleOzoneProxys:
                            for (;;) {
                                for (int i = args.length-1;i>=0;i--) {
                                    if (args[i] instanceof OzoneProxy) {
                                        break checkForPossibleOzoneProxys;
                                    }
                                }
                                return;
                            }
                        }

                        boolean possibleProxyBorderCross = false;

                        synchronized (garbageCollectionLevelsLock) {
                            if (callee.getGarbageCollectionLevel()==doneReachableGarbageCollectionLevel) { // The callee belongs to the doneReachable set
                                if (caller.getGarbageCollectionLevel()!=doneReachableGarbageCollectionLevel) { // The caller does not belong to the doneReachable set
                                    // The caller may transport object references to the doneReachable set where they are not checked. The referenced objects could falsely be identified as unreachable.
                                    possibleProxyBorderCross = true;
                                }
                            }
                        }

                        if (possibleProxyBorderCross) {
                            for (int i = args.length-1;i>=0;i--) {
                                checkForProxyBorderCross(args[i]);
                            }
                        }
                    }
                }
            } finally {
                callStack.push(callee);
            }
        }
    }

    /**
        This method is called by Transactions to indicate that a method of the object callee
        was called by the transaction.

        This method will always be called, even if the GarbageCollector is not startet.
    */
    public void interceptInvocationPost(Transaction transaction,ObjectContainer callee,Object result) {
        /*
            This looks dangerous but is effective.
            The method isRunning() is called without the lock on this GarbageCollector.

            This is safe because this GarbageCollector only really starts to run after all transactions which
            existed during the initiation have completed. All new transactions see the right value of
            isRunning because there is a synchronization on creation of such a transaction.

            The other possibility is that old transactions see this too early. But in this case, all
            what can happen is that the callStack is empty and therefore the caller is null.

            The speed gain is that method invocation may be not disturbed at all on HotSpot VMs, where
            calls to this method may be fully optimized away as long as isRunning() is constant.
        */
        if (isRunning()) {
            SimpleArrayList callStack   =   transaction.getCallStack();

            if (result!=null) {
                ObjectContainer caller      =   (ObjectContainer) callStack.peek();

                if (caller!=null) {
                    if (result instanceof OzoneCompatibleOrProxy) {
                        // An indicator that this is a border cross proxy.

                        boolean possibleProxyBorderCross = false;

                        synchronized (garbageCollectionLevelsLock) {
                            if (caller.getGarbageCollectionLevel()==doneReachableGarbageCollectionLevel) { // The caller belongs to the doneReachable set
                                if (callee.getGarbageCollectionLevel()!=doneReachableGarbageCollectionLevel) { // The callee does not belong to the doneReachable set
                                    // The caller may receive object references to the doneReachable set where they are not checked. The referenced objects could falsely be identified as unreachable.
                                    possibleProxyBorderCross = true;
                                }
                            }
                        }

                        if (possibleProxyBorderCross) {
                            if (result instanceof OzoneCompatible) {
                                checkForProxyBorderCross((OzoneCompatible) result);
                            } else { // Must be OzoneProxy
                                checkForProxyBorderCross((OzoneProxy) result);
                            }
                        }
                    }
                }
            }
            callStack.pop();
        }
    }

    /**
        This method checks wether the given object or the object graph reachable from this given object
        contains OzoneProxys. If so, the database objects referenced by these proxies are considered
        surelyReachable.
    */
    protected void checkForProxyBorderCross(Object o) {
        if (o instanceof OzoneProxy) {
            checkForProxyBorderCross((OzoneProxy) o);
        }
    }

    /**
        This method checks wether the given object or the object graph reachable from this given object
        contains OzoneProxys. If so, the database objects referenced by these proxies are considered
        surelyReachable.
    */
    protected void checkForProxyBorderCross(OzoneProxy o) {
        ObjectID id = o.remoteID();

        addObjectIDToSurelyReachableObjectsWhichHaveToBeMarkedAsSuch(id);
    }

    /**
        This method checks wether the given object or the object graph reachable from this given object
        contains OzoneProxys. If so, the database objects referenced by these proxies are considered
        surelyReachable.
    */
    protected void checkForProxyBorderCross(OzoneCompatible o) {
        // We should not process this object immediately because it is likely that this object itself is currently called.
        ensureSurelyReachable(o.container().id());
    }

    /**
        ObjectOutputStream which servers as an intra ObjectContainer object-graph-walker to
        detect all OzoneProxy instances an OzoneObject does refer.
        Every detected OzoneProxy object is registered as to be marked as surely reachable.
    */
    public class GarbageCollectorProxyObjectIdentificationObjectOutputStream extends ObjectOutputStream {
        /*
            Maybe one GarbageCollectorProxyObjectIdentificationObjectOutputStream per GarbageCollector
            is sufficient, but there is state maintained in the ObjectOutputStream and
            flushing state during time another thread is using the ObjectOutputStream as well
            produces situationes which are unexpected by the developers of ObjectOutputStream.
            So this is currently not considered.
        */

        protected GarbageCollectorProxyObjectIdentificationObjectOutputStream() throws IOException {
            super(NullOutputStream.getDefault());
        }

        public void notifyOzoneProxyEncountered(OzoneProxy encounteredProxy) {
            addObjectIDToSurelyReachableObjectsWhichHaveToBeMarkedAsSuch(encounteredProxy.remoteID());
        }

        protected void identifyProxys(ObjectContainer objectContainer) throws IOException {
            writeObject(objectContainer.target());
        }
    }

    /**
        Sweeps all objects which are considered unreachable.
    */
    protected void sweepUnreachableObjects() {
        env.getLogWriter().newEntry(this,"sweepUnreachableObjects(): starting to sweep...",env.getLogWriter().DEBUG3);

        DxIterator i = env.getStoreManager().objectIDIterator();

        while (i.next()!=null) {
            if (kill) {
                break;
            }

            ObjectID        id          = (ObjectID) i.key();
            try {
                notifyAboutTransactionActionAndRenewTransactionIfRequired();

                ObjectContainer container = transaction.acquireObject(id,Lock.LEVEL_READ);

                try {
                    synchronized (garbageCollectionLevelsLock) {
//                      env.getLogWriter().newEntry(this,"sweepUnreachableObjects(): processing "+id+": container.getGarbageCollectionLevel()="+container.getGarbageCollectionLevel()+", currentGarbageCollectionLevel="+currentGarbageCollectionLevel+".",env.getLogWriter().DEBUG3);

                        if (container.getGarbageCollectionLevel()-currentGarbageCollectionLevel<0) {
                            env.getLogWriter().newEntry(this,"sweepUnreachableObjects(): deleting "+id,env.getLogWriter().DEBUG3);

                            transaction.deleteObject(id);
                        }
                    }
                } finally {
                    transaction.releaseObject(container);
                }
            } catch (ObjectNotFoundException e) {
                env.getLogWriter().newEntry(this,"caught while sweeping: ",e,env.getLogWriter().DEBUG3);
            } catch (ClassNotFoundException e) {
                env.getLogWriter().newEntry(this,"caught while sweeping: ",e,env.getLogWriter().DEBUG3);
            } catch (IOException e) {
                // It does not matter if the object is gone in the meantime. This is the best case for a garbage collector :-)
                env.getLogWriter().newEntry(this,"caught while sweeping: ",e,env.getLogWriter().DEBUG3);
            } catch (TransactionException e) {
                // FIXME. What do we in this case?
                env.getLogWriter().newEntry(this,"caught while sweeping: ",e,env.getLogWriter().ERROR);
            } catch (OzoneInternalException e) {
                env.getLogWriter().newEntry(this,"caught while sweeping: ",e,env.getLogWriter().ERROR);
            } catch (OzoneRemoteException e) {
                env.getLogWriter().newEntry(this,"caught while sweeping: ",e,env.getLogWriter().ERROR);
            }
        }
        try {
            completeTransaction();
        } catch (ClassNotFoundException e) {
            env.getLogWriter().newEntry(this,"caught: ",e,env.getLogWriter().ERROR);
        } catch (IOException e) {
            env.getLogWriter().newEntry(this,"caught: ",e,env.getLogWriter().ERROR);
        }
    }
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -