我刚刚意识到,当我用Java编程时,我会避免局部变量,比如瘟疫,如果我真的必须用小写"L"作为成员的前缀,并有一个重置该对象的方法(如果对象是我的,我通常有一个名为init
的私有方法或构造函数调用的东西)
我刚刚意识到这在很多方面都很丑陋。我做得对吗?
C++程序员会确切地知道我的意思,没有离开函数范围的局部会自动为我们销毁(如果它确实离开了函数范围,请使用指针,等等)
发生这种情况时的模式
我发现,每当我将适配器适配到函数参数并通过该适配器与之交互时,都是在我使用它的时候。
我还倾向于让适配器维护它使用的任何对象的池(最多一定数量)
当我想使用需要"new"进行初始化但仅在方法中的数据类型时,也会发生这种情况。
代码通常是一些主循环的一部分,否则也没关系,显然(这不是一次性的)
GC Collection amount:
Amount (Mb): | 30| 60| 90| 120| 150| 180| 210| 240| 270| 300| 330| 360|
--------------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+
With pattern |## (14)
without |################################################################### (350)
该程序通过了单元测试,显示了平均GC量。两者的标准偏差均小于5。
这感觉好像在某种程度上与飞轮模式有关。。。
没有此代码,因为:
它可以以多种方式表现出来!例如,如果您有一个ComplexNumber
类,如果您只是根据需要创建它们,您将生成大量垃圾,如果您拥有任何类型的vector
或matrix
类,也是如此。
另一个区域是涉及某种图形的任何内容,这些图形被仔细遍历以生成另一个结构,如场景图、关键路径,甚至是表示当前目录的堆栈。
基本上,如果你在一个经常被调用的方法中有"new"要分配给局部变量,你会发现这一点。
以上使用的样品
它来自我写的一个程序,教人们关于有限状态自动机和其他状态机(马尔可夫链等),我注意到严重的ram使用,并决定进行调查。
与其他语言的比较
显然C++没有这个问题。但你也不会很高兴知道Python。Python的引用计数意味着(如果你没有任何cricles),在方法结束的那一刻,东西就被删除了,实际上有一个元方法,你可以可靠地将其用作析构函数(如果你有足够的纪律,不会从方法中泄露它)
我不是第一个遇到这个问题的人,看到类似的问题表明没有解决方案,但我不敢相信以前从未遇到过这种情况!
关于我
我从C++和Python(都喜欢它们!)到Java,我在Java方面"经验丰富",因为我可以读/写有效的东西,它遵循了一个很好的设计哲学等等,但我倾向于非常注意性能和资源。我从恐慌中解脱出来,我是一个彻头彻尾的恐慌妓女。
如何不进行池化
假设您有一个GroupElement
类,它表示代数群的一个成员,我们将使用加法表示法。
假设g.add(h)
返回一个新元素,如果你多次这样做,你就会有很多元素。如果你有:
GroupElement f = new GroupElement(0); //identity
g.add(f,h);
其中:
add
的第一个参数是放置结果的位置,我们不生成垃圾。
不遵循上述内容的人
你应该知道复数是什么?假设一个复数有一个名为add
的方法,它接受一个复数并返回一个新的复数。如果你做了很多次a=b.add(c);
,你会得到很多减去1的垃圾复数。
如果你有inplaceAdd(ComplexNumber target, ComplexNumber value)
,说在哪里:
target.real = value.real+real;
target.im = value.im+im;
如果执行以下操作,则不会创建垃圾:b.inplaceAdd(a,c)
,其操作与上面的a=b.add(c)
相同
BTW add
可以做到:return new ComplexNumber(real+value.real,im+value.im)
——明白我现在的意思了吗?
示例的实现(说真的,伙计们,你们怎么不明白!)
public class ComplexNumber {
private double real;
private double im;
public ComplexNumber(double r, double i) {
real = r;
im = i;
}
public ComplexNumber add(ComplexNumber value) {
return new ComplexNumber(real + value.real, im + value.im);
}
public void inplaceAdd(ComplexNumber target, ComplexNumber value) {
target.real = real + value.real;
target.im = im + value.im;
}
}
例如,如果您有一个
ComplexNumber
类,如果您只是根据需要创建它们,您将生成大量垃圾,如果您拥有任何类型的vector
或matrix
类,也是如此。
请记住,垃圾是免费的;垃圾收集的成本由必须遍历的非垃圾决定。(我的意思是,VM规范实际上并没有具体说明GC必须如何实现,但主要的GC都是这样工作的。)这是有意为之:显然,Java实现不能使用引用计数没有技术原因;只是它被认为不是很健壮/高效/可靠等。(在C++、Perl和Python中,引用计数为您提供了可预测析构函数的优势。Java不提供这一点;相反,它提供了finally
块和try
-资源。)