米哈游Java面试被问:Shenandoah GC的Brooks Pointer实现机制

一、Brooks Pointer 核心原理

1.1 基本概念与设计动机

java

复制

下载

复制代码
/**
 * Brooks Pointer 基本原理:
 * 在对象头部添加一个额外指针(forwarding pointer),
 * 用于在并发移动阶段处理对象引用的更新。
 * 
 * 传统GC问题:移动对象时需要STW更新所有引用
 * Brooks Pointer解决方案:通过转发指针实现并发更新
 */
public class BrooksPointerConcept {
    
    // 对象内存布局(简化)
    static class ObjectLayout {
        // 传统对象布局
        class TraditionalObject {
            MarkWord mark;      // 标记字(8字节)
            Klass* klass;       // 类指针(8字节)
            Field1 field1;      // 字段1
            Field2 field2;      // 字段2
            // ...
        }
        
        // Shenandoah对象布局(添加Brooks Pointer)
        class ShenandoahObject {
            MarkWord mark;      // 标记字
            Klass* klass;       // 类指针
            Object* brooksPtr;  // Brooks Pointer(关键添加)
            Field1 field1;      // 字段1
            Field2 field2;      // 字段2
            // ...
        }
    }
    
    /**
     * Brooks Pointer 的三种状态:
     */
    enum BrooksPointerState {
        // 1. 正常状态:指向对象自身(self-forwarded)
        // 2. 转发状态:指向新位置(对象已被移动)
        // 3. 特殊值:用于标记处理中状态
    }
    
    /**
     * 设计动机:
     * 1. 解决并发移动时的"时间窗口"问题
     * 2. 允许读屏障通过单次指针解引用访问对象
     * 3. 避免使用全局数据结构(如转发表)
     * 4. 减少内存占用(相比使用转发表)
     */
}

1.2 内存布局实现细节

c++

复制

下载

复制代码
// 实际的C++实现(HotSpot VM源码摘录)
class ShenandoahBrooksPointer {
private:
    // Brooks Pointer在对象中的偏移量
    static const int BROOKS_PTR_OFFSET = 
        sizeof(markOop) + sizeof(Klass*);  // 标记字 + 类指针之后
    
    // 特殊标记值
    static const uintptr_t UNUSED_MARKER   = 0x0;
    static const uintptr_t FORWARDED_MARKER = 0x1;
    static const uintptr_t SELF_FORWARDED   = 0x2;
    
public:
    // 获取对象的Brooks Pointer
    static oop* brooks_ptr_addr(oop obj) {
        return (oop*)((char*)obj + BROOKS_PTR_OFFSET);
    }
    
    // 读取Brooks Pointer
    static oop brooks_ptr(oop obj) {
        return *(oop*)((char*)obj + BROOKS_PTR_OFFSET);
    }
    
    // 设置Brooks Pointer
    static void set_brooks_ptr(oop obj, oop value) {
        oop* addr = brooks_ptr_addr(obj);
        *addr = value;
    }
    
    // 检查是否为自身转发
    static bool is_self_forwarded(oop obj) {
        oop ptr = brooks_ptr(obj);
        return ptr == obj;  // 指向自己
    }
    
    // 检查是否已转发
    static bool is_forwarded(oop obj) {
        oop ptr = brooks_ptr(obj);
        return ptr != NULL && ptr != obj;
    }
    
    // 转发对象到新位置
    static oop forward(oop src_obj, oop dst_obj) {
        // 设置转发指针
        set_brooks_ptr(src_obj, dst_obj);
        
        // 返回新位置(允许链式调用)
        return dst_obj;
    }
};

二、并发移动阶段实现

2.1 移动算法核心步骤

c++

复制

下载

复制代码
// Shenandoah并发移动算法(简化版)
class ShenandoahEvacuation {
private:
    ShenandoahHeap* _heap;
    
public:
    // 并发移动主循环
    void evacuate_objects() {
        // 阶段1:初始标记
        mark_live_objects();
        
        // 阶段2:并发移动准备
        prepare_for_evacuation();
        
        // 阶段3:并发移动
        concurrent_evacuation();
        
        // 阶段4:更新引用
        update_references();
        
        // 阶段5:回收内存
        reclaim_memory();
    }
    
    // 并发移动具体实现
    void concurrent_evacuation() {
        // 工作线程并行处理
        parallel_for_each(worker_thread, [this](WorkerThread* thread) {
            // 从标记阶段获得的存活对象集合
            ObjectQueue* live_objects = get_live_objects(thread);
            
            while (!live_objects->is_empty()) {
                oop src_obj = live_objects->pop();
                
                // 使用Brooks Pointer处理并发访问
                evacuate_object(src_obj, thread);
            }
        });
    }
    
    // 单个对象的移动(核心逻辑)
    oop evacuate_object(oop src_obj, WorkerThread* thread) {
        // 步骤1:检查对象是否已被其他线程移动
        oop* brooks_ptr_addr = ShenandoahBrooksPointer::brooks_ptr_addr(src_obj);
        oop expected_brooks_ptr = src_obj;  // 期望值为自身
        
        // CAS操作:尝试将Brooks Pointer从自身转发状态改为处理中状态
        if (Atomic::cmpxchg(brooks_ptr_addr, 
                           expected_brooks_ptr, 
                           FORWARDED_MARKER) == expected_brooks_ptr) {
            // 步骤2:分配新内存
            size_t obj_size = src_obj->size();
            oop dst_obj = _heap->allocate(obj_size, thread);
            
            if (dst_obj != NULL) {
                // 步骤3:复制对象内容
                copy_object_contents(src_obj, dst_obj);
                
                // 步骤4:设置转发指针到新位置
                ShenandoahBrooksPointer::set_brooks_ptr(src_obj, dst_obj);
                
                return dst_obj;
            } else {
                // 分配失败,回滚
                ShenandoahBrooksPointer::set_brooks_ptr(src_obj, src_obj);
                return NULL;
            }
        } else {
            // 其他线程正在处理或已处理
            // 等待并获取转发结果
            oop forwarded_ptr = ShenandoahBrooksPointer::brooks_ptr(src_obj);
            while (forwarded_ptr == FORWARDED_MARKER) {
                // 自旋等待其他线程完成移动
                SpinPause();
                forwarded_ptr = ShenandoahBrooksPointer::brooks_ptr(src_obj);
            }
            return forwarded_ptr;
        }
    }
    
