You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取Java数组中每个元素的精确内存地址?

获取Java数组元素的精确内存地址解决方案

问题分析

  1. 初始代码崩溃原因:Unsafe.getAddress(long)的参数是内存偏移量,你直接传入数组元素的int值,这个值被当作无效内存地址访问,导致JVM致命错误。
  2. 修改后代码的局限:你传入的是一维数组对象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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 06:15:34