当前位置: 首页 > 面试题库 >

为什么jdk代码样式使用变量分配并在同一行上读取-例如 (i = 2)

艾谦
2023-03-14
本文向大家介绍为什么jdk代码样式使用变量分配并在同一行上读取-例如 (i = 2)相关面试题,主要包含被问及为什么jdk代码样式使用变量分配并在同一行上读取-例如 (i = 2)时的应答技巧和注意事项,需要的朋友参考一下

正如评论中已经提到的那样:集合框架和并发包的主要作者之一道格·李(Doug Lea)倾向于进行优化,这些优化对于凡人来说似乎令人困惑(甚至违反直觉)。

这里的“著名”示例是将字段复制到局部变量,以最大程度地减少字节码的大小,这实际上也是在您所引用的示例中使用table字段和局部tab变量来完成的!

对于非常简单的测试,访问是否“内联”似乎没有什么不同(指所得到的字节码大小)。因此,我尝试创建一个与getNode您提到的方法的结构大致相似的示例:对作为数组的字段的访问,长度检查,对一个数组元素的字段的访问…

  • testSeparate方法单独进行分配和检查
  • testInlined方法使用样式分配
  • testRepeated方法(作为反例)重复进行每个访问

代码:

class Node
{
    int k;
    int j;
}

public class AssignAndUseTestComplex
{
    public static void main(String[] args)
    {
        AssignAndUseTestComplex t = new AssignAndUseTestComplex();
        t.testSeparate(1);
        t.testInlined(1);
        t.testRepeated(1);
    }

    private Node table[] = new Node[] { new Node() };

    int testSeparate(int value)
    {
        Node[] tab = table;
        if (tab != null)
        {
            int n = tab.length;
            if (n > 0)
            {
                Node first = tab[(n-1)];
                if (first != null)
                {
                    return first.k+first.j;
                }
            }
        } 
        return 0;
    }

    int testInlined(int value)
    {
        Node[] tab; Node first, e; int n;
        if ((tab = table) != null && (n = tab.length) > 0 && 
            (first = tab[(n - 1)]) != null) {
            return first.k+first.j;
        }
        return 0;
    }

    int testRepeated(int value)
    {
        if (table != null)
        {
            if (table.length > 0)
            {
                if (table[(table.length-1)] != null)
                {
                    return table[(table.length-1)].k+table[(table.length-1)].j;
                }
            }
        } 
        return 0;
    }

}

以及产生的字节码:该testSeparate方法使用 41条指令

  int testSeparate(int);
    Code:
       0: aload_0
       1: getfield      #15                 // Field table:[Lstackoverflow/Node;
       4: astore_2
       5: aload_2
       6: ifnull        40
       9: aload_2
      10: arraylength
      11: istore_3
      12: iload_3
      13: ifle          40
      16: aload_2
      17: iload_3
      18: iconst_1
      19: isub
      20: aaload
      21: astore        4
      23: aload         4
      25: ifnull        40
      28: aload         4
      30: getfield      #37                 // Field stackoverflow/Node.k:I
      33: aload         4
      35: getfield      #41                 // Field stackoverflow/Node.j:I
      38: iadd
      39: ireturn
      40: iconst_0
      41: ireturn

testInlined方法的确小了一点,只有 39条指令

  int testInlined(int);
    Code:
       0: aload_0
       1: getfield      #15                 // Field table:[Lstackoverflow/Node;
       4: dup
       5: astore_2
       6: ifnull        38
       9: aload_2
      10: arraylength
      11: dup
      12: istore        5
      14: ifle          38
      17: aload_2
      18: iload         5
      20: iconst_1
      21: isub
      22: aaload
      23: dup
      24: astore_3
      25: ifnull        38
      28: aload_3
      29: getfield      #37                 // Field stackoverflow/Node.k:I
      32: aload_3
      33: getfield      #41                 // Field stackoverflow/Node.j:I
      36: iadd
      37: ireturn
      38: iconst_0
      39: ireturn

最后,该testRepeated方法使用了多达 63条指令

  int testRepeated(int);
    Code:
       0: aload_0
       1: getfield      #15                 // Field table:[Lstackoverflow/Node;
       4: ifnull        62
       7: aload_0
       8: getfield      #15                 // Field table:[Lstackoverflow/Node;
      11: arraylength
      12: ifle          62
      15: aload_0
      16: getfield      #15                 // Field table:[Lstackoverflow/Node;
      19: aload_0
      20: getfield      #15                 // Field table:[Lstackoverflow/Node;
      23: arraylength
      24: iconst_1
      25: isub
      26: aaload
      27: ifnull        62
      30: aload_0
      31: getfield      #15                 // Field table:[Lstackoverflow/Node;
      34: aload_0
      35: getfield      #15                 // Field table:[Lstackoverflow/Node;
      38: arraylength
      39: iconst_1
      40: isub
      41: aaload
      42: getfield      #37                 // Field stackoverflow/Node.k:I
      45: aload_0
      46: getfield      #15                 // Field table:[Lstackoverflow/Node;
      49: aload_0
      50: getfield      #15                 // Field table:[Lstackoverflow/Node;
      53: arraylength
      54: iconst_1
      55: isub
      56: aaload
      57: getfield      #41                 // Field stackoverflow/Node.j:I
      60: iadd
      61: ireturn
      62: iconst_0
      63: ireturn

因此,看来这种“晦涩”的查询和赋值方式确实可以节省几个字节的字节码,并且(考虑到链接的答案中关于将字段存储在局部变量中的理由),这可能是使用它的原因。这种风格。

但…

无论如何:在执行该方法几次之后,JIT将启动,并且生成的机器代码将与原始字节码“无关”-我很确定这三个版本实际上都是最后编译为相同的机器代码。

因此,最重要的是:不要使用这种样式。相反,只需编写易于阅读和维护的愚蠢代码。您将知道该轮到使用此类“优化”的时候了。

编辑:简短的附录…

我进行了进一步的测试,并比较了testSeparate与JIT生成testInlined的实际 机器代码 有关的和方法。

我对main方法进行了一些修改,以防止JIT可能采取的不切实际的过度优化或其他捷径,但实际方法并未进行任何修改。

和预期的一样:当使用热点反汇编JVM和调用该方法数千次时-XX:+UnlockDiagnosticVMOptions-XX:+LogCompilation -XX:+PrintAssembly,这两个方法的实际机器代码是 相同的

因此,JIT再次表现出色,程序员可以专注于编写 可读 代码(无论如何)。

…以及一些小的更正/说明:

我没有测试第三方法中,testRepeated因为它是 不等同 于其他方法(因此,它 可以
不产生相同的机器代码)。顺便说一下,这是将字段存储在局部变量中的策略的另一个小优点:它提供了( 非常 有限,但有时很方便)“ 线程安全 ”形式:确保数组的长度(例如执行方法时,)中的tab数组无法更改。getNode``HashMap



 类似资料:
  • 我可以在一个对象中携带一行代码吗?如果存在的话,我可以运行这些代码吗?例如,一个数组携带应用于入站消息的信息,以便进一步处理。该主题用于检索正确的信息,以进行计算等。如果主题与第二个对象相同--我想在后处理中检索代码行--并用这样一行做进一步的数据操作。 这可能吗?这样的JS执行变量中的代码行会是什么样子?

  • 问题内容: 我遇到这种奇怪的行为,但无法解释。这些是基准: 与使用变量分配进行比较,为什么比使用带有临时变量的班轮快27%以上呢? 通过Python文档,垃圾回收在timeit期间被禁用,因此并非如此。这是某种优化吗? 结果也可以在Python 2.x中重现,尽管程度较小。 运行Windows 7,CPython 3.5.1,Intel i7 3.40 GHz,64位OS和Python。似乎我尝试

  • 问题内容: 我一直在使用IIFE中和,并一直在使用的结构如下: 方法1: 但是,我经常看到以下情况,其中将变量分配给 方法2: 注意: 此问题 不是 关于此模式是什么或IIFE是什么。这与为什么要 在IIFE上 使用返回变量及其与Angular实践的关系有关。 在Angular方法1中工作正常,但是在我看到的许多原始JS示例中,都使用了方法2。我的假设是,封装在其中的任何内容都可以通过它进行调用。

  • 本文向大家介绍一键配置jdk环境变量的批处理代码,包括了一键配置jdk环境变量的批处理代码的使用技巧和注意事项,需要的朋友参考一下 第一步:肯定是下载JDK并且安装好,记住路径 创建java环境变量批处理cheng步骤: 1、新建txt文本后选择编辑。 2、输入如下代码 3、其中根据自己JDK版本以及安装路径修改第四行后面JDK路径"C:\Program Files (x86)\Java\jdk1

  • 问题内容: 我希望它不是重复的(同时鉴于存在此类错误的问题数量之多,但这是基本错误,同时也很难分辨),但我不明白这里会发生什么。 运行,没有错误(将字符转换为ASCII代码,它是内置的)。现在: 也可以运行,没有错误(不会被覆盖,条件始终为false)。现在: 我得到(在哪里) 看起来,即使不运行代码,仅引用左侧操作数也会使其成为局部变量。 显然,我可以解决此问题,但令我感到 非常 惊讶的是,由于

  • 问题内容: 我试图了解变量如何在python中工作。说我有一个对象存储在变量中: 如果我分配给,则它们都指向同一个对象: 但是,如果我重新分配或,那就不再正确了: 这两个变量现在具有不同的值: 我不明白为什么变量现在不同。为什么不再是真的?有人可以解释发生了什么吗? 问题答案: Python具有引用 对象的 名称 。对象与名称分开存在,名称与它们引用的对象分开存在。 __ 在为“名称分配名称”时,