Java的断言机制允许禁用放置断言的情况,该断言基本上没有运行时成本(除了较大的类文件之外)。但这可能涵盖所有情况。
例如,许多Java集合都具有“快速失败”迭代器,这些迭代器试图检测您何时以线程不安全的方式使用它们。但这要求集合和迭代器本身都必须维护额外的状态,如果不存在这些检查,则不需要这些状态。
假设有人想做类似的事情,但是允许禁用检查,如果禁用了检查,它将在迭代器中保存一些字节,并在ArrayList中保存更多的字节,或者其他。
或者,假设我们正在进行某种对象池化,希望能够在运行时打开和关闭它。关闭时,它应该仅使用Java的垃圾回收并且不给引用计数腾出空间,例如这样(请注意,编写的代码非常破损):
class MyClass {
static final boolean useRefCounts = my.global.Utils.useRefCounts();
static {
if(useRefCounts)
int refCount; // want instance field, not local variable
}
void incrementRefCount(){
if(useRefCounts) refCount++; // only use field if it exists;
}
/**return true if ready to be collected and reused*/
boolean decrementAndTestRefCount(){
// rely on Java's garbage collector if ref counting is disabled.
return useRefCounts && --refCount == 0;
}
}
上面的代码的麻烦在于,静态bock毫无意义。但是,是否有一些技巧可以使用低功率魔术来使沿这些路线的东西起作用? (如果允许使用高功率魔术,则核选项是生成MyClass的两个版本,并安排在开始时将正确的版本放在类路径上。)
答案 0 :(得分:1)
注意:您可能根本不需要这样做。 JIT非常擅长内联运行时已知的常量,尤其是boolean
并优化了未使用的代码。
int
字段不是理想的,但是,如果您使用的是64位JVM,则对象大小可能不会更改。
在OpenJDK / Oracle JVM(64位)上,标头默认为12个字节。对象对齐为8个字节,因此对象将使用16个字节。该字段添加4个字节,对齐后也为16个字节。
要回答这个问题,您需要两个类(除非您使用生成的代码或黑客)
class MyClass {
static final boolean useRefCounts = my.global.Utils.useRefCounts();
public static MyClass create() {
return useRefCounts ? new MyClassPlus() : new MyClass();
}
void incrementRefCount() {
}
boolean decrementAndTestRefCount() {
return false;
}
}
class MyClassPlus extends MyClass {
int refCount; // want instance field, not local variable
void incrementRefCount() {
refCount++; // only use field if it exists;
}
boolean decrementAndTestRefCount() {
return --refCount == 0;
}
}
答案 1 :(得分:1)
如果您在使用引用计数的情况下接受的开销稍高,则可以使用外部存储,即
class MyClass {
static final WeakHashMap<MyClass,Integer> REF_COUNTS
= my.global.Utils.useRefCounts()? new WeakHashMap<>(): null;
void incrementRefCount() {
if(REF_COUNTS != null) REF_COUNTS.merge(this, 1, Integer::sum);
}
/**return true if ready to be collected and reused*/
boolean decrementAndTestRefCount() {
return REF_COUNTS != null
&& REF_COUNTS.compute(this, (me, i) -> --i == 0? null: i) == null;
}
}
如果某人多次调用decrementAndTestRefCount()
而不是incrementRefCount()
,则行为上会有差异。当您的原始代码静默运行到负引用计数时,此代码将引发NullPointerException
。在这种情况下,我更喜欢失败并例外...
如果您不使用该功能,上面的代码将使您仅需static
个字段即可。大多数JVM消除关于static final
变量状态的条件都应该没有问题。
请进一步注意,该代码允许MyClass
实例在具有非零引用计数的同时获取垃圾,就像它是一个实例字段一样,但是当计数达到初始状态时,它也会主动删除映射再次为零,以最小化清理所需的工作。