对象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并避免垃圾回收?
答案 0 :(得分:2)
我想说第二种情况更糟。
在第一个变体amydata
中,一旦方法B()
退出(假设B不存储对amydata
的引用,就可以对其进行所有引用的对象进行垃圾回收。其他地方。
在第二个变体amydata
中,只要A
的实例存在就无法进行垃圾收集。
请考虑以下情况:在第一次调用D()
时,它返回5个对MyData
对象的引用,但是在后续调用中,它不返回任何行。在第一种变体中,amydata
返回时可以立即垃圾回收MyData
数组和5个引用的B()
对象。但是在第二种变体中,amydata
数组和通过它引用的5个MyData
对象都不能被垃圾回收-可能永远不会在应用程序的整个运行期间。
请记住:Java垃圾收集器针对许多短期对象进行了优化
答案 1 :(得分:1)
免责声明: 阅读OP的评论,我不得不承认我没有得到他的真正意图,即开发软实时应用程序,尽可能避免垃圾回收,这在Java世界中是非常特殊且罕见的情况。
因此以下答案与他的问题不符。但是,由于偶然的读者从C ++迁移到Java可能会迷惑于此问题和答案,因此他/她可能会获得一些有关典型Java编程风格的有用提示。
尽管Java和C ++的语法有很多相似之处,但是由于运行时环境差异很大,您应该采用不同的编码样式。
作为一个拥有数十年Java经验的人,我肯定会喜欢原始的方法签名。作为D()
方法的调用者,为什么我应该创建结果数据结构而不是从正在调用的方法中获取它?这会逆转自然的数据流。
我知道,在过去的C时代,动态内存管理意味着很多麻烦,在函数外部准备结果数组并让函数仅填充结果是很常见的,这是您编写第二个版本的方式。但是,使用Java则无需再理会了,只需让垃圾收集器完成其工作即可(并且在这项工作中非常出色)。通常,尝试“帮助” GC会导致实际上效率较低且难以阅读的代码。而且,如果您真的想坚持这种风格,则无需同时传递最大行数和数组,因为数组本身知道其长度(与旧式C数组不同),给出了最大行数。 / p>
您假设
第二个版本应避免创建和复制MyData的
这听起来像是对Java内部工作的误解。每次执行new MyData(...)
表达式时,都会在堆上的某个位置创建一个新实例。提供MyData[]
数组并不能避免这种情况。转换为C术语后,该数组仅保存指向MyData
对象而不是实际对象的指针。而且几乎不会复制Java实例(除非您显式调用类似object.clone()
之类的东西)。将实例分配给变量时,它只是复制实例的引用(=指针)。
但是,如果我正确理解其目的,即使第一个版本也不是完美的。 D()
方法本身可以确定何时没有更多数据可用,那么为什么返回数组的时间比必要的时间长呢?使用Java数组有点不方便,因此典型的Java代码在类似情况下会返回List<MyData>
。
关于MyData()
构造函数的另一条注释,后来又“计算或填写MyData结构”。我知道样式存在(并且在C语言家族中非常流行),但是在Java中它并不占主导地位,我尤其不喜欢它。对我来说,这听起来像是在问“给我一辆汽车”,只是得到一个骨架而不是一辆可用的汽车。如果我要有轮子,引擎和座椅,我以后必须自己提供。如果可用的汽车需要选择选项,那么我想在订购汽车时/调用构造函数时提供它们,这样我就可以诚实地将结果称为汽车而不是骨架。
最后是对Java命名约定的评论:绝大多数Java代码都遵循这些约定,因此您以大写字母开头的方法名称对我来说很奇怪。