自动装箱可能会占用大量内存。例如:
Map<Integer, Integer> square = new HashMap<Integer, Integer>(); for(int i = 256; i < 1024; i++) { square.put(i, i * i); // 自动装箱大整数 }
通常会消耗大量的内存(6k的实际数据约为60kb)。
此外,带框整数通常需要在内存中进行额外的往返,因此会使CPU缓存的效率降低。在上面的示例中,所访问的内存被扩展到五个不同的位置,这些位置可能位于内存的完全不同的区域中:1.HashMap对象; 2。映射的Entry[] table对象; 3。Entry对象; 4。条目key对象(将原语装箱)键); 5. entrysvalue对象(装箱原始值)。
class Example { int primitive; // 直接存储在“ Example”类中 Integer boxed; // 引用另一个存储位置 }
读取boxed需要两次内存访问,而primitive只能访问一次。
从此映射获取数据时,看似无害的代码
int sumOfSquares = 0; for(int i = 256; i < 1024; i++) { sumOfSquares += square.get(i); }
等效于:
int sumOfSquares = 0; for(int i = 256; i < 1024; i++) { sumOfSquares += square.get(Integer.valueOf(i)).intValue(); }
典型地,上述代码使得创建和垃圾收集的的Integer每个对象的操作。(有关更多详细信息,请参见下面的注释。)Map#get(Integer)
为了减少这种开销,一些图书馆提供原始类型那些优化集合不要求拳击。除了避免装箱开销外,这些集合每个条目所需的内存减少约4倍。尽管Java Hotspot可以通过使用堆栈而不是堆上的对象来优化自动装箱,但无法优化内存开销和由此产生的内存间接性。
Java 8流还具有针对原始数据类型的优化接口,例如IntStream不需要装箱的接口。
注意:典型的Java运行时维护工厂方法和自动装箱Integer使用的简单缓存以及其他原始包装对象valueOf。对于Integer,此缓存的默认范围是-128到+127。一些JVM提供了JVM命令行选项来更改缓存大小/范围。