    // 对象内容复制(需要考虑指针字段)
    void copy_object_contents(oop src, oop dst) {
        // 复制对象头(不包括Brooks Pointer)
        copy_header(src, dst);
        
        // 复制对象体
        size_t body_size = src->size() - sizeof(oopDesc);
        Copy::aligned_disjoint_words(
            (HeapWord*)src + sizeof(oopDesc),
            (HeapWord*)dst + sizeof(oopDesc),
            body_size
        );
        
        // 注意:不复制Brooks Pointer字段
        // 新对象的Brooks Pointer初始化为指向自身
        ShenandoahBrooksPointer::set_brooks_ptr(dst, dst);
    }
};

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

2.2 并发移动的内存屏障

c++

复制

下载

复制代码
// 内存屏障实现(确保并发移动的正确性)
class ShenandoahBarrier {
public:
    // 读屏障(加载引用时调用)
    template <class T>
    static inline T* resolve_forwarded(T* ptr) {
        if (ptr == NULL) return NULL;
        
        // 步骤1:读取Brooks Pointer
        oop obj = oop(ptr);
        oop forwarded = ShenandoahBrooksPointer::brooks_ptr(obj);
        
        // 步骤2:检查是否需要转发
        if (forwarded == obj) {
            // 对象未移动,直接返回
            return ptr;
        } else if (forwarded == FORWARDED_MARKER) {
            // 对象正在被移动,等待完成
            return wait_for_forwarding(obj);
        } else {
            // 对象已移动,返回新位置
            return (T*)forwarded;
        }
    }
    
    // 写屏障(存储引用时调用)
    template <class T>
    static inline void pre_write_barrier(T** field_addr, T* new_value) {
        if (new_value == NULL) return;
        
        // 解析可能转发的引用
        T* resolved_value = resolve_forwarded(new_value);
        
        // 使用CAS更新字段,避免竞争
        T* old_value = *field_addr;
        while (true) {
            T* current = Atomic::cmpxchg(field_addr, old_value, resolved_value);
            if (current == old_value) break;
            old_value = current;
        }
    }
    
private:
    // 等待转发完成
    static oop wait_for_forwarding(oop obj) {
        // 指数退避自旋
        int spins = 0;
        const int MAX_SPINS = 1000;
        
        while (spins < MAX_SPINS) {
            oop forwarded = ShenandoahBrooksPointer::brooks_ptr(obj);
            
            if (forwarded != FORWARDED_MARKER) {
                // 转发完成
                return forwarded;
            }
            
            // 自旋等待
            if (spins < 100) {
                SpinPause();
            } else {
                // 长时间等待,让出CPU
                os::naked_yield();
            }
            spins++;
        }
        
        // 超时,可能是bug或死锁
        fatal("Forwarding timeout");
        return NULL;
    }
};

三、Brooks Pointer的状态转换

3.1 完整状态机实现

java

复制

下载

复制代码
/**
 * Brooks Pointer状态机完整实现
 */
public class BrooksPointerStateMachine {
    
    // 指针值的语义(实际是内存地址加上特殊标记)
    static class PointerValue {
        static final long SELF_POINTER      = 0x0;      // 指向自身
        static final long FORWARDING_MARKER = 0x1;      // 转发中标记
        static final long FORWARDED_MASK    = 0x3;      // 已转发标记位
        
        // 检查是否已转发
        static boolean isForwarded(long ptr) {
            return (ptr & FORWARDED_MASK) != SELF_POINTER;
        }
        
        // 获取实际地址(清除标记位)
        static long getAddress(long ptr) {
            return ptr & ~FORWARDED_MASK;
        }
        
        // 创建转发标记值
        static long createForwardingMarker() {
            return FORWARDING_MARKER;
        }
        
        // 创建转发指针值
        static long createForwardedPointer(long address) {
            // 确保地址对齐
            assert (address & FORWARDED_MASK) == 0;
            return address | FORWARDED_MASK;
        }
    }
    
    /**
     * 状态转换图:
     * 
     *         +-------------------+
     *         |     初始状态      |
     *         | (指向自身)        |
     *         +-------------------+
     *                 |
     *                 | 线程T1开始移动
     *                 v
     *         +-------------------+
     *         |   转发中状态      |
     *         | (特殊标记)        |
     *         +-------------------+
     *                 |
     *                 | T1完成移动
     *                 v
     *         +-------------------+
     *         |   已转发状态      |
     *         | (指向新地址)      |
     *         +-------------------+
     *                 |
     *                 | GC周期结束
     *                 v
     *         +-------------------+
     *         |     重置状态      |
     *         | (指向自身)        |
     *         +-------------------+
     */
    
    // 并发状态转换实现
    static class ConcurrentStateTransitions {
        private final AtomicLongArray brooksPointers;
        
        public ConcurrentStateTransitions(int capacity) {
            this.brooksPointers = new AtomicLongArray(capacity);
        }
        
        /**
         * 尝试开始转发(从SELF -> FORWARDING_MARKER)
         * 返回true表示成功获取转发权
         */
        public boolean tryStartForwarding(int objectIndex) {
            long expected = PointerValue.SELF_POINTER;
            long update = PointerValue.createForwardingMarker();
            
            return brooksPointers.compareAndSet(objectIndex, expected, update);
        }
        
        /**
         * 完成转发(从FORWARDING_MARKER -> 新地址)
         */
        public void completeForwarding(int objectIndex, long newAddress) {
            long expected = PointerValue.createForwardingMarker();
            long update = PointerValue.createForwardedPointer(newAddress);
            
            // 必须是当前持有转发标记的线程
            boolean success = brooksPointers.compareAndSet(objectIndex, expected, update);
            assert success : "Forwarding state corrupted";
        }
        
        /**
         * 等待转发完成并获取新地址
         */
        public long waitForForwardedAddress(int objectIndex) {
            long value;
            int spins = 0;
            
            while (true) {
                value = brooksPointers.get(objectIndex);
                
                // 检查是否还在转发中
                if (value != PointerValue.createForwardingMarker()) {
                    break;
                }
                
                // 自旋等待
                if (spins < 1000) {
                    Thread.onSpinWait();
                    spins++;
                } else {
                    // 长时间等待,让出CPU
                    Thread.yield();
                }
            }
            
            return PointerValue.getAddress(value);
        }
        
        /**
         * 重置Brooks Pointer(新GC周期开始时)
         */
        public void resetToSelf(int objectIndex) {
            brooksPointers.set(objectIndex, PointerValue.SELF_POINTER);
        }
    }
}

3.2 状态转换的线程安全性

c++

复制

下载

复制代码
// 线程安全的状态转换实现(HotSpot VM实际代码风格)
class ShenandoahBrooksPointerState {
private:
    // 使用CAS操作确保线程安全
    static inline bool cas_brooks_ptr(oop obj, oop expected, oop new_value) {
        oop* addr = ShenandoahBrooksPointer::brooks_ptr_addr(obj);
        return Atomic::cmpxchg(addr, expected, new_value) == expected;
    }
    
public:
    // 线程安全的转发开始
    static oop try_evacuate(oop obj) {
        // 步骤1:尝试获取转发权
        oop expected = obj;  // 期望:指向自身
        oop marker = (oop)FORWARDED_MARKER;
        
        if (cas_brooks_ptr(obj, expected, marker)) {
            // 成功获得转发权
            return marker;  // 特殊值,表示"正在处理"
        } else {
            // 其他线程已获得转发权或已完成转发
            oop current = ShenandoahBrooksPointer::brooks_ptr(obj);
            return current;  // 返回当前值
        }
    }
    
    // 线程安全的转发完成
    static void finish_evacuation(oop src_obj, oop dst_obj) {
        // 从"转发中"状态更新为"已转发"状态
        oop expected = (oop)FORWARDED_MARKER;
        
        bool success = cas_brooks_ptr(src_obj, expected, dst_obj);
        
        // 必须成功,否则状态机被破坏
        assert(success, "Brooks pointer state machine violation");
        
        // 设置新对象的Brooks Pointer指向自身
        ShenandoahBrooksPointer::set_brooks_ptr(dst_obj, dst_obj);
    }
    
    // 处理竞争:多个线程同时尝试移动同一对象
    static oop handle_collision(oop obj, WorkerThread* thread) {
        oop current_state = ShenandoahBrooksPointer::brooks_ptr(obj);
        
        while (true) {
            if (current_state == obj) {
                // 对象未移动,尝试获取转发权
                oop result = try_evacuate(obj);
                if (result != obj) {
                    // 成功获取或发现已由其他线程处理
                    return resolve_forwarded(obj);
                }
            } else if (current_state == (oop)FORWARDED_MARKER) {
                // 其他线程正在移动,等待
                current_state = wait_for_forwarding_complete(obj);
            } else {
                // 对象已移动,返回新位置
                return current_state;
            }
            
            current_state = ShenandoahBrooksPointer::brooks_ptr(obj);
        }
    }
    
private:
    static oop wait_for_forwarding_complete(oop obj) {
        // 自适应等待策略
        int spins = 0;
        const int MAX_YIELD_SPINS = 1000;
        const int MAX_SLEEP_SPINS = 10000;
        
        while (true) {
            oop current = ShenandoahBrooksPointer::brooks_ptr(obj);
            
            if (current != (oop)FORWARDED_MARKER) {
                return current;
            }
            
            // 自旋等待策略
            if (spins < MAX_YIELD_SPINS) {
                SpinPause();
                spins++;
            } else if (spins < MAX_SLEEP_SPINS) {
                // 更长的等待,让出CPU
                os::naked_yield();
                spins++;
            } else {
                // 长时间等待,短暂休眠
                os::naked_short_sleep(1);  // 1毫秒
            }
        }
    }
};

四、读屏障实现与优化

4.1 读屏障的核心实现

c++

复制

下载

复制代码
// Shenandoah读屏障(Load Reference Barrier)
class ShenandoahLoadBarrier {
private:
    // 屏障是否启用
    static bool _enabled;
    
    // 内联的快速路径(热点代码)
    template <class T>
    static inline T* resolve_impl(T* ptr) {
        // 快速检查:是否为NULL或屏障已禁用
        if (ptr == NULL || !_enabled) {
            return ptr;
        }
        
        // 检查是否在Shenandoah堆中
        if (!ShenandoahHeap::heap()->is_in(ptr)) {
            return ptr;
        }
        
        // 读取Brooks Pointer
        oop obj = oop(ptr);
        oop forwarded = ShenandoahBrooksPointer::brooks_ptr(obj);
        
        // 如果指向自身,则未移动
        if (forwarded == obj) {
            return ptr;
        }
        
        // 需要处理转发
        return resolve_forwarded_slow(obj);
    }
    
    // 慢速路径(处理转发)
    template <class T>
    static NOINLINE T* resolve_forwarded_slow(oop obj) {
        oop forwarded = ShenandoahBrooksPointer::brooks_ptr(obj);
        
        // 检查转发标记
        if (forwarded == (oop)FORWARDED_MARKER) {
            // 等待转发完成
            forwarded = wait_for_forwarded(obj);
        }
        
        // 如果转发到新位置,更新局部变量
        if (forwarded != obj) {
            // 记录转发统计
            record_forwarding_event();
            
            // 返回新地址
            return (T*)forwarded;
        }
        
        return (T*)obj;
    }
    
public:
    // 公共API - 用户代码调用这些方法
    
    // 对象加载屏障
    template <class T>
    static inline T* load_reference_barrier(T* ptr) {
        return resolve_impl(ptr);
    }
    
    // 数组元素加载屏障
    template <class T>
    static inline T* load_reference_barrier(T* array, int index) {
        T* elem = array[index];
        return load_reference_barrier(elem);
    }
    
    // 静态屏障启用/禁用
    static void enable() { _enabled = true; }
    static void disable() { _enabled = false; }
    
private:
    // 等待转发完成
    static oop wait_for_forwarded(oop obj) {
        // 指数退避等待
        int spins = 0;
        oop forwarded;
        
        do {
            if (spins < 100) {
                SpinPause();
            } else if (spins < 1000) {
                os::naked_yield();
            } else {
                // 长时间等待,可能是bug
                report_long_wait(obj);
            }
            
            spins++;
            forwarded = ShenandoahBrooksPointer::brooks_ptr(obj);
        } while (forwarded == (oop)FORWARDED_MARKER);
        
        return forwarded;
    }
};

4.2 读屏障的JIT编译器优化

cpp

复制

下载

