Android Runtime内存访问越界检查源码解析(82)

Android Runtime内存访问越界检查源码解析

一、Android Runtime内存管理基础架构

Android Runtime(ART)的内存管理基础架构是理解内存访问越界检查的前提。ART的内存管理涉及多个关键组件,包括堆内存管理、栈内存管理以及内存分配回收机制等,这些组件协同工作,为应用程序提供内存资源,并通过一系列策略保障内存使用的安全性和高效性。

在AOSP(Android Open Source Project)中,ART内存管理相关的核心代码主要集中在art/runtime/gc目录下。Heap类作为堆内存管理的核心,负责对象内存的分配与回收。在art/runtime/gc/heap.cc文件中,Heap类的构造函数初始化了堆内存的基本结构:

cpp 复制代码
// Heap类构造函数,初始化堆内存结构
Heap::Heap(GCType gc_type, const Heap::Config& config)
    : gc_type_(gc_type),
      // 堆内存的起始地址
      start_(nullptr),
      // 堆内存的结束地址
      end_(nullptr),
      // 用于快速分配的线程本地分配缓存(TLAB)
      tlab_allocator_(this),
      // 垃圾回收相关的配置和状态管理
      gc_(this, config) {
    // 初始化堆内存的其他相关参数和组件
    Initialize();
}

当应用程序请求分配对象内存时,Heap类的AllocObject方法会被调用,该方法实现了对象内存的分配逻辑:

cpp 复制代码
// Heap类的AllocObject方法用于分配对象内存
mirror::Object* Heap::AllocObject(Class* clazz, size_t byte_count, bool from_quick_compiled_code) {
    // 首先尝试从线程本地分配缓存(TLAB)中分配内存
    mirror::Object* result = tlab_allocator_.Alloc(byte_count);
    if (result!= nullptr) {
        return result;
    }
    // 如果TLAB分配失败,则从全局堆内存中分配
    result = AllocFromHeap(byte_count, kGcCauseAlloc, from_quick_compiled_code);
    if (result!= nullptr) {
        // 分配成功后,更新TLAB缓存
        tlab_allocator_.FillFromHeap(result, byte_count);
    }
    return result;
}

这种内存分配机制为内存访问越界检查提供了基础,后续的越界检查将基于对象内存的分配范围和布局进行。同时,ART的栈内存管理则主要服务于方法调用过程中的局部变量存储和函数调用栈帧维护,在art/runtime/stack.hart/runtime/stack.cc文件中实现相关逻辑,栈内存的边界管理同样是越界检查的重要部分 。

二、对象内存布局与边界定义

在Android Runtime中,对象的内存布局决定了其内存边界,清晰了解对象内存布局是进行越界检查的关键。Java对象在ART堆内存中的布局由mirror/Object.hmirror/Object.cc文件定义,mirror::Object类作为所有Java对象的基础类,包含了对象头和实例数据等部分。

对象头主要包含对象的元数据信息,如对象的类引用、哈希码、分代年龄等。在mirror::Object类中,对象头的相关定义如下:

cpp 复制代码
// mirror::Object类,定义对象的基本结构
class Object : public HeapObject {
 public:
    // 获取对象的类引用
    Class* GetClass() const REQUIRES(Locks::mutator_lock_) {
        return *reinterpret_cast<Class**>(GetClassPtr());
    }

    // 获取对象头中存储类引用的指针
    void* GetClassPtr() const REQUIRES(Locks::mutator_lock_) {
        return reinterpret_cast<void*>(
            reinterpret_cast<uintptr_t>(this) + kHeaderSize);
    }

    // 对象头的大小,包含类引用指针等元数据
    static constexpr size_t kHeaderSize = sizeof(void*);
    // 更多对象操作相关的方法定义...
};

对象的实例数据部分则根据对象所属类的成员变量定义进行存储。例如,对于一个简单的Java类Person

java 复制代码
class Person {
    int age;
    String name;
}

在ART中,Person对象的实例数据会按照成员变量的声明顺序依次存储在对象头之后。成员变量的内存占用大小根据其数据类型而定,int类型占用4个字节,String类型在ART中实际存储的是一个指向字符串数据的指针,通常占用8个字节(64位系统)。

对象的内存边界由对象头和实例数据的总大小决定。在art/runtime/class_linker.cc文件中,ClassLinker类在解析类定义时,会计算每个类的对象实例大小:

cpp 复制代码
// ClassLinker类计算类的对象实例大小
size_t ClassLinker::ComputeInstanceSize(const Class& clazz) {
    size_t size = Object::kHeaderSize;
    // 遍历类的成员变量,累加成员变量的大小
    for (const Field& field : clazz.fields_) {
        size += GetPrimitiveSize(field.type_) +
                (IsReferenceType(field.type_) &&!field.IsStatic()? kReferenceSize : 0);
    }
    return size;
}

通过明确对象的内存布局和边界,ART在后续的内存访问操作中就能够依据这些信息进行越界检查,确保对对象内存的访问都在合法范围内。

三、指针操作与越界风险

在Android Runtime中,指针操作是实现对象内存访问的重要方式,但同时也带来了内存访问越界的风险。ART的代码中,尤其是在与底层C/C++交互以及JIT编译生成的机器码中,广泛使用指针来操作对象内存。

以对象字段访问为例,在art/runtime/art_method.cc文件中,ArtMethod类的GetObjectField方法用于获取对象的引用类型字段,该方法通过指针操作实现:

cpp 复制代码
// ArtMethod类获取对象的引用类型字段
mirror::Object* ArtMethod::GetObjectField(mirror::Object* obj, int field_index) {
    // 计算字段在对象内存中的偏移量
    size_t offset = GetFieldOffset(field_index);
    // 通过指针偏移获取字段的内存地址,并转换为Object指针类型返回
    return *reinterpret_cast<mirror::Object**>(reinterpret_cast<uint8_t*>(obj) + offset);
}

在上述代码中,如果field_index计算错误或者obj指针指向无效的对象内存地址,就可能导致内存访问越界。例如,当field_index超出了对象实际的字段数量范围时,计算得到的offset将指向对象内存之外的区域,后续的指针解引用操作就会引发越界访问。

在JIT编译过程中,生成的机器码也会涉及大量的指针操作。在art/runtime/jit/jit_compiler.cc文件中,JitCompiler类在编译方法时,会将Java字节码转换为包含指针操作的机器码指令。对于数组访问操作,编译后的机器码需要检查数组索引是否越界:

cpp 复制代码
// JitCompiler类编译数组访问操作时的部分逻辑
void JitCompiler::CompileArrayAccess(...) {
    // 获取数组对象指针和索引值
    mirror::Object* array_obj =...;
    int index =...;
    // 获取数组的长度
    int array_length = GetArrayLength(array_obj);
    // 检查索引是否越界
    if (index < 0 || index >= array_length) {
        // 越界处理,通常会抛出ArrayIndexOutOfBoundsException异常
        ThrowArrayIndexOutOfBoundsException(index);
    }
    // 计算数组元素在内存中的偏移量
    size_t element_offset = CalculateArrayElementOffset(array_obj, index);
    // 通过指针操作访问数组元素
    //...
}

由于指针操作的灵活性和潜在风险,ART必须在代码的各个环节设置严格的越界检查机制,防止因指针错误使用导致内存访问越界,进而影响系统的稳定性和安全性。

四、栈内存的越界检查机制

栈内存主要用于存储方法调用过程中的局部变量、函数调用栈帧等信息,其越界检查机制对于保障程序的正确执行至关重要。在ART中,栈内存的管理和越界检查相关代码主要在art/runtime/stack.hart/runtime/stack.cc文件中。

当一个方法被调用时,会在栈内存中创建对应的栈帧。StackFrame类定义了栈帧的结构,在art/runtime/stack.h文件中:

