我的解决方案需要许多常量变量,因此在进一步的开发中,我可以简单地创建新的原语或引用现有数据,而不是创建新的原语,以排除将来开发过程中可能犯的错误。

我已经读过Java池中的常量变量,当创建新数据时它将与池进行比较,如果存在这样的对象,它将返回对现有对象的引用,而不是创建新对象。

虽然池化听起来可能是最好的方法,但在我的情况下,我需要许多短变量,每个短变量分配2个字节(用于基本类型)。但是,如果我创建Short,我将丢失2个字节,因为引用将占用4个字节。

即使考虑到Short使用池,仍然使用原语是否有意义?另外,从“空”到“空”的拆箱也需要一些资源(这些资源几乎接近零,但仍然)。请注意,short将不得不不时地转换为原始3字节数组,因此对于原始将另加+。

public static final short USER = 10;


代替

public static final Short USER = 10;

最佳答案

这里最重要的是,在时间和内存复杂性上,原语都比对象包装便宜。

仅当您必须在必须使用对象引用的上下文中使用这些原语(即必须将它们包装/“装箱”到其对象包装器,谷歌自动装箱中)时,池功能才有意义。如果您一直可以将它们用作原始数字,那么这是最有效的方法。

细节:

Java语言对待原语类型(布尔型,字节型,字符型,短型,整型,长型,浮点型,双精度型)的方式与所有其他类型(引用类型)不同。这些原语可以直接存在于堆栈上,并且可以通过JVM指令(每个原语都有一组指令)直接进行操作。数字常量通常直接嵌入到JVM指令中,这意味着不需要额外的内存读取即可执行这些指令。这种结构或多或少直接映射到所有硬件上的本机代码。

另一方面,引用类型不能存在于堆栈中,而必须在堆上分配(这是Java语言设计的选择)。这意味着每次使用它们时,都必须添加指令以查找实例,读取元数据,调用方法或获取字段,然后才能对数据执行任何实际操作。

例如,说你有功能

int add(int a, int b) { return a + b; }


函数的主体(除调用约定外)将简单地为iadd,在大多数CPU上可转换为1条指令。

如果将int更改为Integers,则字节码变为:

   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语言规范没有定义将使用多少字节来存储shortboolean等。JVM实现可能使用更多的字节,因此它将所有内容与CPU的字长对齐。将布尔值存储为字节或32位int纯粹是出于效率目的,这并不稀奇。它确实说过,对小于int的类型进行运算的结果全部以int形式出现。

所有这些意味着short并不会真正为您节省任何内存,除非您有非常大的short数组(必须打包而没有内存缺口)。

而且,如果您真的要使用Short池(包装器),则最有效的实现是数组65536。在这里,您将4k / 8k的内存交换为非常有效的查找。

关于java - Java短常量池与短原语,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31921860/

10-11 17:07