我的解决方案需要许多常量变量,因此在进一步开发中,我可以简单地创建新原语或引用现有数据,而不是创建新数据,从而排除在未来开发过程中可能出现的错误。
我已经读过java池常量变量,当创建新数据时,它会与池进行比较,如果存在这样的对象,则返回对现有对象的引用,而不是创建新的。
虽然池化可能听起来是最好的方法,但就我而言,我需要许多短变量,每个变量分配2个字节(对于基元)。但是,如果我创建 Short ,我将丢失2个字节,因为引用将占用4个字节。
即使考虑到Short使用池化,仍然使用原语是否有意义。另外,从短到短的拆箱也需要一些资源(几乎接近于零,但仍然如此)。注意short必须不时地转换为原始的3字节数组,所以另一个+用于原语。
public static final short USER = 10;
而不是
public static final Short USER = 10;
答案 0 :(得分:4)
我不太清楚为什么你需要Short
。无论如何,作为一个拇指规则总是记住,与它们的包装相比,原语总是更快,更便宜。它们不是对象,因此许多JIT优化可以产生非常快的代码。此外,Wrappers不包含所有可能值的缓存 - 例如Integer
(默认情况下)具有-128到127的缓存。您可以扩展它,但这不是重点。
如果你需要将短片推送到一个集合中,那么可能使用缓存是有意义的(Wrappers已经有-128到127之间的缓存)。然而,如果你想推动数百万的数字,那么不使用缓存更有意义。
答案 1 :(得分:3)
这里最重要的是基元在时间和内存复杂性方面比对象包装更便宜。
如果你必须在必须使用Object引用的上下文中使用这些原语,那么池化功能才会变得相关(即它们必须被包装/"盒装"到它们的对象包装器中,google自动装箱)。如果你可以一直使用它们作为原始数字,那么它是最有效的方式。
详细说明:
Java语言将原语类型(boolean,byte,char,short,int,long,float,double)与所有其他类型(引用类型)区别对待。基元可以直接存在于堆栈中,并且可以由JVM指令直接操作(每个基元都有指令集)。数字常量通常直接嵌入到JVM指令中,这意味着执行这些指令不需要额外的内存读取。此结构或多或少地直接映射到所有硬件上的本机代码。
另一方面,引用类型不能存在于堆栈中,必须在堆上分配(这是Java语言设计选择)。这意味着每次使用它们时,必须添加指令以查找实例,读取元数据,调用方法或获取字段,然后才能对数据执行任何实际操作。
例如,假设你有这个功能
int add(int a, int b) { return a + b; }
函数的主体(除了调用约定)将只是iadd
,在大多数CPU上转换为1条指令。
如果将int
更改为Integers
s,则字节代码变为:
0: aload_1
1: invokevirtual #16 // Method java/lang/Integer.intValue:()I
4: aload_2
5: invokevirtual #16 // Method java/lang/Integer.intValue:()I
8: iadd
9: invokestatic #22 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
转换为多个内部函数调用和数千个本机指令。
随机笔记:
IIRC,Java语言规范没有定义将用于存储short
,boolean
等的字节数.JVM实现可能会使用更多字节,因此它将所有内容与字长相对应。 CPU。为了提高效率,将布尔值存储为字节或32位int并不罕见。但它确实说,对int
以下类型的操作的结果都是int
。
除非您拥有非常大的short
数组(必须在内存中无间隙地填充),所有这些意味着short
并不能真正为您节省内存。
如果你真的,真的想要使用Short
s(包装器)池,最有效的实现是一个大小为65536的数组。在这里你交换4k / 8k的内存以实现非常高效的查找。