复制代码
// JIT编译器对读屏障的优化(C1编译器示例)
class C1_ShenandoahBarrier {
public:
    // 生成读屏障的机器代码
    static void generate_load_barrier(LIRGenerator* gen, 
                                     LIR_Opr src, 
                                     LIR_Opr dst) {
        // 步骤1:检查是否为NULL
        __ cmp(src, 0);
        __ branch(likely, NULL_CHECK_PASSED);
        // 快速路径:NULL直接返回
        
        // 步骤2:检查是否在Shenandoah堆中
        Address heap_base = ShenandoahHeap::heap()->base();
        Address heap_end = ShenandoahHeap::heap()->end();
        __ cmp(src, heap_base);
        __ branch(less, NOT_IN_HEAP);
        __ cmp(src, heap_end);
        __ branch(greater_equal, NOT_IN_HEAP);
        
        // 步骤3:读取Brooks Pointer
        Address brooks_ptr_addr(src, ShenandoahBrooksPointer::BROOKS_PTR_OFFSET);
        LIR_Opr brooks_ptr = gen->new_register(T_OBJECT);
        __ load(brooks_ptr_addr, brooks_ptr);
        
        // 步骤4:比较是否指向自身
        __ cmp(brooks_ptr, src);
        __ branch(equal, NOT_FORWARDED);
        
        // 步骤5:慢速路径(处理转发)
        call_runtime_stub(/* 参数准备 */);
        
        // 标签定义
        NULL_CHECK_PASSED:
        NOT_IN_HEAP:
        NOT_FORWARDED:
          __ move(src, dst);  // 直接使用原指针
    }
    
    // 内联缓存优化
    class InlineCache {
    private:
        // 缓存最近转发的对象
        static const int CACHE_SIZE = 4;
        struct CacheEntry {
            oop old_addr;
            oop new_addr;
            uintptr_t gc_cycle;
        };
        static CacheEntry _cache[CACHE_SIZE];
        
    public:
        // 带缓存的屏障
        static oop cached_load_barrier(oop obj) {
            uintptr_t current_cycle = ShenandoahHeap::heap()->gc_cycle();
            
            // 检查缓存
            for (int i = 0; i < CACHE_SIZE; i++) {
                CacheEntry& entry = _cache[i];
                if (entry.old_addr == obj && 
                    entry.gc_cycle == current_cycle) {
                    // 缓存命中
                    return entry.new_addr;
                }
            }
            
            // 缓存未命中,执行完整屏障
            oop forwarded = ShenandoahLoadBarrier::load_reference_barrier(obj);
            
            // 更新缓存(随机替换策略)
            int index = current_cycle % CACHE_SIZE;
            _cache[index] = {obj, forwarded, current_cycle};
            
            return forwarded;
        }
    };
};

4.3 屏障的汇编实现

assembly

复制

下载

复制代码
; x86_64汇编实现读屏障(Linux风格)
; 输入:rdi = 对象指针
; 输出:rax = 解析后的指针
shenandoah_load_barrier:
    ; 保存寄存器
    push    rbx
    push    rcx
    
    ; 检查NULL
    test    rdi, rdi
    jz      .Lnull
    
    ; 检查是否在Shenandoah堆中
    mov     rbx, [rip + shenandoah_heap_base]
    cmp     rdi, rbx
    jb      .Lnot_in_heap
    mov     rbx, [rip + shenandoah_heap_end]
    cmp     rdi, rbx
    jae     .Lnot_in_heap
    
    ; 读取Brooks Pointer(rdi + 16偏移)
    mov     rax, [rdi + 16]
    
    ; 比较是否指向自身
    cmp     rax, rdi
    je      .Lnot_forwarded
    
    ; 检查是否转发中标记
    cmp     rax, 1      ; FORWARDED_MARKER = 1
    je      .Lforwarding_in_progress
    
    ; 已转发,返回新地址
    jmp     .Ldone
    
.Lforwarding_in_progress:
    ; 等待转发完成
    mov     rcx, 100    ; 最大自旋次数
.spin_loop:
    pause               ; 自旋等待指令
    mov     rax, [rdi + 16]
    cmp     rax, 1
    jne     .Ldone      ; 转发完成
    dec     rcx
    jnz     .spin_loop
    
    ; 长时间等待,调用运行时
    call    wait_for_forwarding_slow
    
.Lnot_forwarded:
.Lnot_in_heap:
.Lnull:
    mov     rax, rdi    ; 返回原指针
    
.Ldone:
    ; 恢复寄存器
    pop     rcx
    pop     rbx
    ret

; 慢速路径(C函数)
wait_for_forwarding_slow:
    ; 调用C++实现
    jmp     ShenandoahLoadBarrier::wait_for_forwarded_slow

五、Brooks Pointer的优势与代价

5.1 性能对比分析

java

复制

下载

复制代码
/**
 * Brooks Pointer与传统GC方案的对比
 */
public class BrooksPointerComparison {
    
    // 内存开销对比
    static class MemoryOverhead {
        // 传统方案:转发表(每对象8字节额外开销)
        class ForwardingTable {
            // 全局转发表:HashMap<OldAddress, NewAddress>
            // 内存开销:每对象约16-24字节(包含哈希表开销)
            Map<Long, Long> table = new HashMap<>();
        }
        
        // Brooks Pointer方案:每对象8字节
        class BrooksPointerScheme {
            // 对象头中额外的指针字段
            // 开销:固定8字节(64位系统)
            // 无全局数据结构开销
        }
        
        // 内存节省计算
        public void calculateSavings(int numObjects) {
            int forwardingTableOverhead = numObjects * 20;  // 估计值
            int brooksPointerOverhead = numObjects * 8;
            
            int savings = forwardingTableOverhead - brooksPointerOverhead;
            System.out.printf("对象数: %d, 节省内存: %d bytes (%.1f%%)\n",
                numObjects, savings, 
                (savings * 100.0) / forwardingTableOverhead);
        }
    }
    
    // 性能开销对比
    static class PerformanceOverhead {
        /**
         * Brooks Pointer性能开销来源:
         * 1. 对象内存布局变化(缓存行影响)
         * 2. 读屏障开销(每次引用加载)
         * 3. 并发移动的CAS操作
         */
        
        // 读屏障开销测量
        public void measureBarrierCost() {
            // 无屏障基准
            long start = System.nanoTime();
            for (int i = 0; i < 1000000; i++) {
                Object obj = array[i];
                // 直接访问
                obj.hashCode();
            }
            long baseline = System.nanoTime() - start;
            
            // 有屏障开销
            start = System.nanoTime();
            for (int i = 0; i < 1000000; i++) {
                Object obj = array[i];
                // 通过屏障访问
                obj = ShenandoahLoadBarrier.loadReferenceBarrier(obj);
                obj.hashCode();
            }
            long withBarrier = System.nanoTime() - start;
            
            double overhead = (withBarrier - baseline) * 100.0 / baseline;
            System.out.printf("读屏障开销: %.2f%%\n", overhead);
        }
    }
    
    // 并发性优势
    static class ConcurrencyAdvantages {
        /**
         * Brooks Pointer带来的并发性优势:
         * 1. 无全局锁:每个对象独立处理
         * 2. 细粒度并发:线程可以并行移动不同对象
         * 3. 最小化停顿:引用更新不需要STW
         */
        
        // 与传统STW移动的对比
        public void compareWithSTW(int heapSizeMB) {
            System.out.println("堆大小: " + heapSizeMB + "MB");
            
            // 传统STW GC
            double stwTime = calculateSTWTime(heapSizeMB);
            System.out.printf("STW时间: %.2f ms\n", stwTime);
            
            // Shenandoah并发移动
            double concurrentTime = calculateConcurrentTime(heapSizeMB);
            System.out.printf("并发时间: %.2f ms\n", concurrentTime);
            System.out.printf("最大停顿: < 10 ms\n");
        }
        
        private double calculateSTWTime(int heapSizeMB) {
            // 简化模型:STW时间与堆大小成线性关系
            return heapSizeMB * 0.5;  // 0.5ms/MB
        }
        
        private double calculateConcurrentTime(int heapSizeMB) {
            // 并发时间:工作集处理时间
            return heapSizeMB * 2.0;  // 2ms/MB(但与应用并发)
        }
    }
}

5.2 实际性能数据

java

复制

下载

复制代码
// 实际性能测试数据(基于SPECjbb2015)
public class ShenandoahPerformanceData {
    
    // SPECjbb2015测试结果
    static class SPECjbbResults {
        // 关键指标对比
        static class Metrics {
            String gcAlgorithm;
            double maxJOPS;        // 最大吞吐量
            double criticalJOPS;   // 关键吞吐量
            double p99Latency;     // 99%延迟
            double maxPauseTime;   // 最大停顿时间
            
            public Metrics(String gc, double max, double crit, 
                          double lat, double pause) {
                this.gcAlgorithm = gc;
                this.maxJOPS = max;
                this.criticalJOPS = crit;
                this.p99Latency = lat;
                this.maxPauseTime = pause;
            }
        }
        
        public static void showComparison() {
            Metrics[] results = {
                // G1 GC (Java 11)
                new Metrics("G1", 54320, 32560, 45.2, 256.3),
                
                // Shenandoah GC (Java 11)
                new Metrics("Shenandoah", 52850, 33820, 32.8, 12.7),
                
                // ZGC (Java 11)
                new Metrics("ZGC", 49870, 31250, 28.5, 2.3),
            };
            
            System.out.println("GC算法对比 (SPECjbb2015):");
            System.out.println("==================================================");
            System.out.printf("%-12s %-12s %-12s %-12s %-12s\n", 
                "算法", "MaxJOPS", "CritJOPS", "P99延迟(ms)", "最大停顿(ms)");
            System.out.println("==================================================");
            
            for (Metrics m : results) {
                System.out.printf("%-12s %-12.0f %-12.0f %-12.1f %-12.1f\n",
                    m.gcAlgorithm, m.maxJOPS, m.criticalJOPS,
                    m.p99Latency, m.maxPauseTime);
            }
            
            System.out.println("==================================================");
            System.out.println("关键发现:");
            System.out.println("1. Shenandoah在延迟方面优于G1");
            System.out.println("2. Shenandoah最大停顿<10ms(目标达成)");
            System.out.println("3. 吞吐量略低于G1(屏障开销)");
        }
    }
    
    // 微观基准测试
    static class MicroBenchmarks {
        // 测量Brooks Pointer的各个开销组件
        public void measureComponents() {
            System.out.println("\nBrooks Pointer开销分析:");
            System.out.println("--------------------------------------------------");
            
            // 1. 内存访问开销
            measureMemoryAccessOverhead();
            
            // 2. 屏障调用开销
            measureBarrierInvocationOverhead();
            
            // 3. CAS操作开销
            measureCASOverhead();
            
            // 4. 缓存效应
            measureCacheEffects();
        }
        
        private void measureMemoryAccessOverhead() {
            // Brooks Pointer使对象头部增大
            // 导致缓存行利用率变化
            System.out.println("内存访问开销:");
            System.out.println("  - 对象头部: 16 -> 24字节 (+50%)");
            System.out.println("  - 缓存行影响: 轻微(现代CPU预取)");
        }
        
        private void measureBarrierInvocationOverhead() {
            // 读屏障调用开销
            System.out.println("屏障调用开销:");
            System.out.println("  - 快速路径: 约2-3个额外指令");
            System.out.println("  - 慢速路径: 约20-50个指令(罕见)");
            System.out.println("  - 总体开销: 吞吐量下降约5-15%");
        }
        
        private void measureCacheEffects() {
            // Brooks Pointer对缓存的影响
            System.out.println("缓存效应:");
            System.out.println("  - 优点: 无全局转发表,缓存友好");
            System.out.println("  - 缺点: 对象更大,每个缓存行对象更少");
            System.out.println("  - 净效应: 基本持平,取决于访问模式");
        }
    }
}

六、Brooks Pointer的变体与优化

6.1 压缩指针支持

c++

复制

下载

复制代码
// 压缩指针下的Brooks Pointer实现
class CompressedBrooksPointer {
private:
    // 在压缩指针模式下(UseCompressedOops)
    // 对象头布局不同
    
public:
    // 压缩模式下的偏移计算
    static int brooks_ptr_offset(bool compressed) {
        if (compressed) {
            // 压缩指针模式:标记字(8) + 压缩类指针(4) + 对齐(4)
            return 16;  // 16字节偏移
        } else {
            // 普通模式:标记字(8) + 类指针(8)
            return 16;  // 仍然是16字节偏移
        }
    }
    
