用于内存缩减的Java整数标志和按位操作的性能
使用整数标志和按位操作是减少大容量对象内存占用的有效方法吗
内存占用
我的理解是
boolean
通常在JVM实现中存储为int
。这是正确的吗?在这种情况下,32个标志无疑代表了内存占用的大幅减少当然,尽管JVM实现各不相同,但情况并非总是如此
性能
据我所知,CPU是非常数字驱动的,按位运算的效率与计算中的效率差不多
与布尔运算相比,使用位运算是否会降低性能,甚至增加性能
备选方案
有没有更好的方法来完成同样的事情?枚举是否允许组合标志,即
FLAGX = FLAG1 | FLAG2
示例代码
注意,最后一个方法propogateMove()
是递归的,每秒可能被调用数百次,并且对应用程序的响应性有直接影响,因此使用标志来避免逻辑位和调用其他方法
// FLAGS helper functions
private final void setclear(int mask, boolean set) { if (set) set(mask); else clear(mask); }
private final void set(int mask) { flags |= mask; }
private final void clear(int mask) { flags &= ~mask; }
private final boolean test(int mask) { return ((flags & mask) == mask); }
// Flags //////////////////////////////////////////////////////////////////////
private static final boolean HORIZONTAL = true;
private static final boolean VERTICAL = false;
private static final int ORIENT = 0x00000001;
private static final int DISPLAY = 0x00000002;
private static final int HSHRINK = 0x00000004;
private static final int VSHRINK = 0x00000008;
private static final int SHRINK = HSHRINK | VSHRINK;
private static final int TILE_IMAGE = 0x00000010;
private static final int CURSOR = 0x00000020;
private static final int MOUSEINSIDE = 0x00000040;
private static final int MOUSEINSIDE_BLOCKED = 0x00000080;
private static final int CONSTRAIN = 0x00000100;
private static final int CONSTRAIN_DESCENDENT = 0x00000200;
private static final int PLACE = 0x00000400;
private static final int PLACE_DESCENDENT = 0x00000800;
private static final int REFLOW = CONSTRAIN | CONSTRAIN_DESCENDENT | PLACE | PLACE_DESCENDENT;
private static final int PACK = 0x00001000;
private static final int CLIP = 0x00002000;
private static final int HAS_WIDTH_SLACK = 0x00004000;
private static final int HAS_HEIGHT_SLACK = 0x00008000;
private static final int ALIGN_TOP = 0x00010000;
private static final int ALIGN_BOTTOM = 0x00020000;
private static final int ALIGN_LEFT = 0x00040000;
private static final int ALIGN_RIGHT = 0x00080000;
private static final int ALIGNS = ALIGN_TOP | ALIGN_BOTTOM | ALIGN_LEFT | ALIGN_RIGHT;
private static final int ALIGN_TOPLEFT = ALIGN_TOP | ALIGN_LEFT;
private static final int ALIGN_TOPRIGHT = ALIGN_TOP | ALIGN_RIGHT;
private static final int ALIGN_BOTTOMLEFT = ALIGN_BOTTOM | ALIGN_LEFT;
private static final int ALIGN_BOTTOMRIGHT = ALIGN_BOTTOM | ALIGN_RIGHT;
private static final int ENTER_TRAP = 0x00100000;
private static final int LEAVE_TRAP = 0x00200000;
private static final int _MOVE_TRAP = 0x00400000;
private static final int MOVE_TRAP = 0x00800000;
private static final int CHILDREN_READ_TRAP = 0x01000000;
private static final int CHILDREN_TRAP = 0x02000000;
private static final int PLACE_CLEAN = 0x03000000;
private static final int SHRINK_TRAP = 0x04000000;
private static final int HSHRINK_TRAP = 0x10000000;
private static final int VSHRINK_TRAP = 0x20000000;
//private static final int UNUSED = 0x40000000;
//private static final int UNUSED = 0x80000000;
// Flags in switch ////////////////////////////////////////////////////////////
/** get align value as a string from align flags */
private JS alignToJS() {
switch(flags & ALIGNS) {
case (ALIGN_TOPLEFT):
return SC_align_topleft;
case (ALIGN_BOTTOMLEFT):
return SC_align_bottomleft;
case (ALIGN_TOPRIGHT):
return SC_align_topright;
case (ALIGN_BOTTOMRIGHT):
return SC_align_bottomright;
case ALIGN_TOP:
return SC_align_top;
case ALIGN_BOTTOM:
return SC_align_bottom;
case ALIGN_LEFT:
return SC_align_left;
case ALIGN_RIGHT:
return SC_align_right;
case 0: // CENTER
return SC_align_center;
default:
throw new Error("This should never happen; invalid alignment flags: " + (flags & ALIGNS));
}
}
// Flags in logic /////////////////////////////////////////////////////////////
private final boolean propagateMove(int mousex, int mousey) throws JSExn {
// start with pre-event _Move which preceeds Enter/Leave
if (test(_MOVE_TRAP)) {
if (Interpreter.CASCADE_PREVENTED == justTriggerTraps(SC__Move, JSU.T)) {
// _Move cascade prevention induces Leave
propagateLeave();
// propagate cascade prevention
return true;
}
}
// REMARK: anything from here on in is a partial interruption relative
// to this box so we can not call propagateLeave() directly upon it
int i;
boolean interrupted = false;
if (!test(PACK)) {
// absolute layout - allows for interruption by overlaying siblings
for (Box b = getChild(i=treeSize()-1); b != null; b = getChild(--i)) {
if (!b.test(DISPLAY)) {
continue;
}
if (interrupted) {
b.propagateLeave();
continue;
}
int b_mx = mousex-getXInParent(b);
int b_my = mousey-getYInParent(b);
if (b.inside(b_mx, b_my)) {
if (b.propagateMove(b_mx, b_my)) {
interrupted = true;
}
} else {
b.propagateLeave();
}
}
} else {
// packed layout - interrupted still applies, plus packedhit shortcut
boolean packedhit = false;
for (Box b = getChild(i=treeSize()-1); b != null; b = getChild(--i)) {
if (!b.test(DISPLAY)) {
continue;
}
if (packedhit) {
b.propagateLeave();
continue;
}
int b_mx = mousex-getXInParent(b);
int b_my = mousey-getYInParent(b);
if (b.inside(b_mx, b_my)) {
packedhit = true;
if (b.propagateMove(b_mx, b_my)) {
interrupted = true;
}
} else {
b.propagateLeave();
}
}
}
// child prevented cascade during _Move/Move which blocks
// Enter on this box - invoking Leave if necessary
if (interrupted) {
if (test(MOUSEINSIDE)) {
if (!test(MOUSEINSIDE_BLOCKED)) {
// mouse previously inside, now blocked so invoke Leave
set(MOUSEINSIDE_BLOCKED);
if (test(LEAVE_TRAP)) {
justTriggerTraps(SC_Leave, JSU.T);
}
}
} else {
// mouse not previously inside, Enter not yet triggered, so
// do not invoke Leave
set(MOUSEINSIDE);
set(MOUSEINSIDE_BLOCKED);
}
// propagate cascade prevention
return true;
}
// set cursor if applicable to this box
if (test(CURSOR)) {
Surface s = getSurface();
if (s!=null && !s.cursorset) {
s.cursor = JSU.toString(getAndTriggerTraps(SC_cursor));
s.cursorset = true;
}
}
// fire Enter traps
if (!test(MOUSEINSIDE)) {
set(MOUSEINSIDE);
if (test(ENTER_TRAP)) {
justTriggerTraps(SC_Enter, JSU.T);
}
}
// finish post-event Move which follows Enter/Leave
if (test(MOVE_TRAP)) {
if (Interpreter.CASCADE_PREVENTED == justTriggerTraps(SC_Move, JSU.T)) {
// propagate cascade prevention
return true;
}
}
// propagation uninterrupted
return false;
}
# 1 楼答案
当然,这取决于JVM的实现,但对于主流CPU上的实现可能是如此
如果一个类中实际上有32个标志,并且该类有大量实例,那么是的。如果您的实例从未超过几百个,那么就不值得担心了
这是真的
这还取决于内存使用情况。如果只对少数对象进行非常密集的操作,则按位操作可能会减慢速度。如果有很多对象,由于缓存性能更好,减少的内存可能会大大提高性能
您可以(并且应该)使用^{} ,而不是自己执行逐位操作。是的,如果您可以使用enum和^{} ,它会更干净,但是如果您有许多enum,每个都有几个元素,那么由于多个
EnumSet
实例的开销,它可能不会产生所需的内存节省# 2 楼答案
如果一个对象有大量这样的标志,并且有大量这样的对象(或者减少堆使用是一个关键问题),那么这可能是一个值得进行的微优化。否则没有(海事组织)
这样做的问题是,它使代码更难阅读,也更脆弱。因此,只有当内存使用是一个关键问题时,您才应该考虑它
# 3 楼答案
是的,布尔值存储为32位整数。方法签名区分布尔值和整数,但在其他情况下,它们的处理方式相同。这是JVM字节码规范的一部分
Java上的按位操作直接映射到CPU上执行相同按位操作的单个指令,因此它确实非常快。当然,将每个值保存在其自己的32位字中会更快(这样就根本不需要执行任何逐位操作)。使用您的方法将节省内存并花费更多的CPU周期
Java没有用于组合枚举的运算符,我不认为这有什么意义
# 4 楼答案
因为你不能相信布尔值是以位或整数的形式存储的(我认为后者是实际的实现),是的,我认为位掩码和标志确实提高了性能
我在一些项目中使用过它们。它们的可读性较差,但我从不在意这一点,因为我的观点是每个程序员都应该理解二进制符号和算术
建议:对于java 7,我们可以这样定义数字文本:
甚至像这样: