如何获取Java数组中每个元素的精确内存地址?
获取Java数组元素的精确内存地址解决方案
问题分析
- 初始代码崩溃原因:
Unsafe.getAddress(long)的参数是内存偏移量,你直接传入数组元素的int值,这个值被当作无效内存地址访问,导致JVM致命错误。 - 修改后代码的局限:你传入的是一维数组对象
arr[i],所以得到的是该数组对象本身的地址,而非其中元素的地址。
解决方案
要获取基本类型数组中每个元素的内存地址,需要结合数组对象的起始地址、数组元素的基础偏移量和元素大小来计算:
- 获取
Unsafe实例(通过反射访问内部字段); - 计算一维数组对象的起始内存地址;
- 获取目标数组类型的基础偏移量(数组对象中第一个元素相对于对象起始地址的偏移)和元素大小;
- 每个元素的地址 = 数组对象起始地址 + 基础偏移量 + 元素索引 × 元素大小。
完整代码示例
import sun.misc.Unsafe; import java.lang.reflect.Field; public class ArrayElementAddress { // 将int无符号转换为long,避免符号扩展导致的地址错误 private static long normalize(int value) { if (value >= 0) return value; return (~0L >>> 32) & value; } // 获取任意对象的起始内存地址 private static long getObjectAddress(Object obj) throws NoSuchFieldException, IllegalAccessException { Object[] holder = new Object[]{obj}; Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); unsafeField.setAccessible(true); Unsafe unsafe = (Unsafe) unsafeField.get(null); // 获取Object[]数组的基础偏移量,holder[0]的地址就是holder的起始地址+baseOffset long baseOffset = unsafe.arrayBaseOffset(Object[].class); return normalize(unsafe.getInt(holder, baseOffset)); } public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { int[][] arr = new int[3][4]; // 填充测试数据 for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr[i].length; j++) { arr[i][j] = i * 10 + j; } } Unsafe unsafe = null; Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe"); unsafeField.setAccessible(true); unsafe = (Unsafe) unsafeField.get(null); // 获取int数组的基础偏移量和元素大小 int intArrBaseOffset = unsafe.arrayBaseOffset(int[].class); int intElementSize = unsafe.arrayIndexScale(int[].class); for (int i = 0; i < arr.length; i++) { int[] subArr = arr[i]; // 获取当前一维数组对象的起始地址 long subArrAddress = getObjectAddress(subArr); System.out.printf("一维数组arr[%d]的起始地址: 0x%X%n", i, subArrAddress); for (int j = 0; j < subArr.length; j++) { // 计算第j个元素的内存地址 long elementAddress = subArrAddress + intArrBaseOffset + (long) j * intElementSize; System.out.printf("元素arr[%d][%d]的值: %d, 内存地址: 0x%X%n", i, j, subArr[j], elementAddress); } System.out.println("---"); } } }
关键说明
arrayBaseOffset(Class<?> arrayClass):返回数组类型中第一个元素相对于数组对象起始地址的偏移量;arrayIndexScale(Class<?> arrayClass):返回数组中每个元素的大小(单位:字节),对于int[]来说固定为4;getObjectAddress(Object obj):通过将对象放入Object[]数组,利用Unsafe获取数组第一个元素的地址,即目标对象的起始地址;- 运行注意事项:Java 9及以上版本需要添加VM参数
--add-exports java.base/sun.misc=ALL-UNNAMED才能访问sun.misc.Unsafe;部分JVM可能禁用了Unsafe的使用,或开启指针压缩(默认开启),此时显示的地址是压缩后的32位地址,如需64位地址可添加参数-XX:-UseCompressedOops。
内容的提问来源于stack exchange,提问作者littlestone
相关产品推荐
相关产品推荐

