代码之家  ›  专栏  ›  技术社区  ›  Swiss Frank

减少返回数组的方法的Java垃圾收集?

  •  1
  • Swiss Frank  · 技术社区  · 7 年前

    对象A有方法B(),并且在应用程序的大部分生命周期中都有效。B调用对象C方法D()。D()返回最多包含x个MyData对象的数组。MyData可能是POD(普通旧数据)/PDS(被动数据结构)或更多,但MyData可以通过调用方法或设置字段来重用;它的特性或功能不是在建造或其他过程中铸造的。

    目前B()的定义如下:

    class A {
      public B() {
        MyData[] amydata = c.D( 5 );
           :
           :
      }
    }
    

    目前,D()的定义如下:

    MyData[] D( int iRows ) {
    
        MyData[] amydata = new MyData[ iRows ];
    
        for ( int i = 0; i < iRows; i++ ) {
    
            if ( no more data )
                return amydata;
    
            amydata [ i ] = new MyData();
    
            // calculate or fill in MyData structure.
        }
    
        return amydata;
    }
    

    即使数据会有所不同,A也会一直或在很长一段时间(例如,直到用户重新配置它)要求相同数量的行。

    那么,如果我让调用者在数组引用中传递:

    class A {
      int iRequestSize = 5;
      int iResultSize;
      MyData[] amydata = new MyData[ iRequestSize ];
    
      public B() {
        iResultSize = c.D( iRequestSize, amydata );
           :
           :
        // use up to iResultSize even though array is potentially bigger.
      }
    }
    
    // returns number of rows actually used
    int D( int iRowsMax, MyData[] amydata ) {
    
        for ( int i = 0; i < iRowsMax; i++ ) {
    
            if ( no more data )
                return i;
    
            if ( amydata [ i ] == null )
                amydata [ i ] = new MyData();
    
            // calculate or fill in MyData structure.
        }
    
        return iRowsMax;
    }
    

    我是一个C++的家伙,对java来说是新手,但是假设MyDATA可以像这样循环,第二个版本应该避免创建和复制MyDATA,同时消除垃圾收集。

    2 回复  |  直到 7 年前
        1
  •  2
  •   Thomas Kläger    7 年前

    我认为第二种变体更糟糕。

    在第一种变体中 amydata 并且只要该方法成功,它引用的所有对象都可以被垃圾收集 B() 退出(假设B不存储对 amydata 其他地方。)

    在第二种变体中 amydata 只要 A 生活。

    考虑第一次呼叫时的情况 D() 它返回了对 MyData 对象,但在后续调用中,它不会返回更多行。在第一种变体中 amydata 数组和引用的5 我的数据 对象可以被垃圾收集 B() 返回。但在第二种变体中 amydata 数组也不是5 我的数据 通过它引用的对象可以被垃圾收集——可能永远不会在应用程序的整个运行时被垃圾收集。

    记住:Java垃圾收集器针对许多短期对象进行了优化

        2
  •  1
  •   Ralf Kleberhoff    7 年前

    免责声明: 在阅读OP的评论时,我不得不承认我没有理解他的真正意图,即开发一个软实时应用程序,尽可能避免垃圾收集,这在Java世界中是非常特殊和罕见的情况。

    所以下面的答案与他的问题不符。但是,作为一个偶然的读者,从C++迁移到java可能会遇到这个问题和答案,他/她可能会得到一些有用的暗示,典型java编程风格。


    虽然java和C++的语法有很多相似之处,但是由于运行时环境非常不同,所以应该采用不同的编码风格。

    作为一个几十年的Java爱好者,我当然更喜欢原始的方法签名。作为一个打电话的人 D() 方法,为什么我要创建结果数据结构,而不是从我调用的方法获取它?这逆转了数据的自然流动。

    我知道,在旧的C时代,动态内存管理意味着很多头痛的事情,在函数之外准备结果数组,让函数只填充结果是很常见的,就像编写第二个版本那样。但是,用Java忘了这一点,让垃圾收集器完成它的工作(它非常擅长这项工作)。通常,试图“帮助”GC会导致实际上效率更低、更难阅读的代码。如果你真的想坚持这种风格,就不需要同时传递最大行数和数组,因为数组本身知道它的长度(这与老式的C数组不同),从而给出最大行数。

    你认为

    第二个版本应该避免创建和复制MyData的

    这听起来像是对Java内部工作原理的误解。每次你执行命令 new MyData(...) 表达式,则在堆的某个位置创建一个新实例。提供 MyData[] 数组并不能避免这种情况。翻译成C术语后,数组只包含指向 MyData 物体,而不是真正的物体。Java实例很少被复制(除非您显式地调用 object.clone() ).当你给变量赋值时,它只是对实例的引用(=指针)被复制。

    但如果我正确理解它的目的,即使是第一个版本也远远不够完美。这个 D() 方法本身可以确定何时没有更多可用数据,所以为什么它返回的数组长度超过了需要的长度?使用Java数组有点不方便,所以典型的Java代码会返回 List<MyData> 在类似的情况下。

    还有一条评论 MyData() 构造函数,然后“计算或填写MyData结构”。我知道这种风格是存在的(在C语言家族中非常流行),但它在Java中并不占主导地位,我尤其不喜欢它。对我来说,这听起来像是在问“给我一辆车”,然后只得到一个骨架,而不是一辆可用的车。如果我想让它有轮子、引擎和座椅,我以后必须自己提供。如果一辆可用的汽车需要选择选项,我想在订购汽车/调用构造器时提供这些选项,这样我就可以诚实地将结果称为汽车而不是骨架。

    最后是对Java命名约定的评论:绝大多数Java代码都遵循这些约定,因此以大写字母开头的方法名称在我看来非常奇怪。