是否有警卫来检查Java是否调用了一个方法?

时间:2016-02-07 14:03:01

标签: java performance optimization

请考虑以下代码:

public class BasicBuildOrder {
    private List<GameObject> gameObjects = new ArrayList<>();

    private List<GameObject> savedState;

    public void saveState() {
        savedState = new ArrayList<>(gameObjects);
    }

    public void resetToSavedState() {
        if (savedState == null) {
            throw new IllegalStateException("the state has not been saved");
        }
        gameObjects = savedState;
    }

    public void add(GameObject gameObject) {
        gameObjects.add(gameObject);
    }

    public void get(int index) {
        return gameObjects.get(index);
    }

    public void remove(GameObject gameObject) {
        gameObjects.remove(gameObject);
    }
}

我正在实现一种模式,您可以将列表标记为在某个特定状态下保存,然后恢复该状态。我知道当前的保存/重置方式有点阴暗并且可以提供O(n)性能,而这可以通过标记已添加,检索和删除的项目来改进。

现在我看到的一个问题是,在调用resetToSavedState() 之前,对saveState()的调用将完成,因此我需要检查一下。

我的问题是:如果if (savedState == null)知道已调用saveState()并因此savedState,那么null检查是否在某些时候被Java优化(由编译器或在运行时) }不能再是x-ms-request-charge了吗?

另外一个问题,这个方法是否有一种模式可用于方法B之间的关系?

3 个答案:

答案 0 :(得分:5)

将Java源代码编译为.class文件时无法优化。如果从同一方法调用saveState()resetToSavedState(),并且在JIT编译期间内联两者(或者某些更深的调用链完全内联),它可以在运行时由JIT编译器进行优化。由于saveState()resetToSavedState()非常简单,因此可以在此处内联。之后,两者都成为外部方法的一部分,在构建控制流图和值分析之后,可以确定没有控制路径进入if语句,因此将修剪这样的CFG边缘。但是你不应该依赖这种情况。

通常,只能查看程序代码(不实际执行程序代码),您无法可靠地预测程序顺序。但是,额外的空检查在性能方面几乎没有任何意义,所以你不必担心它。

答案 1 :(得分:3)

  

if(savedState == null)是否在某些时候通过Java(由编译器或在运行时)进行优化,如果它知道已经调用了saveState(),那么savedState不能再为null?

JVM,而不是JIT都没有完全优化它(将来可能)。但是,CPU可以使用分支预测来假设检查将是错误的,因此几乎没有影响。即&lt; 1 ns。

答案 2 :(得分:2)

此“methodA-called-before-methodB relation”有一种模式,您甚至可以将其编码到类型系统中。

考虑构建器的流体API。这似乎完美地封装了你的问题,甚至没有进行运行时检查:

public class BasicBuildOrder {
    protected List<GameObject> gameObjects = new ArrayList<>();

    public static class SavedBuildOrder extends BasicBuildOrder {

        private List<GameObject> savedState;

        private SavedBuildOrder(Collection<GameObject> gameObjects) {
             savedState = new ArrayList<>(gameObjects);
        }

        public void resetToSavedState() {
            gameObjects = savedState;
        }
    }

    public SavedBuildOrder saveState() {
        return new SavedBuildOrder(gameObjects);
    }

    public void add(GameObject gameObject) {
        gameObjects.add(gameObject);
    }

    public void get(int index) {
        return gameObjects.get(index);
    }

    public void remove(GameObject gameObject) {
        gameObjects.remove(gameObject);
    }
}

这种相当笨重的解决方法的替代方案类似于Memento Pattern

无论如何,Tagir ValeevOliver CharlesworthFilipp可能都是正确的:

  

然而,即使没有编译器,null检查也应该相当便宜   魔法。 - 菲利普

     

然而,额外的空检查在性能方面几乎没有任何意义,   所以你不应该担心它。 - Tagir

     

除非可以通过静态分析显示该方法始终如此   调用后,必须使用某种运行时信息。因此有些   必须进行检查。 - 奥利弗