在运行时,int
和 Integer
在内存上的区别主要体现在它们的存储位置和内存分配方式上。以下是详细说明:
1. 内存分配
-
int:
- 作为基本数据类型,
int
直接存储在栈内存中。 - 栈内存用于存储方法调用过程中的局部变量,存取速度快,生命周期随方法调用的结束而结束。
- 因为
int
是直接存储数值,不涉及对象的创建和垃圾回收,所以效率较高。
- 作为基本数据类型,
-
Integer:
- 作为包装类,
Integer
是对象类型,存储在堆内存中。 - 堆内存用于存储所有的对象,生命周期由垃圾回收器管理。
Integer
对象包含了一个int
类型的成员变量,用于存储实际的数值,同时还包含了对象头信息和其他元数据。
- 作为包装类,
2. 内存布局
-
int:
- 在栈内存中,
int
直接占用 4 字节(32 位)空间,表示实际的整数值。
- 在栈内存中,
-
Integer:
- 在堆内存中,一个
Integer
对象占用的空间要比一个int
多。除了实际的int
值外,还包括对象头和一些额外的元数据。 - 对象头包含了用于垃圾回收和类型信息的指针,以及其他与对象有关的数据。在 HotSpot 虚拟机中,一个对象头通常占用 12 字节(在 64 位 JVM 中,没有开启指针压缩时)。
- 实际的
int
值占用 4 字节。 - 额外的元数据和对齐填充可能会导致
Integer
对象占用更多的内存,具体取决于 JVM 的实现。
- 在堆内存中,一个
3. 示例说明
以下是一个简单的示例,用于说明在内存中的布局:
public class Main {
public static void main(String[] args) {
// 使用 int
int intValue = 10;
// 使用 Integer
Integer integerObject = 10;
// 打印内存地址(伪代码,实际 Java 不提供直接获取内存地址的方法)
System.out.println("int value memory address: " + System.identityHashCode(intValue));
System.out.println("Integer object memory address: " + System.identityHashCode(integerObject));
}
}
4. 内存使用示意图
-
int 在栈内存中的布局:
|--------------------| | intValue (10) | <- 栈 |--------------------|
-
Integer 在堆内存中的布局:
栈: |--------------------| | integerObject ref | -> 指向堆中的 Integer 对象 |--------------------| 堆: |--------------------| | Object Header | |--------------------| | int value (10) | |--------------------|
总结
int
:直接在栈内存中分配,内存占用少,访问速度快。Integer
:在堆内存中分配,需要额外的内存开销来存储对象头和元数据,访问速度相对较慢,需要依赖垃圾回收机制管理生命周期。
了解这些内存上的区别,有助于在开发中根据具体需求选择合适的数据类型,以优化性能和内存使用。
整数缓存机制
在 Java 中,Integer
类通过一个内部的缓存机制来缓存一定范围内的整数对象。这个缓存范围默认是从 -128
到 127
,在这个范围内的整数会被缓存起来,重复使用,以减少对象的创建开销。
实现原理
整数缓存机制在 Integer
类的源码中实现。以下是相关代码片段:
public final class Integer extends Number implements Comparable<Integer> {
// 缓存的最小值
private static final int LOW = -128;
// 缓存的最大值,默认是127,可以通过启动参数调整
private static final int HIGH;
// 缓存数组
private static final Integer[] CACHE;
static {
// 获取最大缓存值
int high = 127;
String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
int parsed = Integer.parseInt(integerCacheHighPropValue);
high = Math.max(parsed, high);
}
HIGH = high;
// 初始化缓存数组
CACHE = new Integer[(HIGH - LOW) + 1];
int j = LOW;
for (int k = 0; k < CACHE.length; k++) {
CACHE[k] = new Integer(j++);
}
}
// 返回缓存的 Integer 对象或创建新的 Integer 对象
public static Integer valueOf(int i) {
if (i >= LOW && i <= HIGH) {
return CACHE[i + (-LOW)];
}
return new Integer(i);
}
}
工作原理
-
缓存初始化:
Integer
类在静态代码块中初始化了一个Integer
对象的缓存数组,默认缓存范围是从-128
到127
。- 缓存数组
CACHE
存储了这些范围内的所有Integer
对象。
-
缓存获取:
- 当调用
Integer.valueOf(int i)
方法时,如果i
的值在缓存范围内(-128
到127
),则直接返回缓存数组中的Integer
对象。 - 如果
i
的值不在缓存范围内,则创建一个新的Integer
对象并返回。
- 当调用
优势
-
减少对象创建:
- 缓存机制避免了在常用范围内频繁创建相同的
Integer
对象,减少了内存分配和垃圾回收的开销。
- 缓存机制避免了在常用范围内频繁创建相同的
-
提高性能:
- 通过缓存,常用整数的对象复用提高了程序的运行性能,因为直接从缓存中获取对象比创建新对象要快。
示例
以下是一个简单的示例,演示了缓存机制的效果:
public class IntegerCacheTest {
public static void main(String[] args) {
// 在缓存范围内的整数
Integer a = Integer.valueOf(100);
Integer b = Integer.valueOf(100);
// 超出缓存范围的整数
Integer c = Integer.valueOf(200);
Integer d = Integer.valueOf(200);
// 比较内存地址
System.out.println(a == b); // 输出: true
System.out.println(c == d); // 输出: false
}
}
在上面的示例中:
a
和b
是相同的对象,因为100
在缓存范围内。c
和d
是不同的对象,因为200
超出了默认缓存范围。
自定义缓存范围
可以通过 JVM 启动参数 -XX:AutoBoxCacheMax=<size>
来调整缓存范围,例如:
java -XX:AutoBoxCacheMax=1000 IntegerCacheTest
这样可以将缓存范围扩展到 -128
到 1000
。
总结:
Integer
类维护了一个缓存数组,用于缓存常用范围内的Integer
对象。- 通过缓存机制,提高了内存使用效率和运行性能。
- 可以通过 JVM 启动参数调整缓存范围。