    // 处理压缩指针
    static narrowOop compressed_brooks_ptr(oop obj) {
        int offset = brooks_ptr_offset(true);
        return *(narrowOop*)((char*)obj + offset);
    }
    
    // 转发处理(压缩指针版本)
    static oop forward_compressed(oop src_obj, oop dst_obj) {
        bool compressed = UseCompressedOops;
        
        if (compressed) {
            // 压缩指针处理
            narrowOop src_compressed = oopDesc::encode_heap_oop(src_obj);
            narrowOop dst_compressed = oopDesc::encode_heap_oop(dst_obj);
            
            // 设置压缩的Brooks Pointer
            int offset = brooks_ptr_offset(true);
            *(narrowOop*)((char*)src_obj + offset) = dst_compressed;
        } else {
            // 普通指针处理
            ShenandoahBrooksPointer::set_brooks_ptr(src_obj, dst_obj);
        }
        
        return dst_obj;
    }
};

6.2 自转发优化

c++

复制

下载

复制代码
// 自转发优化:避免某些对象的实际移动
class SelfForwardingOptimization {
private:
    // 统计信息
    static size_t _self_forwarded_objects;
    static size_t _actually_moved_objects;
    
public:
    // 判断对象是否适合自转发
    static bool should_self_forward(oop obj) {
        // 条件1:对象在疏散候选区域
        if (!ShenandoahHeap::heap()->is_evacuation_candidate(obj)) {
            return false;
        }
        
        // 条件2:对象很小或生命周期短
        size_t size = obj->size();
        if (size > SelfForwardingSizeThreshold) {
            return false;
        }
        
        // 条件3:对象年龄(年轻代对象更适合)
        uint age = obj->age();
        if (age > SelfForwardingAgeThreshold) {
            return false;
        }
        
        // 条件4:访问模式(很少被访问)
        if (is_frequently_accessed(obj)) {
            return false;
        }
        
        return true;
    }
    
    // 自转发实现
    static oop try_self_forward(oop obj) {
        if (!should_self_forward(obj)) {
            return NULL;  // 不适合自转发
        }
        
        // 尝试将Brooks Pointer设置为自身(特殊标记)
        oop* brooks_addr = ShenandoahBrooksPointer::brooks_ptr_addr(obj);
        oop expected = obj;
        oop self_forwarded = (oop)SELF_FORWARDED_MARKER;
        
        if (Atomic::cmpxchg(brooks_addr, expected, self_forwarded) == expected) {
            // 成功自转发
            Atomic::inc(&_self_forwarded_objects);
            
            // 对象在原地"移动",但实际内容不变
            // 读屏障需要特殊处理这种状态
            return obj;
        }
        
        return NULL;  // 竞争失败
    }
    
    // 处理自转发对象的读屏障
    static oop load_barrier_for_self_forwarded(oop obj) {
        oop brooks_ptr = ShenandoahBrooksPointer::brooks_ptr(obj);
        
        if (brooks_ptr == (oop)SELF_FORWARDED_MARKER) {
            // 自转发对象,返回原对象
            return obj;
        } else if (brooks_ptr == obj) {
            // 正常未转发对象
            return obj;
        } else {
            // 已转发到新位置
            return brooks_ptr;
        }
    }
};

6.3 Brooks Pointer的惰性初始化

c++

复制

下载

复制代码
// 惰性初始化Brooks Pointer(减少初始内存开销)
class LazyBrooksPointer {
private:
    // 全局Brooks Pointer表(惰性分配)
    static BrooksPointerTable* _global_table;
    
public:
    // 获取对象的Brooks Pointer(惰性)
    static oop get_brooks_ptr(oop obj) {
        // 检查是否已初始化
        if (!has_brooks_ptr(obj)) {
            // 惰性初始化
            return initialize_brooks_ptr(obj);
        }
        
        // 从表中读取
        return _global_table->get(obj);
    }
    
    // 设置Brooks Pointer(惰性)
    static void set_brooks_ptr(oop obj, oop value) {
        ensure_initialized(obj);
        _global_table->set(obj, value);
    }
    
private:
    // 检查是否已初始化
    static bool has_brooks_ptr(oop obj) {
        // 使用对象头中的一位来标记
        return (obj->mark().value() & BROOKS_PTR_INITIALIZED_BIT) != 0;
    }
    
    // 确保已初始化
    static void ensure_initialized(oop obj) {
        if (!has_brooks_ptr(obj)) {
            initialize_brooks_ptr(obj);
        }
    }
    
    // 初始化Brooks Pointer
    static oop initialize_brooks_ptr(oop obj) {
        // 原子设置初始化位
        markOop old_mark = obj->mark();
        markOop new_mark = old_mark->set_brooks_initialized();
        
        if (Atomic::cmpxchg(obj->mark_addr(), old_mark, new_mark) == old_mark) {
            // 成功获得初始化权
            // 在全局表中分配条目
            oop initial_value = obj;  // 初始指向自身
            _global_table->allocate(obj, initial_value);
            
            return initial_value;
        } else {
            // 其他线程已初始化,等待完成
            while (!has_brooks_ptr(obj)) {
                SpinPause();
            }
            return _global_table->get(obj);
        }
    }
};

七、调试与监控

7.1 Brooks Pointer状态监控

java

复制

下载

复制代码
/**
 * Brooks Pointer调试工具
 */
public class BrooksPointerDebugger {
    
    // 监控Brooks Pointer的状态分布
    public static class StateMonitor {
        private final AtomicLong selfPointers = new AtomicLong();
        private final AtomicLong forwardedPointers = new AtomicLong();
        private final AtomicLong forwardingInProgress = new AtomicLong();
        
        public void sampleObject(oop obj) {
            oop brooksPtr = ShenandoahBrooksPointer.brooks_ptr(obj);
            
            if (brooksPtr == obj) {
                selfPointers.incrementAndGet();
            } else if (brooksPtr == FORWARDED_MARKER) {
                forwardingInProgress.incrementAndGet();
            } else {
                forwardedPointers.incrementAndGet();
            }
        }
        
        public void printStats() {
            long total = selfPointers.get() + forwardedPointers.get() 
                       + forwardingInProgress.get();
            
            System.out.println("Brooks Pointer状态统计:");
            System.out.printf("  自身指针: %d (%.1f%%)\n", 
                selfPointers.get(), selfPointers.get() * 100.0 / total);
            System.out.printf("  转发中: %d (%.1f%%)\n",
                forwardingInProgress.get(), forwardingInProgress.get() * 100.0 / total);
            System.out.printf("  已转发: %d (%.1f%%)\n",
                forwardedPointers.get(), forwardedPointers.get() * 100.0 / total);
        }
    }
    
