java 里使用unsafe类实现只分配内存不初始化的实例

package UnSafe;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class UnsafeUtil {
    private static final Object UNSAFE;
    private static final Method ALLOCATE_INSTANCE;

    static {
        try {
            // 1. 通过反射加载 sun.misc.Unsafe 类
            Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
            // 2. 获取 Unsafe 类中名为 "theUnsafe" 的私有静态字段
            Field field = unsafeClass.getDeclaredField("theUnsafe");
            // 3. 绕过 Java 的访问控制检查,使其可以访问
            field.setAccessible(true);
            // 4. 获取该字段的值。由于它是静态的,传入 null 作为对象参数
            UNSAFE = field.get(null);
            // 5. 缓存 allocateInstance 方法
            ALLOCATE_INSTANCE = unsafeClass.getDeclaredMethod("allocateInstance", Class.class);
            ALLOCATE_INSTANCE.setAccessible(true);
        } catch (ClassNotFoundException | NoSuchFieldException | NoSuchMethodException | IllegalAccessException e) {
            throw new RuntimeException("无法获取 Unsafe 实例", e);
        }
    }

    public static Object getUnsafe() {
        return UNSAFE;
    }

    @SuppressWarnings("unchecked")
    public static <T> T allocateInstance(Class<T> clazz) throws InstantiationException {
        try {
            return (T) ALLOCATE_INSTANCE.invoke(UNSAFE, clazz);
        } catch (Exception e) {
            throw new InstantiationException(e.getMessage());
        }
    }
}
package UnSafe;

public class YourFrameworkClass {
    public static <T> T createInstanceWithoutConstructor(Class<T> clazz) throws InstantiationException {
        // 注意:allocateInstance 本身声明了 InstantiationException
        // 但如果传入的是接口、抽象类、数组或原始类型,会抛出异常
        return UnsafeUtil.allocateInstance(clazz);
    }

    // 使用示例
    public static void main(String[] args) throws Exception {
        // 假设有一个类,其构造器可能很重,或私有的,或有副作用
        class MyClass {
            private int value = 10; // 字段会被初始化为默认值 0,而非 10
            public MyClass() {
                System.out.println("构造器被调用");
                this.value = 20;
            }
            public int getValue() { return value; }
        }

        // 常规方式:构造器被调用,输出 "构造器被调用",value 为 20
        MyClass normal = new MyClass();
        System.out.println(normal.getValue()); // 输出 20

        // Unsafe 方式:构造器未被调用,value 被 JVM 零值初始化为 0
        MyClass unsafeInstance = createInstanceWithoutConstructor(MyClass.class);
        System.out.println(unsafeInstance.getValue()); // 输出 0
    }

}
package UnSafe;

import java.lang.reflect.Field;

/**
 * 演示使用 Unsafe 解决循环依赖问题
 * 
 * 场景:A 依赖 B,B 依赖 A,形成循环依赖
 * 传统方式:new A(new B(new A(...))) 无法完成
 * Unsafe 方式:先创建空壳对象,再注入依赖
 */
public class CircularDependencyDemo {

    // ========== 1. 定义循环依赖的类 ==========
    
    static class ServiceA {
        private ServiceB serviceB;
        private String name = "ServiceA";

        public ServiceA(ServiceB serviceB) {
            this.serviceB = serviceB;
            System.out.println("ServiceA 构造器被调用");
        }

        public void doSomething() {
            System.out.println(name + " 调用 " + serviceB.getName());
        }

        public String getName() {
            return name;
        }

        public ServiceB getServiceB() {
            return serviceB;
        }
    }

    static class ServiceB {
        private ServiceA serviceA;
        private String name = "ServiceB";

        public ServiceB(ServiceA serviceA) {
            this.serviceA = serviceA;
            System.out.println("ServiceB 构造器被调用");
        }

        public void doSomething() {
            System.out.println(name + " 调用 " + serviceA.getName());
        }

        public String getName() {
            return name;
        }

        public ServiceA getServiceA() {
            return serviceA;
        }
    }

    // ========== 2. 循环依赖解决器 ==========
    
    static class CircularDependencyResolver {
        
