簡體   English   中英

HashMap空無明顯原因

[英]HashMap empties with no evident cause

我用Java創建了一個狀態機類,以便為我們的FTC機器人團隊提供一種編程方式,使他們能夠理解基於狀態機的自治模式。

這是(完全壓縮的)類。 實際的代碼中包含大量注釋,調試信息和null保護內容。 但是,我將其刪去以縮短發布時間

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(){
    }
}

狀態被構造為主類的私有成員,但是從不使用成員變量,因為它們是通過靜態HashMap查找的。 之后,主類僅調用靜態OpState.DoCurrentState(),而類基礎結構將處理其余部分。

例如:

private OpState forward = new DriveState("Forward", this, 0.50, 12.0, "Delay");
private OpState delay = new DelayState("Delay", this, 300, "Turn1");
private OpState turn = new TurnState("Turn1", this, 0.50, 180, "Delay2");
private OpState delay2 = new DelayState("Delay2", this, 300, "Forward2");
private OpState forward2 = new DriveState("Forward2", this, 0.50, 12.0, "Delay3");
private OpState delay3 = new DelayState("Delay3", this, 200, "Turn2");

public void start() {OpState.SetCurrentState("Forward");}
public void loop() {OpState.DoCurrentState();}

它確實運行良好,除了正在運行時,它有時會執行SetCurrentState並且因為HashMap為空而找不到狀態。 該地圖是私有的,訪問它的所有方法都有日志,並且日志中沒有任何原因的證據。

他們去哪了!!! 誰接受我的狀態!

我唯一的猜測是這是一個Java / Android垃圾回收問題。 但是,我不明白為什么。 這里有對狀態的引用,但在創建狀態的類中,它們也位於地圖中。 我不明白為什么要收集它們。

好的,所以問題肯定與垃圾回收有關; 但是事實證明,原因是一個微妙的代碼設計缺陷。

我能夠在日志中捕獲事件:

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

“已刪除”消息來自OpState.finalize()。 此轉儲揭示的另一個謎團是,如果收集了OpState Turn1,它將如何繼續運行。 這和帕特里克·杜布羅伊(Patrick Dubroy)出色的Memory Leak視頻 ,使我了解了導致問題的原因。

首先是一些背景。 該代碼在FTC機器人控制程序主應用程序中作為OpMode運行。 這是學生編寫的用於控制機器人的代碼。 您可以根據需要擁有任意數量的OpMode,並可以在它們之間進行選擇。

我相信正在發生的事情是主應用程序正在構建我的OpMode的新副本(然后將其稱為“原始副本”),然后運行該副本。 當OpMode停止並重新啟動以使新的Start出現在干凈副本上時,可能會發生這種情況。 這會導致將用於復制的OpStates添加到靜態StateList中。 由於它們具有相同的名稱,因此它們會將原始OpMode從StateList中推出,並且可用於GC。 然后,當在原始OpState上調用get和finalize進行獲取時,這將導致Copy OpState從StateList中刪除,因為它們與原始名稱相同。 這也解釋了OpState如何繼續運行,因為正在收集的OpState不是活動的。

因此,解決方案很容易。 首先,我刪除了重載的finalize()並讓Java對其進行管理。 第二,不是清除OpState作為成員,而是清除StateList並在OpMode Start()方法中構建OpState。 由於我非常確定Main App在運行時不會復制OpMode,因此列表應保持完整無缺且安全。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM