【问题标题】:How many nested "new" objects can be created in java?在 java 中可以创建多少个嵌套的“新”对象?
【发布时间】:2014-02-25 19:10:33
【问题描述】:
class car{
    Salon s ;
}
class Salon{
     Radio musicsystem ;
}
class Radio{
    Button play ;
}
class Button{
     String s ;
}

void main(){
    car mustang = new car( new Salon( new Radio(new Button ("fight club song"))))
}

我可以很容易地想象有很多新的(新的(新的(新的 ....)))。你能走多深? 我“直觉地”觉得编译器/jvm/系统有太多级别的对象可能很糟糕......

java对深度有限制吗?

【问题讨论】:

  • 只要你有记忆。这对编译器来说不是问题。
  • 你到底为什么要写这样的代码?
  • @RohitJain,这只是一个例子,我看到了 3 个新的嵌套对象,所以我很好奇
  • 无限制创建..
  • @SotiriosDelimanolis 在 some 点它必须成为编译器的问题。但在此之前,您将达到 64K 方法大小的限制。

标签: java oop object object-oriented-analysis


【解决方案1】:

一个令人满意的答案应该是,你可以比你想看到的代码中实际发生的更深入。

如果你只是为了好玩而接受限制,我相信你实际上会遇到的第一个限制是单个方法的字节码长度的限制,它被设置为非常低的 64K .

真正担心达到限制的唯一方法是调用 递归 构造函数,例如在构造不可变链表或类似结构时可能会遇到的情况。

【讨论】:

  • @JasonSperske 不,所有这些news 的调用堆栈仍然只是一个(除非它们自己在构造函数中调用方法)。
  • @TomG 但是,在稍微不同的场景中,比如我提到的,new 将从构造函数的 within 中调用,然后它会应用。
  • @MarkoTopolnik 正确,这就是我的意思,除非那些构造函数继续调用方法。但是通过方法调用提供参数不会增加调用堆栈,因为一旦调用构造函数,提供参数的方法已经返回。
  • 所以您担心的不是news,而是递归方法参数?
【解决方案2】:

不,这完全没有问题,尤其是在像您这样的人类可读级别上。

不过,让我们看看限制。像这样的代码:

public class Foo {
  public Foo(Foo f) {}
  public static void main(String[] args) {
    new Foo(new Foo(new Foo(new Foo(null))));
  }
}

编译成:

public static void main(java.lang.String[]);
Code:
   0: new           #2                  // class Foo
   3: dup           
   4: new           #2                  // class Foo
   7: dup           
   8: new           #2                  // class Foo
  11: dup           
  12: new           #2                  // class Foo
  15: dup           
  16: aconst_null   
  17: invokespecial #3                  // Method "<init>":(LFoo;)V
  20: invokespecial #3                  // Method "<init>":(LFoo;)V
  23: invokespecial #3                  // Method "<init>":(LFoo;)V
  26: invokespecial #3                  // Method "<init>":(LFoo;)V
  29: pop           
  30: return     

即对于每个嵌套,它只使用操作数堆栈上的两个或多个元素:要实例化的类、嵌套对象以及与其一起传递的任何其他变量。

操作数堆栈的最大大小是implementation specific,但是任何JVM 都肯定能够容纳数千个变量并高效地对它们进行操作。相比之下,javac 在嵌套 1000 次后就崩溃了。

所以不,你的四级深度嵌套对于 JVM 来说绝对没有问题。

【讨论】:

    【解决方案3】:

    编译器对单个方法有 64 KB 的限制。这是因为跳转只能转到绝对字节码位置并使用 16 位无符号值。即使您没有这样的跳跃,该限制也适用。

    这会产生一些令人惊讶的后果。虽然您可能不希望以这种方式定义数千个嵌套对象,但您可能会生成一个包含数千个 ENUM 值的类。这些值是在一个静态初始化方法中创建的,这也有同样的限制,所以你只能有大约 3K 的枚举值。

    一个更可能的限制是大型数组。当您定义一个数组时,它实际上会生成代码来设置每个单元格。这是相当低效的,但一般。这也意味着您不能在 Java 中定义具有初始化值的数组,例如 10K 硬编码值。同样,这实际上只是生成代码的问题。

    【讨论】:

    • 所以理论上可以使用任何大小的方法,只要其中没有跳跃?数组和枚举填充似乎正是这种情况。我还听说javac 可以增强为自动生成方法的调用链,每个方法的大小都合适。
    • @MarkoTopolnik 一个方法链更有意义,因为大于 8K 的方法默认不会被 JITted。更有意义的是 JVM 解除了字节码的限制。 ;)
    • 但这需要对字节码进行彻底的重新设计——考虑到由于这个原因而真正停止的用例数量,这可能不值得麻烦。
    • 还有其他的东西,比如start_pcend_pc 属性,它们也接受16 位数组索引。但我不认为这是主要的一点——关键是没有非常严重的原因根本不接触字节码,例如invokedynamic——这可能是曾经引入的唯一指令从 Java 1.0 开始。
    • @MarkoTopolnik 是的,可能需要进行一些更改,但我认为为新的类版本格式扩大类型是一个相对简单的更改。对 JVM 进行任何更改都是一项挑战。
    【解决方案4】:

    你的堆内存大小。当内存满或GC无法收集对象时,它会抛出java.lang.OutOfMemoryError

    只要你有记忆,这不是问题

    【讨论】:

      【解决方案5】:

      真的,这和写作没什么不同:

      Button button = new Button ("fight club song");
      Radio radio = new Radio(button);
      Salon salon = new Salon(radio);
      car mustang = new car(salon);
      

      唯一的区别是您没有为每个引用分配一个变量,因此您以后不能引用它们。所以,答案是,只要您有足够的内存来实例化这些对象,您就可以创建任意数量的对象。

      这并不是真正的嵌套层次深度的问题,因为编译器会调用每个构造函数,初始化对象,然后返回对初始化对象的引用.这里没有递归发生,堆栈也没有随着每一级而增长。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-12-12
        • 1970-01-01
        • 1970-01-01
        • 2022-08-05
        • 1970-01-01
        相关资源
        最近更新 更多