我需要在模拟循环中经常使用多种方法调用类的方法。
其中一些方法需要访问临时对象以在其中存储信息。离开该方法后,不再需要存储的数据。 例如:
Class class {
method1() {
...
SomeObject temp = new SomeObject();
...
}
method2() {
...
SomeObject temp = new SomeObject();
SomeObject temp2 = new SomeObject();
...
}
}
我需要尽可能地优化。最昂贵的(可移动的)问题是发生了太多的分配。 我认为最好不要每次都分配这些对象所需的空间,所以我想保留它们。
以静态方式存储它们会更有效吗? 像:
Class class {
private (static?) SomeObject temp;
private (static?) SomeObject temp2;
methods...
}
还是有更好的方法吗?谢谢你的帮助!
根据答案进行修改 内存占用不是实际问题,而是垃圾收集清理混乱。 SomeObject是一个类似Point2D的类,没有内存昂贵(在我看来)。 我不确定使用(最终是静态的)类级别对象作为占位符还是一些我不知道的更高级的方法更好。
答案 0 :(得分:3)
我会在这个预成熟优化的例子中保持警惕。通常情况下,它会使代码更复杂(并且复杂性使错误更容易),更难以阅读,可能会引入错误,可能无法提供您期望的加速等等。对于一个简单的对象,例如表示2D点,坐标,我不担心重复使用。如果您要么使用大量内存,避免使用冗长的昂贵构造函数,或者将对象构造从经常执行的紧密循环中拉出来,通常重用会获得最大的好处。
您可以尝试一些不同的策略:
将责任推送给调用者一种方法是让调用者在预先初始化的对象中传递,使方法参数最终。但是,这是否有效取决于您需要对该对象执行的操作。
指向临时对象的方法作为方法参数另一种方法是让调用者作为对象传递作为参数,其目的本质上是指向方法应该执行临时对象的指针存储。我认为这种技术在C ++中更常用,但效果相似,但有时会出现在图形编程等地方。
对象池重用临时对象的一种常用方法是使用对象池,其中对象是从固定的“可用”对象库中分配的。这有一些开销,但是如果对象很大,并且经常只在很短的时间内使用,那么内存碎片可能会引起关注,那么开销可能就不足以值得考虑。
成员变量如果您不关心对该方法的并发调用(或者使用同步来防止这种情况),您可以模拟“本地静态”变量的C ++主题,为您的存储创建类的成员变量。它使代码的可读性降低,并且使用变量引入意外干扰代码的其他部分的空间略大,但开销低于对象池,并且不需要更改方法签名。如果这样做,您也可以选择在变量上使用transient
关键字来表示该变量不需要序列化。
我会回避临时的静态变量,除非该方法也是静态的,因为这可能会在程序运行的整个时间内产生内存开销,这是不可取的,并且与此目的的成员变量相同的缺点x2(同一类的多个实例)
答案 1 :(得分:2)
请注意,temp
和temp2
本身不是对象,而是指向SomeObject
类型对象的变量。您计划这样做的方式,唯一的区别是temp
和temp2
将是实例变量而不是局部变量。调用
temp = new SomeObject();
仍会在堆上分配新的SomeObject
。
此外,将它们设置为静态或实例变量而不是本地变量将导致最后分配的SomeObject
保持强可访问性(只要您的class
实例在实例变量的范围内),它们不会被垃圾收集,直到重新分配变量。
以这种方式进行优化可能效果不佳。目前,temp
和temp2
超出范围后,他们指向的SomeObject
将有资格进行垃圾回收。
如果您仍然对内存优化感兴趣,则需要显示SomeObject
的内容,以获取有关如何缓存其所持信息的建议。
答案 2 :(得分:1)
这些物体有多大。在我看来,你可以有类级别的对象(不一定是静态的。我会回到那里)。对于SomeObject,您可以使用一种方法来清除其内容。在一个地方完成使用后,请调用方法清除其内容。
就静态而言,多个呼叫者是否会使用此类并具有不同的值?如果是这样,请不要使用静态。
答案 3 :(得分:1)
首先,您需要确保您确实遇到此问题。垃圾收集器的好处是它可以自动处理所有临时对象。
无论如何,假设您运行单线程应用程序,并且在任何给定时间最多使用MAX_OBJECTS。一种解决方案可能是这样的:
public class ObjectPool {
private final int MAX_OBJECTS = 5;
private final Object [] pool = new Object [MAX_OBJECTS];
private int position = 0;
public Object getObject() {
// advance to the next object
position = (position + 1) % MAX_OBJECTS;
// check and create new object if needed
if(pool[position] == null) {
pool[position] = new Object();
}
// return next object
return pool[position];
}
// make it a singleton
private ObjectPool() {}
private static final ObjectPool instance = new ObjectPool();
public static ObjectPool getInstance() { return instance;}
}
以下是用法示例:
public class ObjectPoolTest {
public static void main(String[] args) {
for(int n = 0; n < 6; n++) {
Object o = ObjectPool.getInstance().getObject();
System.out.println(o.hashCode());
}
}
}
这是输出:
0) 1660364311
1) 1340465859
2) 2106235183
3) 374283533
4) 603737068
5) 1660364311
您可以注意到第一个和最后一个数字是相同的 - MAX_OBJECTS + 1次迭代返回相同的临时对象。