        /**
         * 解决 A 和 B 的循环依赖
         * 核心思路:
         * 1. 用 Unsafe 创建空壳对象(跳过构造器)
         * 2. 通过反射注入依赖字段
         */
        public static Object[] resolveCircularDependency() throws Exception {
            // 第一步:创建空壳对象(不调用构造器)
            ServiceA serviceA = UnsafeUtil.allocateInstance(ServiceA.class);
            ServiceB serviceB = UnsafeUtil.allocateInstance(ServiceB.class);
            
            System.out.println("✓ 空壳对象创建完成(构造器未被调用)");
            System.out.println("  serviceA.name = " + serviceA.getName()); // null(零值)
            System.out.println("  serviceB.name = " + serviceB.getName()); // null(零值)

            // 第二步:通过反射注入依赖
            // 注入 serviceA.serviceB = serviceB
            Field fieldB = ServiceA.class.getDeclaredField("serviceB");
            fieldB.setAccessible(true);
            fieldB.set(serviceA, serviceB);

            // 注入 serviceB.serviceA = serviceA
            Field fieldA = ServiceB.class.getDeclaredField("serviceA");
            fieldA.setAccessible(true);
            fieldA.set(serviceB, serviceA);

            System.out.println("✓ 依赖注入完成");

            // 第三步:手动初始化字段(因为跳过了构造器)
            Field nameFieldA = ServiceA.class.getDeclaredField("name");
            nameFieldA.setAccessible(true);
            nameFieldA.set(serviceA, "ServiceA");

            Field nameFieldB = ServiceB.class.getDeclaredField("name");
            nameFieldB.setAccessible(true);
            nameFieldB.set(serviceB, "ServiceB");

            System.out.println("✓ 字段初始化完成");
            System.out.println("  serviceA.name = " + serviceA.getName());
            System.out.println("  serviceB.name = " + serviceB.getName());

            return new Object[]{serviceA, serviceB};
        }
    }

    // ========== 3. 演示 ==========
    
    public static void main(String[] args) throws Exception {
        System.out.println("========== 循环依赖问题演示 ==========\n");

        // 传统方式:无法解决循环依赖
        System.out.println("【传统方式】");
        System.out.println("new ServiceA(new ServiceB(new ServiceA(...))) - 无法完成!\n");

        // Unsafe 方式:成功解决
        System.out.println("【Unsafe 方式】");
        Object[] result = CircularDependencyResolver.resolveCircularDependency();
        ServiceA serviceA = (ServiceA) result[0];
        ServiceB serviceB = (ServiceB) result[1];

        System.out.println("\n========== 验证循环依赖已解决 ==========");
        
        // 验证循环引用
        System.out.println("serviceA.getServiceB() == serviceB: " + (serviceA.getServiceB() == serviceB));
        System.out.println("serviceB.getServiceA() == serviceA: " + (serviceB.getServiceA() == serviceA));

        // 验证方法调用
        System.out.println("\n========== 验证方法调用 ==========");
        serviceA.doSomething(); // ServiceA 调用 ServiceB
        serviceB.doSomething(); // ServiceB 调用 ServiceA

        System.out.println("\n========== 对比:构造器调用情况 ==========");
        System.out.println("注意:构造器从未被调用!");
        System.out.println("这就是 Unsafe.allocateInstance() 的威力:");
        System.out.println("- 跳过构造器创建对象");
        System.out.println("- 允许后续手动注入依赖");
        System.out.println("- 从而打破循环依赖的死锁");
    }
}
# UnSafe 包说明文档

## 概述

本包演示了 Java 中 `sun.misc.Unsafe` 的使用方式,包括绕过构造器创建对象、解决循环依赖等高级场景。这些技术是 Spring、Netty 等主流框架底层实现的核心原理。

## 文件结构

```
UnSafe/
├── UnsafeUtil.java              # Unsafe 工具类(核心)
├── YourFrameworkClass.java      # 基础示例:跳过构造器创建对象
├── CircularDependencyDemo.java  # 进阶示例:解决循环依赖
└── README.md                    # 本文档
```

## 核心类说明

### 1. UnsafeUtil — Unsafe 工具类

