spring-projects / spring-statemachine

Spring Statemachine is a framework for application developers to use state machine concepts with Spring.
1.53k stars 599 forks source link

With the increase of the number of executions CompositeStateMachineLinsterer.stateChanged method performs slower and slower #1005

Open Vanessafang opened 2 years ago

Vanessafang commented 2 years ago

When submachine! = null, the entry method performs submachine.addStateListener, but in the exit approach because completionListeners is empty, which could lead to an increase in submachine stateListener has not been released, Lead to the state machine. With the increase of the number of executions CompositeStateMachineLinsterer.stateChanged method performs slower and slower

The relevant codes are as follows:

@Override public void exit(StateContext<S, E> context) { if (submachine != null) { for (StateMachineListener<S, E> l : completionListeners) { submachine.removeStateListener(l); } } else if (!regions.isEmpty()) { for (Region<S, E> region : regions) { for (StateMachineListener<S, E> l : completionListeners) { region.removeStateListener(l); } } } completionListeners.clear(); cancelStateActions(); stateListener.onExit(context); disarmTriggers(); }

@Override
public void entry(StateContext<S, E> context) {
    if (submachine != null) {
        final StateMachineListener<S, E> l = new StateMachineListenerAdapter<S, E>() {

            @Override
            public void stateContext(StateContext<S, E> stateContext) {
                if (stateContext.getStage() == Stage.STATEMACHINE_STOP) {
                    if (stateContext.getStateMachine() == submachine && submachine.isComplete()) {
                        completionListeners.remove(this);
                        submachine.removeStateListener(this);
                        if (completionListeners.isEmpty()) {
                            notifyStateOnComplete(stateContext);
                        }
                    }
                }
            }
        };
        submachine.addStateListener(l);
    } else if (!regions.isEmpty()) {
        for (final Region<S, E> region : regions) {
            final StateMachineListener<S, E> l = new StateMachineListenerAdapter<S, E>() {

                @Override
                public void stateContext(StateContext<S, E> stateContext) {
                    if (stateContext.getStage() == Stage.STATEMACHINE_STOP) {
                        if (stateContext.getStateMachine() == region && region.isComplete()) {
                            completionListeners.remove(this);
                            region.removeStateListener(this);
                            if (completionListeners.isEmpty()) {
                                notifyStateOnComplete(stateContext);
                            }
                        }
                    }
                }
            };
            completionListeners.add(l);
            region.addStateListener(l);
        }
    }

@Override
public void stateChanged(State<S, E> from, State<S, E> to) {
    for (Iterator<StateMachineListener<S, E>> iterator = getListeners().reverse(); iterator.hasNext();) {
        StateMachineListener<S, E> listener = iterator.next();
        try {
            listener.stateChanged(from, to);
        } catch (Throwable e) {
            log.warn("Error during stateChanged", e);
        }
    }
}