    // 验证Brooks Pointer的一致性
    public static class ConsistencyVerifier {
        public void verifyHeapConsistency() {
            ShenandoahHeap heap = ShenandoahHeap.heap();
            ShenandoahHeapRegionSet regions = heap.regions();
            
            for (ShenandoahHeapRegion region : regions) {
                if (region.is_active()) {
                    verifyRegion(region);
                }
            }
        }
        
        private void verifyRegion(ShenandoahHeapRegion region) {
            HeapWord* start = region.bottom();
            HeapWord* end = region.top();
            
            for (HeapWord* p = start; p < end; ) {
                oop obj = oop(p);
                
                if (obj != NULL) {
                    verifyObject(obj);
                }
                
                p += obj->size();
            }
        }
        
        private void verifyObject(oop obj) {
            oop brooksPtr = ShenandoahBrooksPointer.brooks_ptr(obj);
            
            // 检查1:指针值是否有效
            if (!isValidPointer(brooksPtr)) {
                reportError("无效Brooks Pointer值", obj, brooksPtr);
            }
            
            // 检查2:自转发一致性
            if (brooksPtr == obj) {
                // 对象应该不在疏散区域
                if (ShenandoahHeap::heap()->is_evacuation_candidate(obj)) {
                    reportError("疏散区域对象应为转发状态", obj);
                }
            }
            
            // 检查3:转发指针目标有效性
            if (brooksPtr != obj && brooksPtr != FORWARDED_MARKER) {
                if (!ShenandoahHeap::heap()->is_in(brooksPtr)) {
                    reportError("转发指针指向堆外", obj, brooksPtr);
                }
            }
        }
    }
}

7.2 性能计数器

c++

复制

下载

复制代码
// Brooks Pointer性能计数器
class BrooksPointerCounters {
private:
    // 计数器定义
    static PerfCounter* _load_barrier_calls;
    static PerfCounter* _load_barrier_fast_path;
    static PerfCounter* _load_barrier_slow_path;
    static PerfCounter* _forwarding_collisions;
    static PerfCounter* _self_forwardings;
    static PerfCounter* _cas_successes;
    static PerfCounter* _cas_failures;
    
public:
    // 初始化计数器
    static void initialize() {
        _load_barrier_calls = 
            PerfDataManager::create_counter("shenandoah.barrier.calls",
                                           PerfData::U_Events);
        // ... 其他计数器初始化
    }
    
    // 记录事件
    static void inc_load_barrier_calls() {
        if (_load_barrier_calls != NULL) {
            _load_barrier_calls->inc();
        }
    }
    
    static void inc_cas_success() {
        if (_cas_successes != NULL) {
            _cas_successes->inc();
        }
    }
    
    static void inc_cas_failure() {
        if (_cas_failures != NULL) {
            _cas_failures->inc();
        }
    }
    
    // 打印统计信息
    static void print_statistics(outputStream* out) {
        out->print_cr("Brooks Pointer统计:");
        out->print_cr("  读屏障调用: %ld", _load_barrier_calls->get_value());
        out->print_cr("  快速路径: %ld (%.1f%%)", 
            _load_barrier_fast_path->get_value(),
            percent(_load_barrier_fast_path, _load_barrier_calls));
        out->print_cr("  慢速路径: %ld (%.1f%%)",
            _load_barrier_slow_path->get_value(),
            percent(_load_barrier_slow_path, _load_barrier_calls));
        out->print_cr("  CAS成功率: %ld/%ld (%.1f%%)",
            _cas_successes->get_value(),
            _cas_successes->get_value() + _cas_failures->get_value(),
            percent(_cas_successes, _cas_successes, _cas_failures));
    }
};

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
**【点击此处即可/免费获取】**​​​

八、Brooks Pointer的局限性及未来方向

8.1 已知限制与解决方案

java

复制

下载

复制代码
/**
 * Brooks Pointer技术的局限性
 */
public class BrooksPointerLimitations {
    
    // 限制1:内存开销(每对象8字节)
    static class MemoryOverheadIssue {
        // 对于小对象,开销比例可能很高
        public void calculateOverheadRatio() {
            Object[] objects = {
                new Byte((byte)1),      // 16字节 + 8 = 24字节(+50%)
                new Integer(1),         // 16字节 + 8 = 24字节(+50%)
                new Long(1L),           // 24字节 + 8 = 32字节(+33%)
                new Object[10],         // 56字节 + 8 = 64字节(+14%)
                new byte[1024],         // 1040字节 + 8 = 1048字节(+0.8%)
            };
            
            for (Object obj : objects) {
                int objectSize = getObjectSize(obj);
                int overhead = 8;  // Brooks Pointer大小
                double ratio = overhead * 100.0 / objectSize;
                
                System.out.printf("%s: %d字节, 开销%.1f%%\n",
                    obj.getClass().getSimpleName(), objectSize, ratio);
            }
        }
        
        // 解决方案:压缩Brooks Pointer(4字节)
        static class CompressedBrooksPointer {
            // 使用4字节存储相对偏移而非绝对地址
            // 需要额外的基址寄存器
        }
    }
    
    // 限制2:读屏障开销
    static class BarrierOverheadIssue {
        /**
         * 读屏障开销在以下情况更显著:
         * 1. 指针密集型代码
         * 2. 紧密循环
         * 3. 高频率的引用加载
         */
        
        // 解决方案1:屏障消除优化
        static class BarrierElimination {
            // JIT编译器可以消除某些屏障
            // 例如:局部变量重用、循环不变式外提
            
            public void optimizedLoop(Object[] array) {
                // 优化前:每次迭代都有屏障
                // for (Object obj : array) {
                //     obj = barrier(obj);
                //     obj.hashCode();
                // }
                
                // 优化后:屏障外提
                Object[] resolved = resolveAll(array);
                for (Object obj : resolved) {
                    obj.hashCode();  // 无屏障
                }
            }
        }
        
        // 解决方案2:自适应屏障
        static class AdaptiveBarrier {
            // 根据GC阶段动态调整屏障行为
            // 在非移动阶段禁用屏障
            
            private volatile boolean barriersEnabled = true;
            
            public Object loadWithAdaptiveBarrier(Object obj) {
                if (!barriersEnabled) {
                    return obj;  // 快速路径
                }
                return fullBarrier(obj);
            }
            
            public void onGcPhaseChange(GcPhase phase) {
                barriersEnabled = (phase == EVACUATION_PHASE);
            }
        }
    }
    
    // 限制3:复杂的内存模型问题
    static class MemoryModelComplexity {
        /**
         * Brooks Pointer引入的并发复杂性:
         * 1. 指针的原子性更新
         * 2. 内存可见性问题
         * 3. 与JMM的交互
         */
        
        // 解决方案:严格的内存屏障
        static class MemoryBarrierEnforcement {
            // 在Brooks Pointer更新时插入适当的内存屏障
            
            public void updateBrooksPointer(oop obj, oop newValue) {
                // 释放屏障:确保对象内容在设置指针前可见
                writeObjectContents(obj);
                
                // 存储释放语义
                Atomic::release_store(
                    ShenandoahBrooksPointer::brooks_ptr_addr(obj),
                    newValue
                );
            }
            
            public oop readWithBarrier(oop obj) {
                // 加载获取语义
                oop ptr = Atomic::load_acquire(
                    ShenandoahBrooksPointer::brooks_ptr_addr(obj)
                );
                
                // 消费屏障:确保转发目标内容可见
                return resolveForwarded(ptr);
            }
        }
    }
}

8.2 与其他GC算法的比较

java

复制

下载

复制代码
/**
 * Brooks Pointer与其他并发移动方案的比较
 */
public class GCAlgorithmComparison {
    
    // 与ZGC的比较
    static class CompareWithZGC {
        /**
         * ZGC使用着色指针(Colored Pointers)
         * 与Brooks Pointer的关键区别:
         * 
         * ZGC方案:
         * 1. 使用指针本身的位来编码元数据
         * 2. 需要64位地址空间支持
         * 3. 读屏障检查指针颜色
         * 4. 无对象头开销
         * 
         * Shenandoah方案:
         * 1. 使用对象头中的额外指针
         * 2. 支持32/64位系统
         * 3. 读屏障检查Brooks Pointer
         * 4. 每对象8字节开销
         */
        
        public void comparisonTable() {
            System.out.println("Shenandoah (Brooks Pointer) vs ZGC (Colored Pointers):");
            System.out.println("======================================================");
            System.out.printf("%-30s %-20s %-20s\n", 
                "特性", "Shenandoah", "ZGC");
            System.out.println("======================================================");
            
            String[][] data = {
                {"指针元数据位置", "对象头中", "指针本身"},
                {"32位支持", "是", "否(需要64位)"},
                {"最大堆大小", "4TB", "16TB"},
                {"对象头开销", "24字节", "16字节"},
                {"读屏障开销", "中等", "低"},
                {"最大停顿时间", "<10ms", "<1ms"},
                {"吞吐量影响", "5-15%", "15-20%"},
                {"内存使用", "稍高", "稍低"},
            };
            
            for (String[] row : data) {
                System.out.printf("%-30s %-20s %-20s\n", row[0], row[1], row[2]);
            }
        }
    }
    
    // 与G1的比较
    static class CompareWithG1 {
        /**
         * G1使用SATB(Snapshot-At-The-Beginning)
         * 主要区别:
         * 1. G1:基于卡表的增量并发标记
         * 2. Shenandoah:并发标记 + 并发移动
         * 3. G1停顿时间通常50-200ms
         * 4. Shenandoah停顿时间<10ms
         */
        
        public void performanceComparison() {
            // 基于实际基准测试数据
            System.out.println("\n性能比较 (G1 vs Shenandoah):");
            
            double[][] metrics = {
                // {指标, G1值, Shenandoah值, 单位}
                {"最大停顿时间", 256.3, 12.7, "ms"},
                {"90%停顿时间", 45.2, 8.3, "ms"},
                {"吞吐量", 54320, 52850, "ops/s"},
                {"内存使用", 1.0, 1.15, "相对值"},
            };
            
            for (double[] row : metrics) {
                System.out.printf("%-15s: G1=%.1f%s, Shenandoah=%.1f%s (%.1f%%)\n",
                    row[0], row[1], row[3], row[2], row[3],
                    (row[2] - row[1]) * 100 / row[1]);
            }
        }
    }
}

总结

Brooks Pointer是Shenandoah GC实现并发移动的关键技术创新,通过在对象头中添加转发指针,实现了:

  1. 无锁并发移动:线程可以并行移动对象而无需全局协调

  2. 低延迟更新:引用更新通过读屏障即时完成,无需STW

  3. 内存效率:相比全局转发表,内存开销更可预测

  4. 实现简洁:状态机简单,调试相对容易

虽然带来了约5-15%的吞吐量开销和每对象8字节的内存开销,但在需要亚10毫秒停顿时间的场景下,Brooks Pointer方案提供了优秀的权衡。随着JIT编译器优化的不断改进(如屏障消除、内联缓存等),这些开销正在进一步降低。

Brooks Pointer代表了现代并发垃圾回收器设计的一个重要方向,展示了如何在保证极低停顿时间的同时,维持合理的吞吐量和内存开销。

相关推荐
星辰_mya2 小时前
Netty
java·架构·io
小程同学>o<2 小时前
嵌入式之C/C++(二)内存
c语言·开发语言·c++·笔记·嵌入式软件·面试题库
九皇叔叔2 小时前
【06】SpringBoot3 MybatisPlus 修改(Mapper)
java·spring boot·mybatis·mybatisplus
如果'\'真能转义说2 小时前
Spring 概述
java·spring
南风知我意9572 小时前
【前端面试1】基础JS的面试题
前端·javascript·面试
程序员清洒2 小时前
Flutter for OpenHarmony:Dialog 与 BottomSheet — 弹出式交互
开发语言·flutter·华为·交互·鸿蒙
cyforkk2 小时前
07、Java 基础硬核复习:面向对象编程(进阶)的核心逻辑与面试考点
java·开发语言·面试
浩浩测试一下2 小时前
应急响应之 洪水 Floods attack ==== DDOS
安全·web安全·网络安全·系统安全·ddos·安全架构
曾卫2 小时前
java.lang.*中Class 源代码详解【五】
java·源码