cpp 复制代码
// StackFrame类定义栈帧结构
class StackFrame {
 public:
    // 获取栈帧中局部变量的地址
    void* GetLocalVariableAddress(int index) {
        return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(this) +
                                       kHeaderSize + index * kWordSize);
    }

    // 栈帧头的大小
    static constexpr size_t kHeaderSize = sizeof(void*);
    // 每个局部变量的大小(以字为单位,通常一个字为8字节,64位系统)
    static constexpr size_t kWordSize = sizeof(void*);
    // 更多栈帧操作相关的方法定义...
};

在方法执行过程中,对局部变量的访问需要进行越界检查。例如,当通过索引访问局部变量时,StackFrame类的GetLocalVariableAddress方法会确保索引在合法范围内:

cpp 复制代码
// StackFrame类获取局部变量地址时的越界检查
void* StackFrame::GetLocalVariableAddress(int index) {
    // 检查索引是否越界
    if (index < 0 || index >= GetLocalVariableCount()) {
        // 越界处理,通常会抛出异常
        ThrowStackOverflowException();
    }
    return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(this) +
                                   kHeaderSize + index * kWordSize);
}

此外,在函数调用和返回过程中,栈指针的管理也需要严格的越界检查。当函数调用发生时,需要将当前栈指针保存,并将栈指针向下移动以分配新的栈帧空间;函数返回时,需要恢复栈指针到调用前的状态。在art/runtime/thread.cc文件中,Thread类处理函数调用栈操作时:

cpp 复制代码
// Thread类处理函数调用时的栈操作
void Thread::CallMethod(ArtMethod* method,...) {
    // 保存当前栈指针
    uintptr_t old_sp = GetStackPointer();
    // 分配新的栈帧空间
    AllocateStackFrame(method->GetStackSize());
    // 检查栈指针是否越界
    if (GetStackPointer() < old_sp || GetStackPointer() >= GetStackLimit()) {
        // 越界处理,通常会终止程序并记录错误信息
        HandleStackOverflow();
    }
    // 执行方法调用逻辑
    //...
}

通过对栈内存的严格管理和越界检查,ART能够确保方法调用过程中局部变量的正确访问,避免因栈内存越界导致程序崩溃或数据损坏。

五、堆内存对象访问的越界检查

堆内存是Android应用中对象存储的主要区域,对堆内存对象的访问越界检查是保障内存安全的核心环节。ART在对象创建、字段访问、数组操作等场景下,都设置了详细的越界检查机制。

在对象创建时,Heap类在分配内存后会对对象进行初始化,并检查对象的内存边界。Heap类的InitializeObject方法在初始化对象时:

cpp 复制代码
// Heap类初始化对象时的越界检查
void Heap::InitializeObject(mirror::Object* obj, size_t size) {
    // 检查对象指针是否在堆内存范围内
    if (obj < start_ || obj >= end_) {
        // 对象指针越界,抛出异常
        ThrowOutOfMemoryError("Object out of heap bounds");
    }
    // 初始化对象的内存数据
    memset(obj, 0, size);
}

在对象字段访问时,除了前文提到的指针操作越界风险检查,还会检查对象是否已经被回收。在art/runtime/gc/collector/marksweep/mark_sweep_collector.cc文件中,MarkSweepCollector类在进行垃圾回收标记时,会更新对象的状态。当访问对象字段时,ArtMethod类的GetObjectField方法会检查对象的回收状态:

cpp 复制代码
// ArtMethod类获取对象字段时检查对象回收状态
mirror::Object* ArtMethod::GetObjectField(mirror::Object* obj, int field_index) {
    // 检查对象是否已被回收
    if (obj->IsMarked()) {
        // 对象已被回收,抛出异常
        ThrowObjectRecycledException();
    }
    size_t offset = GetFieldOffset(field_index);
    return *reinterpret_cast<mirror::Object**>(reinterpret_cast<uint8_t*>(obj) + offset);
}

对于数组对象,在进行元素访问时,ART会严格检查数组索引是否越界。在art/runtime/array.cc文件中,Array类的Get方法用于获取数组元素:

cpp 复制代码
// Array类获取数组元素时的越界检查
template <typename T>
T Array::Get(int index) {
    // 检查数组索引是否越界
    if (index < 0 || index >= GetLength()) {
        // 越界处理,抛出ArrayIndexOutOfBoundsException异常
        ThrowArrayIndexOutOfBoundsException(index);
    }
    // 计算元素在数组内存中的偏移量并返回元素值
    return reinterpret_cast<T*>(GetPayload())[index];
}

通过在堆内存对象访问的各个环节设置越界检查,ART有效防止了因对象内存访问越界导致的程序错误和安全漏洞。

六、垃圾回收过程中的越界检查

垃圾回收(GC)是ART内存管理的重要功能,在垃圾回收过程中同样需要进行越界检查,以确保回收操作的正确性和安全性。ART的垃圾回收算法主要有标记-清除、标记-整理等,相关代码在art/runtime/gc/collector目录下。

以标记-清除算法为例,在MarkSweepCollector类的Mark方法中,对存活对象进行标记时,需要确保访问的对象内存是合法的。在标记过程中,会遍历对象的引用关系:

cpp 复制代码
// MarkSweepCollector类标记存活对象时的越界检查
void MarkSweepCollector::Mark(Thread* self, Heap* heap) {
    // 从根集合开始标记
    MarkRoots(self, heap);
    while (!mark_queue_.IsEmpty()) {
        mirror::Object* obj = mark_queue_.Dequeue();
        // 检查对象指针是否在堆内存范围内
        if (obj < heap->start_ || obj >= heap->end_) {
            // 对象指针越界,跳过该对象
            continue;
        }
        // 标记对象的引用
        for (mirror::Object* ref : obj->GetReferences()) {
            // 检查引用对象指针是否在堆内存范围内
            if (ref < heap->start_ || ref >= heap->end_) {
                continue;
            }
            if (!ref->IsMarked()) {
                MarkObject(ref);
            }
        }
    }
}

在垃圾回收后的内存复用阶段,Heap类在回收对象内存并重新分配时,也会进行越界检查。Heap类的Free方法在释放对象内存后,会将空闲内存块加入空闲链表,在后续分配时,会检查分配的内存块是否越界:

cpp 复制代码
// Heap类释放对象内存并重新分配时的越界检查
void Heap::Free(mirror::Object* obj) {
    // 计算对象占用的内存大小
    size_t size = obj->GetClass()->GetInstanceSize();
    // 将空闲内存块加入空闲链表
    free_list_.Insert(obj, size);
}

// Heap类从空闲链表中分配内存时的越界检查
mirror::Object* Heap::AllocFromFreeList(size_t byte_count) {
    // 从空闲链表中查找合适的空闲内存块
    mirror::Object* result = free_list_.Find(byte_count);
    if (result!= nullptr) {
        // 检查分配的内存块是否在堆内存范围内
        if (result < start_ || result >= end_) {
            // 内存块越界,处理错误
            HandleMemoryAllocationError();
        }
        // 从空闲链表中移除该内存块并返回
        free_list_.Remove(result);
    }
    return result;
}

通过在垃圾回收过程中进行越界检查,ART能够避免因回收和复用内存时的越界操作导致系统出现内存错误,保障内存管理的稳定性。

七、JNI调用中的内存越界检查

Java Native Interface(JNI)允许Java代码与C/C++代码进行交互,在JNI调用过程中,内存访问越界的风险较高,因此需要严格的越界检查机制。在AOSP中,JNI相关代码主要在libnativehelper目录下。

