HashMap没有明显原因

时间:2015-11-21 05:31:08

标签: java android garbage-collection

我用Java创建了一个State Machine类,为我们的FTC机器人团队提供了一种方法来编程他们可以理解的基于状态机的自治模式。

这是(真正浓缩的)课程。真正的代码有很多注释,调试信息和空保护的东西;但是,我把它缩短了以缩短它的帖子

[object Object]

状态被构造为主类的私有成员,但成员变量从不使用,因为它们是使用静态HashMap查找的。之后,主类只调用静态OpState.DoCurrentState(),类基础结构负责其余部分。

例如:

public abstract class OpState {
    private static Map<String, OpState> StateList = new HashMap<String, OpState>();

    public final static void SetCurrentState( String state_name ){
        OpState state = GetOpState(state_name);
        CurrentState.OnExit();
        CurrentState = state;
        CurrentState.OnEntry();
    }

    public final static String GetCurrentState(){
        return CurrentState.Name;
    }

    public final static void DoCurrentState() {
        CurrentState.Do();
    }
    private final static OpState GetOpState(String name){
        return StateList.get(name);
    }
    public final String Name;

    public OpState(String name) {
        Name = name;
        StateList.put(Name, this);
    }

    protected void finalize() throws Throwable {
        StateList.remove(Name);
        super.finalize();
    }

    protected void OnEntry(){
    }

    protected abstract void Do();

    protected void OnExit(){
    }
}

除了正在运行之外,它的工作非常好,在某些时候,它会执行一个SetCurrentState并且无法找到状态,因为HashMap是空的。 Map是私有的,访问它的所有方法都有日志,并且原因日志中没有证据。

他们去哪了!谁采取了我的国家!!!

我唯一的猜测是它是一个java / android垃圾收集问题;但是,我不明白为什么。有对状态的引用,但是在创建它们的类中,它们也在Map中。我不明白他们为什么会被收集。

1 个答案:

答案 0 :(得分:-1)

好的,所以问题肯定与垃圾收集有关;但是,事实证明,原因是一个微妙的代码设计缺陷。

我能够在日志中捕获事件:

11-22 16:38:00.922    8719-8807/? I/FIRST﹕ Doing OpState 'Turn1'
11-22 16:38:00.932    8719-8807/? I/FIRST﹕ Doing OpState 'Turn1'
11-22 16:38:00.942    8719-8807/? I/FIRST﹕ Doing OpState 'Turn1'
11-22 16:38:00.942    8719-8807/? I/FIRST﹕ Doing OpState 'Turn1'
11-22 16:38:00.942    8719-8723/? D/dalvikvm﹕ GC_CONCURRENT freed 1923K, 66% free 3827K/11088K, paused 3ms+3ms, total 36ms
11-22 16:38:00.942    8719-8728/? I/FIRST﹕ Removed 'Delay3' from StateMap
11-22 16:38:00.942    8719-8728/? I/FIRST﹕ Removed OpState 'Delay3'
11-22 16:38:00.942    8719-8728/? I/FIRST﹕ Removed 'Flash3' from StateMap
11-22 16:38:00.942    8719-8728/? I/FIRST﹕ Removed OpState 'Forward'
11-22 16:38:00.942    8719-8728/? I/FIRST﹕ Removed 'Delay1' from StateMap
11-22 16:38:00.942    8719-8728/? I/FIRST﹕ Removed OpState 'Delay'
11-22 16:38:00.942    8719-8728/? I/FIRST﹕ Removed 'Flash2' from StateMap
11-22 16:38:00.942    8719-8728/? I/FIRST﹕ Removed OpState 'Turn1'
11-22 16:38:00.942    8719-8728/? I/FIRST﹕ Removed 'Delay2' from StateMap
11-22 16:38:00.942    8719-8728/? I/FIRST﹕ Removed OpState 'Delay2'
11-22 16:38:00.942    8719-8728/? I/FIRST﹕ Removed 'Flash1' from StateMap
11-22 16:38:00.942    8719-8728/? I/FIRST﹕ Removed OpState 'Forward2'
11-22 16:38:00.942    8719-8807/? I/FIRST﹕ Doing OpState 'Turn1'
11-22 16:38:00.942    8719-8807/? I/FIRST﹕ Doing OpState 'Turn1'
11-22 16:38:00.952    8719-8807/? I/FIRST﹕ Doing OpState 'Turn1'
11-22 16:38:00.952    8719-8807/? I/FIRST﹕ Doing OpState 'Turn1'
11-22 16:38:00.962    8719-8807/? I/FIRST﹕ Could not find OpState'Delay2'
11-22 16:38:00.962    8719-8807/? I/FIRST﹕ StateMap has 0 members
11-22 16:38:00.962    8719-8807/? I/FIRST﹕ Exiting OpState 'Turn1'
11-22 16:38:00.962    8719-8807/? I/FIRST﹕ No OpState to Do
11-22 16:38:00.962    8719-8807/? I/FIRST﹕ No OpState to Do

&#39;删除&#39;消息来自OpState.finalize()。此转储暴露的另一个谜团是OpState Turn1如果已经收集,它将如何继续运行。这一点以及Patrick Dubroy的出色Memory Leak video引导我找出导致问题的原因。

首先是一些背景。此代码在FTC机器人控制程序主应用程序中作为OpMode运行。它是学生用来控制机器人的代码。您可以拥有任意数量的OpMode,并可以在它们之间进行选择。

我认为正在发生的是主应用程序正在构建一个新的副本我的OpMode然后(我称之为原始和复制)然后运行副本。当OpMode停止并重新启动时,可能会发生这种情况,以便在干净的副本上启动新的Start。这会导致将复制的OpStates添加到静态StateList。由于它们具有相同的名称,因此它们会将Original OpModes推出StateList,它们将可用于GC。然后,当收集get和在原始OpStates上调用finalize时,它将导致Copy OpStates从StateList中删除,因为它们与Original具有相同的名称。这也解释了OpState如何继续运行,因为正在收集的OpState不是活动的。

所以,解决方案很简单。首先,我删除重载的finalize()并让Java管理它。其次,我不是将OpStates构建为成员,而是清除StateList并在OpMode Start()方法中构造OpStates。由于我非常确定主应用程序在运行时不会复制OpMode,因此列表应保持完整且安全。