**作用:** 通过纯反射方式获取 `sun.misc.Unsafe` 实例,兼容 JDK 8 ~ JDK 21+。

**实现原理:**

```
Class.forName("sun.misc.Unsafe")  →  获取 theUnsafe 字段  →  缓存 allocateInstance 方法
```

- 不直接 import `sun.misc.Unsafe`,避免编译期模块访问限制
- 通过 `Class.forName()` 动态加载,绕过 JDK 9+ 的模块化封装
- 缓存 `allocateInstance` 方法,避免重复反射开销

**核心 API:**

| 方法 | 说明 |
|------|------|
| `getUnsafe()` | 返回 Unsafe 实例(Object 类型) |
| `allocateInstance(Class<T>)` | 跳过构造器创建对象实例 |

### 2. YourFrameworkClass — 基础示例

**作用:** 演示 `allocateInstance()` 与 `new` 创建对象的区别。

**关键对比:**

| 特性 | `new MyClass()` | `UnsafeUtil.allocateInstance(MyClass.class)` |
|------|-----------------|----------------------------------------------|
| 构造器调用 | 是 | 否 |
| 字段初始化 | 执行字段初始化器 | 全部为零值(null/0/false) |
| 示例中 value 值 | 20 | 0 |

### 3. CircularDependencyDemo — 循环依赖解决示例

**作用:** 演示使用 Unsafe 解决 A ↔ B 循环依赖问题。

**问题场景:**

```
ServiceA 构造器需要 ServiceB
ServiceB 构造器需要 ServiceA
→ new ServiceA(new ServiceB(new ServiceA(...)))  无限递归,无法完成
```

**解决步骤:**

```
第一步:allocateInstance() 创建空壳对象(跳过构造器)
    ↓
第二步:反射注入依赖字段(建立对象间引用)
    ↓
第三步:手动初始化其他字段值
```

**流程图:**

```
┌─────────────────────────────────────────────────┐
│  ServiceA a = allocateInstance(ServiceA.class)  │  ← 空壳,字段全为 null
│  ServiceB b = allocateInstance(ServiceB.class)  │  ← 空壳,字段全为 null
├─────────────────────────────────────────────────┤
│  a.serviceB = b    (反射注入)                    │  ← 建立 A → B 引用
│  b.serviceA = a    (反射注入)                    │  ← 建立 B → A 引用
├─────────────────────────────────────────────────┤
│  a.name = "ServiceA"  (手动初始化)               │
│  b.name = "ServiceB"  (手动初始化)               │
├─────────────────────────────────────────────────┤
│  循环依赖解决完成 ✓                               │
│  a.serviceB == b  ✓                              │
│  b.serviceA == a  ✓                              │
└─────────────────────────────────────────────────┘
```

## 与 Spring 框架的关系

Spring 解决循环依赖的核心机制与本示例一致:

| 本示例 | Spring 实现 |
|--------|------------|
| `allocateInstance()` 创建空壳 | `Objenesis` 创建早期引用 |
| 反射注入依赖字段 | 属性注入(`populateBean`) |
| 手动初始化字段 | `initializeBean`(调用 `@PostConstruct` 等) |

Spring 使用**三级缓存** + `Objenesis`(底层即 `Unsafe.allocateInstance()`)来处理 Bean 的循环依赖。

## 注意事项

1. **字段零值问题:** `allocateInstance()` 创建的对象所有字段为零值,不会执行字段初始化器和构造器逻辑
2. **安全风险:** Unsafe 操作绕过 Java 访问控制,使用不当可能导致 JVM 崩溃
3. **生产建议:** 生产环境应使用 Spring 等成熟框架,不建议直接操作 Unsafe
4. **JDK 兼容性:** 本实现通过纯反射方式获取 Unsafe,兼容 JDK 8 至 JDK 21+

## 运行方式

```bash
# 编译
mvn compile

# 运行基础示例
mvn exec:java -Dexec.mainClass="UnSafe.YourFrameworkClass"

# 运行循环依赖示例
mvn exec:java -Dexec.mainClass="UnSafe.CircularDependencyDemo"
```
View Code

 

posted @ 2026-07-06 09:23  甜菜波波  阅读(6)  评论(0)    收藏  举报