当Java代码通过JNI调用C/C++函数时,需要传递Java对象、数组等数据。在JNIEnv类中,对这些数据的访问和操作都包含越界检查。以访问Java数组为例,JNIEnv类的GetIntArrayElements方法用于获取Java整数数组的元素指针:

八、JIT编译与内存越界检查优化

Android Runtime的即时编译(JIT)技术在提升应用执行效率的同时,也深度参与内存越界检查的优化。JIT编译过程将字节码动态转换为机器码,在此期间会对代码进行分析并插入针对性的越界检查逻辑,相关核心代码位于art/runtime/jit目录下。

JitCompiler类的CompileMethod方法中,当对方法进行编译时,会依据字节码指令类型判断是否存在内存访问操作。对于涉及对象字段访问、数组操作等可能引发越界的指令,会生成特定的检查代码:

cpp 复制代码
// JitCompiler::CompileMethod方法对方法进行JIT编译
void JitCompiler::CompileMethod(ArtMethod* method, Thread* self) {
    // 获取方法对应的字节码
    const DexFile::CodeItem* code_item = method->GetCodeItem();
    const uint8_t* insns = code_item->insns;
    size_t insn_count = code_item->insns_size_in_words * 2;

    for (size_t i = 0; i < insn_count; ) {
        // 解析字节码指令
        uint16_t opcode = DecodeOpcode(insns, i);
        if (opcode == Instruction::OP_GET_FIELD) {
            // 处理对象字段获取指令
            uint16_t field_idx = DecodeFieldOffset(insns, i);
            // 生成字段访问越界检查代码
            EmitFieldAccessBoundsCheck(method, field_idx);
            // 生成实际的字段访问机器码
            EmitGetFieldMachineCode(method, field_idx);
        } else if (opcode == Instruction::OP_ARRAY_LENGTH) {
            // 处理获取数组长度指令,无需额外越界检查,但为后续数组操作铺垫
            EmitArrayLengthMachineCode();
        } else if (opcode == Instruction::OP_ASTORE || opcode == Instruction::OP_ALOAD) {
            // 处理数组存储和加载指令
            uint16_t array_idx = DecodeArrayIndex(insns, i);
            // 生成数组索引越界检查代码
            EmitArrayIndexBoundsCheck(array_idx);
            // 生成数组操作机器码
            EmitArrayAccessMachineCode(opcode, array_idx);
        }
        // 移动到下一条指令
        i += GetInstructionSize(opcode);
    }
}

其中,EmitFieldAccessBoundsCheck函数会检查对象是否有效以及字段偏移是否超出对象范围:

cpp 复制代码
// 生成对象字段访问越界检查代码
void JitCompiler::EmitFieldAccessBoundsCheck(ArtMethod* method, uint16_t field_idx) {
    // 获取字段在对象中的偏移量
    size_t field_offset = method->GetFieldOffset(field_idx);
    // 获取对象头大小
    size_t header_size = method->GetDeclaringClass()->GetObjectSize() - method->GetDeclaringClass()->GetInstanceSize();
    // 加载对象指针到寄存器
    LoadObjectPointerToRegister();
    // 检查对象指针是否为空
    EmitNullCheck();
    // 计算对象内存结束地址
    EmitAddImmediateToRegister(header_size + field_offset);
    // 检查计算后的地址是否超出对象范围
    EmitBoundsCheck();
}

EmitArrayIndexBoundsCheck函数则专注于数组索引的合法性验证:

cpp 复制代码
// 生成数组索引越界检查代码
void JitCompiler::EmitArrayIndexBoundsCheck(uint16_t array_idx) {
    // 加载数组对象指针到寄存器
    LoadArrayObjectPointerToRegister(array_idx);
    // 获取数组长度
    EmitGetArrayLength();
    // 加载数组索引值到寄存器
    LoadArrayIndexValueToRegister(array_idx);
    // 检查索引是否小于0
    EmitLessThanZeroCheck();
    // 检查索引是否大于等于数组长度
    EmitGreaterThanOrEqualLengthCheck();
}

通过JIT编译阶段对内存访问越界检查代码的动态插入,使得应用在运行时能够实时检测越界行为,相比解释执行时的检查,效率更高且更具针对性。同时,JIT编译器还会根据代码执行的热度和频率,对越界检查代码进行优化,如合并重复检查、去除不必要的检查等,在保障安全性的前提下降低性能损耗 。

九、内存访问越界的异常处理机制

当Android Runtime检测到内存访问越界时,需要一套完善的异常处理机制来应对,避免系统崩溃或数据损坏。相关处理逻辑分布在多个类和模块中,核心围绕异常的抛出、捕获与恢复展开。

在检测到越界情况时,ART会抛出对应的异常对象。以数组越界为例,在art/runtime/array.cc中,Array类的访问方法一旦检测到索引越界,就会调用ThrowArrayIndexOutOfBoundsException函数:

cpp 复制代码
// 抛出数组越界异常
void ThrowArrayIndexOutOfBoundsException(int index) {
    ScopedObjectAccess soa(Thread::Current());
    // 创建数组越界异常对象
    mirror::Throwable* exception = soa.Self()->CreateExceptionWithFormat(
        "java/lang/ArrayIndexOutOfBoundsException", "Index %d out of bounds", index);
    // 抛出异常
    soa.Self()->ThrowException(exception);
}

异常抛出后,Java代码层面可以通过try - catch块进行捕获处理。例如:

java 复制代码
try {
    int[] arr = new int[5];
    // 故意触发越界
    int value = arr[10]; 
} catch (ArrayIndexOutOfBoundsException e) {
    // 捕获异常并进行处理
    System.err.println("数组越界异常已捕获: " + e.getMessage());
}

在ART的运行时环境中,异常的传播和处理由art/runtime/exceptions.cc中的ExceptionHandler类负责。当异常抛出后,ExceptionHandler会沿着调用栈向上查找匹配的try - catch块:

cpp 复制代码
// 异常处理核心逻辑
bool ExceptionHandler::HandleException(Thread* self, mirror::Throwable* exception) {
    StackTraceElement* stack_top = self->GetStackTraceTop();
    while (stack_top!= nullptr) {
        // 获取当前栈帧的方法
        ArtMethod* method = stack_top->GetMethod();
        // 检查方法中是否存在try - catch块
        if (method->HasExceptionHandler()) {
            // 查找匹配的异常处理器
            ExceptionHandlerInfo* handler_info = method->FindExceptionHandler(exception->GetClass());
            if (handler_info!= nullptr) {
                // 跳转到异常处理代码位置
                JumpToExceptionHandler(self, handler_info);
                return true;
            }
        }
        // 移动到上一个栈帧
        stack_top = stack_top->GetPrevious();
    }
    // 未找到合适的异常处理器,进行默认处理
    HandleUnhandledException(self, exception);
    return false;
}

对于未被捕获的异常,HandleUnhandledException函数会进行兜底处理,通常会打印详细的异常堆栈信息到日志,便于开发者定位问题:

cpp 复制代码
// 处理未捕获的异常
void ExceptionHandler::HandleUnhandledException(Thread* self, mirror::Throwable* exception) {
    // 打印异常堆栈信息
    self->DumpExceptionToLog(exception);
    // 根据配置决定是否终止进程
    if (kTerminateOnUnhandledException) {
        self->TerminateProcess();
    }
}

通过这套异常处理机制,ART能够在内存访问越界时,尽可能保证系统的稳定运行,同时为开发者提供有效的错误排查信息 。

十、内存访问越界检查的性能优化策略

尽管内存访问越界检查对系统安全性至关重要,但频繁检查会带来一定的性能开销。ART采用多种策略对越界检查进行性能优化,平衡安全性与执行效率。

  1. 缓存与预计算 在对象字段访问方面,ArtMethod类会预计算字段的偏移量并进行缓存。在art/runtime/art_method.cc中,ArtMethod的构造函数会计算字段偏移:
cpp 复制代码
// ArtMethod构造函数预计算字段偏移
ArtMethod::ArtMethod(...) {
    // 遍历类的字段
    for (size_t i = 0; i < declaring_class_->GetFieldCount(); ++i) {
        const Field& field = declaring_class_->GetField(i);
        // 计算字段偏移并存储
        field_offset_[i] = CalculateFieldOffset(field);
    }
}

后续进行字段访问时,直接从缓存中获取偏移量,减少重复计算开销:

cpp 复制代码
// 获取字段偏移量
size_t ArtMethod::GetFieldOffset(uint16_t field_idx) {
    return field_offset_[field_idx];
}

对于数组操作,JIT编译阶段会对数组长度进行缓存。在编译数组访问指令时,会将数组长度加载到寄存器并复用,避免每次访问都重新获取数组长度。

  1. 分层检查策略 ART采用分层的越界检查策略,根据代码的执行热度动态调整检查强度。对于冷代码(执行频率低的代码),会进行完整的越界检查;而对于热代码(频繁执行的代码),会采用更轻量级的检查方式。在JitCompiler中,通过计数器记录方法的执行次数,当达到一定阈值认定为热代码后,优化检查逻辑:
cpp 复制代码
// JitCompiler判断方法是否为热代码
bool JitCompiler::IsHotMethod(ArtMethod* method) {
    return method->GetInvocationCount() >= kHotMethodThreshold;
}

// 对热代码的数组访问进行优化检查
void JitCompiler::EmitOptimizedArrayIndexBoundsCheck(uint16_t array_idx) {
    // 采用更简洁的检查逻辑,例如只检查非负性,
    // 假设数组长度在之前已确认有效
    LoadArrayIndexValueToRegister(array_idx);
    EmitLessThanZeroCheck();
}
  1. 硬件辅助优化 利用现代处理器的硬件特性进行优化。例如,一些处理器支持内存保护机制,ART可以通过设置内存页的访问权限,使得非法的内存访问直接触发硬件异常,相比软件检查更加高效。在Linux内核层,通过mmap系统调用分配内存时设置合适的保护标志:
cpp 复制代码
// 使用mmap分配内存并设置保护标志
void* allocated_memory = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (allocated_memory == MAP_FAILED) {
    // 内存分配失败处理
}
// 设置内存保护,禁止越界访问
mprotect(allocated_memory, size, PROT_READ | PROT_WRITE | PROT_EXEC);

通过这些性能优化策略,ART在保障内存访问安全性的同时,有效降低了越界检查带来的性能损耗,提升了应用的整体运行效率。

相关推荐
Java中文社群18 分钟前
面试官:如何实现大模型连续对话?
java·后端·面试
yuanlaile3 小时前
Flutter Android打包学习指南
android·flutter·flutter打包·flutter android
教程分享大师3 小时前
中兴B860AV5.1-M2_S905L3SB最新完美版线刷包 解决指示灯异常问题
android
天天摸鱼的java工程师3 小时前
如何防止重复提交订单?
java·后端·面试
_一条咸鱼_3 小时前
LangChain解析器与下游任务集成的源码实现剖析(23)
人工智能·面试·langchain
2501_915918413 小时前
iOS 性能监控工具全解析 选择合适的调试方案提升 App 性能
android·ios·小程序·https·uni-app·iphone·webview
冉冉同学3 小时前
【HarmonyOS NEXT】解决Repeat复用导致Image加载图片展示的是上一张图片的问题
android·前端·客户端
天天摸鱼的java工程师3 小时前
订单超时取消的五种解法:从普通商品到秒杀订单,业务不同方案不同!
java·后端·面试
努力学习的小廉4 小时前
深入了解linux系统—— 信号的捕捉
android·linux·运维
ssshooter4 小时前
前端 Monorepo 实践指南:从选择到实现
前端·面试·架构