Android运行时ART加载OAT文件的过程
目录
一,概述
1.1 OAT是如何产生的
一,概述
OAT文件是一种Android私有ELF文件格式,它不仅包含有从DEX文件翻译而来的本地机器指令,还包含有原来的DEX文件内容。这使得我们无需重新编译原有的APK就可以让它正常地在ART里面运行,也就是我们不需要改变原来的APK编程接口
为了更好了弄明白oat文件的格式,目前还需要看下elf 文件格式
oat文件格式
作为Android私有的一种ELF文件,OAT文件包含有两个特殊的段oatdata和oatexec,前者包含有用来生成本地机器指令的dex文件内容,后者包含有生成的本地机器指令,它们之间的关系通过储存在oatdata段前面的oat头部描述。此外,在OAT文件的dynamic段,导出了三个符号oatdata、oatexec和oatlastword,它们的值就是用来界定oatdata段和oatexec段的起止位置的。其中,[oatdata, oatexec - 1]描述的是oatdata段的起止位置,而[oatexec, oatlastword + 3]描述的是oatexec的起止位置。要完全理解OAT的文件格式,除了要理解本文即将要分析的OAT加载过程之外,还需要掌握接下来文章分析的类和方法查找过程。
1.1 OAT是如何产生的
/Volumes/aosp/android-8.1.0_r52/frameworks/native/cmds/installd/dexopt.cpp
static void run_dex2oat(int zip_fd, int oat_fd, int input_vdex_fd, int output_vdex_fd, int image_fd,const char* input_file_name, const char* output_file_name, int swap_fd,const char* instruction_set, const char* compiler_filter,bool debuggable, bool post_bootcomplete, int profile_fd, const char* class_loader_context) {static const unsigned int MAX_INSTRUCTION_SET_LEN = 7;if (strlen(instruction_set) >= MAX_INSTRUCTION_SET_LEN) {ALOGE("Instruction set %s longer than max length of %d",instruction_set, MAX_INSTRUCTION_SET_LEN);return;}// Get the relative path to the input file.const char* relative_input_file_name = get_location_from_path(input_file_name);char dex2oat_Xms_flag[kPropertyValueMax];bool have_dex2oat_Xms_flag = get_property("dalvik.vm.dex2oat-Xms", dex2oat_Xms_flag, NULL) > 0;char dex2oat_Xmx_flag[kPropertyValueMax];bool have_dex2oat_Xmx_flag = get_property("dalvik.vm.dex2oat-Xmx", dex2oat_Xmx_flag, NULL) > 0;char dex2oat_threads_buf[kPropertyValueMax];bool have_dex2oat_threads_flag = get_property(post_bootcomplete? "dalvik.vm.dex2oat-threads": "dalvik.vm.boot-dex2oat-threads",dex2oat_threads_buf,NULL) > 0;char dex2oat_threads_arg[kPropertyValueMax + 2];if (have_dex2oat_threads_flag) {sprintf(dex2oat_threads_arg, "-j%s", dex2oat_threads_buf);}char dex2oat_isa_features_key[kPropertyKeyMax];sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", instruction_set);char dex2oat_isa_features[kPropertyValueMax];bool have_dex2oat_isa_features = get_property(dex2oat_isa_features_key,dex2oat_isa_features, NULL) > 0;char dex2oat_isa_variant_key[kPropertyKeyMax];sprintf(dex2oat_isa_variant_key, "dalvik.vm.isa.%s.variant", instruction_set);char dex2oat_isa_variant[kPropertyValueMax];bool have_dex2oat_isa_variant = get_property(dex2oat_isa_variant_key,dex2oat_isa_variant, NULL) > 0;const char *dex2oat_norelocation = "-Xnorelocate";bool have_dex2oat_relocation_skip_flag = false;char dex2oat_flags[kPropertyValueMax];int dex2oat_flags_count = get_property("dalvik.vm.dex2oat-flags",dex2oat_flags, NULL) <= 0 ? 0 : split_count(dex2oat_flags);ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags);// If we are booting without the real /data, don't spend time compiling.char vold_decrypt[kPropertyValueMax];bool have_vold_decrypt = get_property("vold.decrypt", vold_decrypt, "") > 0;bool skip_compilation = (have_vold_decrypt &&(strcmp(vold_decrypt, "trigger_restart_min_framework") == 0 ||(strcmp(vold_decrypt, "1") == 0)));bool generate_debug_info = property_get_bool("debug.generate-debug-info", false);char app_image_format[kPropertyValueMax];char image_format_arg[strlen("--image-format=") + kPropertyValueMax];bool have_app_image_format =image_fd >= 0 && get_property("dalvik.vm.appimageformat", app_image_format, NULL) > 0;if (have_app_image_format) {sprintf(image_format_arg, "--image-format=%s", app_image_format);}char dex2oat_large_app_threshold[kPropertyValueMax];bool have_dex2oat_large_app_threshold =get_property("dalvik.vm.dex2oat-very-large", dex2oat_large_app_threshold, NULL) > 0;char dex2oat_large_app_threshold_arg[strlen("--very-large-app-threshold=") + kPropertyValueMax];if (have_dex2oat_large_app_threshold) {sprintf(dex2oat_large_app_threshold_arg,"--very-large-app-threshold=%s",dex2oat_large_app_threshold);}static const char* DEX2OAT_BIN = "/system/bin/dex2oat";static const char* RUNTIME_ARG = "--runtime-arg";static const int MAX_INT_LEN = 12; // '-'+10dig+'\0' -OR- 0x+8dig// clang FORTIFY doesn't let us use strlen in constant array bounds, so we// use arraysize instead.char zip_fd_arg[arraysize("--zip-fd=") + MAX_INT_LEN];char zip_location_arg[arraysize("--zip-location=") + PKG_PATH_MAX];char input_vdex_fd_arg[arraysize("--input-vdex-fd=") + MAX_INT_LEN];char output_vdex_fd_arg[arraysize("--output-vdex-fd=") + MAX_INT_LEN];char oat_fd_arg[arraysize("--oat-fd=") + MAX_INT_LEN];char oat_location_arg[arraysize("--oat-location=") + PKG_PATH_MAX];char instruction_set_arg[arraysize("--instruction-set=") + MAX_INSTRUCTION_SET_LEN];char instruction_set_variant_arg[arraysize("--instruction-set-variant=") + kPropertyValueMax];char instruction_set_features_arg[arraysize("--instruction-set-features=") + kPropertyValueMax];char dex2oat_Xms_arg[arraysize("-Xms") + kPropertyValueMax];char dex2oat_Xmx_arg[arraysize("-Xmx") + kPropertyValueMax];char dex2oat_compiler_filter_arg[arraysize("--compiler-filter=") + kPropertyValueMax];bool have_dex2oat_swap_fd = false;char dex2oat_swap_fd[arraysize("--swap-fd=") + MAX_INT_LEN];bool have_dex2oat_image_fd = false;char dex2oat_image_fd[arraysize("--app-image-fd=") + MAX_INT_LEN];size_t class_loader_context_size = arraysize("--class-loader-context=") + PKG_PATH_MAX;char class_loader_context_arg[class_loader_context_size];if (class_loader_context != nullptr) {snprintf(class_loader_context_arg, class_loader_context_size, "--class-loader-context=%s",class_loader_context);}sprintf(zip_fd_arg, "--zip-fd=%d", zip_fd);sprintf(zip_location_arg, "--zip-location=%s", relative_input_file_name);sprintf(input_vdex_fd_arg, "--input-vdex-fd=%d", input_vdex_fd);sprintf(output_vdex_fd_arg, "--output-vdex-fd=%d", output_vdex_fd);sprintf(oat_fd_arg, "--oat-fd=%d", oat_fd);sprintf(oat_location_arg, "--oat-location=%s", output_file_name);sprintf(instruction_set_arg, "--instruction-set=%s", instruction_set);sprintf(instruction_set_variant_arg, "--instruction-set-variant=%s", dex2oat_isa_variant);sprintf(instruction_set_features_arg, "--instruction-set-features=%s", dex2oat_isa_features);if (swap_fd >= 0) {have_dex2oat_swap_fd = true;sprintf(dex2oat_swap_fd, "--swap-fd=%d", swap_fd);}if (image_fd >= 0) {have_dex2oat_image_fd = true;sprintf(dex2oat_image_fd, "--app-image-fd=%d", image_fd);}if (have_dex2oat_Xms_flag) {sprintf(dex2oat_Xms_arg, "-Xms%s", dex2oat_Xms_flag);}if (have_dex2oat_Xmx_flag) {sprintf(dex2oat_Xmx_arg, "-Xmx%s", dex2oat_Xmx_flag);}// Compute compiler filter.bool have_dex2oat_compiler_filter_flag = false;if (skip_compilation) {strcpy(dex2oat_compiler_filter_arg, "--compiler-filter=extract");have_dex2oat_compiler_filter_flag = true;have_dex2oat_relocation_skip_flag = true;} else if (compiler_filter != nullptr) {if (strlen(compiler_filter) + strlen("--compiler-filter=") <arraysize(dex2oat_compiler_filter_arg)) {sprintf(dex2oat_compiler_filter_arg, "--compiler-filter=%s", compiler_filter);have_dex2oat_compiler_filter_flag = true;} else {ALOGW("Compiler filter name '%s' is too large (max characters is %zu)",compiler_filter,kPropertyValueMax);}}if (!have_dex2oat_compiler_filter_flag) {char dex2oat_compiler_filter_flag[kPropertyValueMax];have_dex2oat_compiler_filter_flag = get_property("dalvik.vm.dex2oat-filter",dex2oat_compiler_filter_flag, NULL) > 0;if (have_dex2oat_compiler_filter_flag) {sprintf(dex2oat_compiler_filter_arg,"--compiler-filter=%s",dex2oat_compiler_filter_flag);}}// Check whether all apps should be compiled debuggable.if (!debuggable) {char prop_buf[kPropertyValueMax];debuggable =(get_property("dalvik.vm.always_debuggable", prop_buf, "0") > 0) &&(prop_buf[0] == '1');}char profile_arg[strlen("--profile-file-fd=") + MAX_INT_LEN];if (profile_fd != -1) {sprintf(profile_arg, "--profile-file-fd=%d", profile_fd);}// Get the directory of the apk to pass as a base classpath directory.char base_dir[arraysize("--classpath-dir=") + PKG_PATH_MAX];std::string apk_dir(input_file_name);unsigned long dir_index = apk_dir.rfind('/');bool has_base_dir = dir_index != std::string::npos;if (has_base_dir) {apk_dir = apk_dir.substr(0, dir_index);sprintf(base_dir, "--classpath-dir=%s", apk_dir.c_str());}ALOGV("Running %s in=%s out=%s\n", DEX2OAT_BIN, relative_input_file_name, output_file_name);const char* argv[9 // program name, mandatory arguments and the final NULL+ (have_dex2oat_isa_variant ? 1 : 0)+ (have_dex2oat_isa_features ? 1 : 0)+ (have_dex2oat_Xms_flag ? 2 : 0)+ (have_dex2oat_Xmx_flag ? 2 : 0)+ (have_dex2oat_compiler_filter_flag ? 1 : 0)+ (have_dex2oat_threads_flag ? 1 : 0)+ (have_dex2oat_swap_fd ? 1 : 0)+ (have_dex2oat_image_fd ? 1 : 0)+ (have_dex2oat_relocation_skip_flag ? 2 : 0)+ (generate_debug_info ? 1 : 0)+ (debuggable ? 1 : 0)+ (have_app_image_format ? 1 : 0)+ dex2oat_flags_count+ (profile_fd == -1 ? 0 : 1)+ (class_loader_context != nullptr ? 1 : 0)+ (has_base_dir ? 1 : 0)+ (have_dex2oat_large_app_threshold ? 1 : 0)];int i = 0;argv[i++] = DEX2OAT_BIN;argv[i++] = zip_fd_arg;argv[i++] = zip_location_arg;argv[i++] = input_vdex_fd_arg;argv[i++] = output_vdex_fd_arg;argv[i++] = oat_fd_arg;argv[i++] = oat_location_arg;argv[i++] = instruction_set_arg;if (have_dex2oat_isa_variant) {argv[i++] = instruction_set_variant_arg;}if (have_dex2oat_isa_features) {argv[i++] = instruction_set_features_arg;}if (have_dex2oat_Xms_flag) {argv[i++] = RUNTIME_ARG;argv[i++] = dex2oat_Xms_arg;}if (have_dex2oat_Xmx_flag) {argv[i++] = RUNTIME_ARG;argv[i++] = dex2oat_Xmx_arg;}if (have_dex2oat_compiler_filter_flag) {argv[i++] = dex2oat_compiler_filter_arg;}if (have_dex2oat_threads_flag) {argv[i++] = dex2oat_threads_arg;}if (have_dex2oat_swap_fd) {argv[i++] = dex2oat_swap_fd;}if (have_dex2oat_image_fd) {argv[i++] = dex2oat_image_fd;}if (generate_debug_info) {argv[i++] = "--generate-debug-info";}if (debuggable) {argv[i++] = "--debuggable";}if (have_app_image_format) {argv[i++] = image_format_arg;}if (have_dex2oat_large_app_threshold) {argv[i++] = dex2oat_large_app_threshold_arg;}if (dex2oat_flags_count) {i += split(dex2oat_flags, argv + i);}if (have_dex2oat_relocation_skip_flag) {argv[i++] = RUNTIME_ARG;argv[i++] = dex2oat_norelocation;}if (profile_fd != -1) {argv[i++] = profile_arg;}if (has_base_dir) {argv[i++] = base_dir;}if (class_loader_context != nullptr) {argv[i++] = class_loader_context_arg;}// Do not add after dex2oat_flags, they should override others for debugging.argv[i] = NULL;execv(DEX2OAT_BIN, (char * const *)argv);ALOGE("execv(%s) failed: %s\n", DEX2OAT_BIN, strerror(errno));
}
其中,参数zip_fd和oat_fd都是打开文件描述符,指向的分别是正在安装的APK文件和要生成的OAT文件。OAT文件的生成过程主要就是涉及到将包含在APK里面的classes.dex文件的DEX字节码翻译成本地机器指令。这相当于是编写一个输入文件为DEX、输出文件为OAT的编译器
当我们选择了ART运行时时,Zygote进程在启动的过程中,会调用libart.so里面的函数JNI_CreateJavaVM来创建一个ART
/Volumes/aosp/android-8.1.0_r52/art/runtime/java_vm_ext.cc
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {ScopedTrace trace(__FUNCTION__);const JavaVMInitArgs* args = static_cast<JavaVMInitArgs*>(vm_args);if (JavaVMExt::IsBadJniVersion(args->version)) {LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;return JNI_EVERSION;}RuntimeOptions options;for (int i = 0; i < args->nOptions; ++i) {JavaVMOption* option = &args->options[i];options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo));}bool ignore_unrecognized = args->ignoreUnrecognized;if (!Runtime::Create(options, ignore_unrecognized)) {return JNI_ERR;}// Initialize native loader. This step makes sure we have// everything set up before we start using JNI.android::InitializeNativeLoader();Runtime* runtime = Runtime::Current();bool started = runtime->Start();if (!started) {delete Thread::Current()->GetJniEnv();delete runtime->GetJavaVM();LOG(WARNING) << "CreateJavaVM failed";return JNI_ERR;}*p_env = Thread::Current()->GetJniEnv();*p_vm = runtime->GetJavaVM();return JNI_OK;
}
参数vm_args用作ART虚拟机的启动参数,它被转换为一个JavaVMInitArgs对象后,再按照Key-Value的组织形式保存一个Options向量中,并且以该向量作为参数传递给Runtime类的静态成员函数Create。
Runtime类的静态成员函数Create负责在进程中创建一个ART虚拟机。创建成功后,就调用Runtime类的另外一个静态成员函数Start启动该ART虚拟机。注意,这个创建ART虚拟的动作只会在Zygote进程中执行,SystemServer系统进程以及Android应用程序进程的ART虚拟机都是直接从Zygote进程fork出来共享的。这与Dalvik虚拟机的创建方式是完全一样的。
接下来我们就重点分析Runtime类的静态成员函数Create,它的实现如下所示
/Volumes/aosp/android-8.1.0_r52/art/runtime/runtime.cc
bool Runtime::Create(RuntimeArgumentMap&& runtime_options) {// TODO: acquire a static mutex on Runtime to avoid racing.if (Runtime::instance_ != nullptr) {return false;}instance_ = new Runtime;Locks::SetClientCallback(IsSafeToCallAbort);if (!instance_->Init(std::move(runtime_options))) {// TODO: Currently deleting the instance will abort the runtime on destruction. Now This will// leak memory, instead. Fix the destructor. b/19100793.// delete instance_;instance_ = nullptr;return false;}return true;
}
instance_是Runtime类的静态成员变量,它指向进程中的一个Runtime单例。这个Runtime单例描述的就是当前进程的ART虚拟机实例。
函数首先判断当前进程是否已经创建有一个ART虚拟机实例了。如果有的话,函数就立即返回。否则的话,就创建一个ART虚拟机实例,并且保存在Runtime类的静态成员变量instance_中,最后调用Runtime类的成员函数Init对该新创建的ART虚拟机进行初始化。
Runtime类的成员函数Init的实现如下所示:
bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) {// (b/30160149): protect subprocesses from modifications to LD_LIBRARY_PATH, etc.// Take a snapshot of the environment at the time the runtime was created, for use by Exec, etc.env_snapshot_.TakeSnapshot();RuntimeArgumentMap runtime_options(std::move(runtime_options_in));ScopedTrace trace(__FUNCTION__);CHECK_EQ(sysconf(_SC_PAGE_SIZE), kPageSize);MemMap::Init();// Try to reserve a dedicated fault page. This is allocated for clobbered registers and sentinels.// If we cannot reserve it, log a warning.// Note: We allocate this first to have a good chance of grabbing the page. The address (0xebad..)// is out-of-the-way enough that it should not collide with boot image mapping.// Note: Don't request an error message. That will lead to a maps dump in the case of failure,// leading to logspam.{constexpr uintptr_t kSentinelAddr =RoundDown(static_cast<uintptr_t>(Context::kBadGprBase), kPageSize);protected_fault_page_.reset(MemMap::MapAnonymous("Sentinel fault page",reinterpret_cast<uint8_t*>(kSentinelAddr),kPageSize,PROT_NONE,/* low_4g */ true,/* reuse */ false,/* error_msg */ nullptr));if (protected_fault_page_ == nullptr) {LOG(WARNING) << "Could not reserve sentinel fault page";} else if (reinterpret_cast<uintptr_t>(protected_fault_page_->Begin()) != kSentinelAddr) {LOG(WARNING) << "Could not reserve sentinel fault page at the right address.";protected_fault_page_.reset();}}using Opt = RuntimeArgumentMap;VLOG(startup) << "Runtime::Init -verbose:startup enabled";QuasiAtomic::Startup();oat_file_manager_ = new OatFileManager;Thread::SetSensitiveThreadHook(runtime_options.GetOrDefault(Opt::HookIsSensitiveThread));Monitor::Init(runtime_options.GetOrDefault(Opt::LockProfThreshold),runtime_options.GetOrDefault(Opt::StackDumpLockProfThreshold));boot_class_path_string_ = runtime_options.ReleaseOrDefault(Opt::BootClassPath);class_path_string_ = runtime_options.ReleaseOrDefault(Opt::ClassPath);properties_ = runtime_options.ReleaseOrDefault(Opt::PropertiesList);compiler_callbacks_ = runtime_options.GetOrDefault(Opt::CompilerCallbacksPtr);patchoat_executable_ = runtime_options.ReleaseOrDefault(Opt::PatchOat);must_relocate_ = runtime_options.GetOrDefault(Opt::Relocate);is_zygote_ = runtime_options.Exists(Opt::Zygote);is_explicit_gc_disabled_ = runtime_options.Exists(Opt::DisableExplicitGC);dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::Dex2Oat);image_dex2oat_enabled_ = runtime_options.GetOrDefault(Opt::ImageDex2Oat);dump_native_stack_on_sig_quit_ = runtime_options.GetOrDefault(Opt::DumpNativeStackOnSigQuit);vfprintf_ = runtime_options.GetOrDefault(Opt::HookVfprintf);exit_ = runtime_options.GetOrDefault(Opt::HookExit);abort_ = runtime_options.GetOrDefault(Opt::HookAbort);default_stack_size_ = runtime_options.GetOrDefault(Opt::StackSize);use_tombstoned_traces_ = runtime_options.GetOrDefault(Opt::UseTombstonedTraces);
#if !defined(ART_TARGET_ANDROID)CHECK(!use_tombstoned_traces_)<< "-Xusetombstonedtraces is only supported in an Android environment";
#endifstack_trace_file_ = runtime_options.ReleaseOrDefault(Opt::StackTraceFile);compiler_executable_ = runtime_options.ReleaseOrDefault(Opt::Compiler);compiler_options_ = runtime_options.ReleaseOrDefault(Opt::CompilerOptions);for (StringPiece option : Runtime::Current()->GetCompilerOptions()) {if (option.starts_with("--debuggable")) {SetJavaDebuggable(true);break;}}image_compiler_options_ = runtime_options.ReleaseOrDefault(Opt::ImageCompilerOptions);image_location_ = runtime_options.GetOrDefault(Opt::Image);max_spins_before_thin_lock_inflation_ =runtime_options.GetOrDefault(Opt::MaxSpinsBeforeThinLockInflation);monitor_list_ = new MonitorList;monitor_pool_ = MonitorPool::Create();thread_list_ = new ThreadList(runtime_options.GetOrDefault(Opt::ThreadSuspendTimeout));intern_table_ = new InternTable;verify_ = runtime_options.GetOrDefault(Opt::Verify);allow_dex_file_fallback_ = !runtime_options.Exists(Opt::NoDexFileFallback);no_sig_chain_ = runtime_options.Exists(Opt::NoSigChain);force_native_bridge_ = runtime_options.Exists(Opt::ForceNativeBridge);Split(runtime_options.GetOrDefault(Opt::CpuAbiList), ',', &cpu_abilist_);fingerprint_ = runtime_options.ReleaseOrDefault(Opt::Fingerprint);if (runtime_options.GetOrDefault(Opt::Interpret)) {GetInstrumentation()->ForceInterpretOnly();}zygote_max_failed_boots_ = runtime_options.GetOrDefault(Opt::ZygoteMaxFailedBoots);experimental_flags_ = runtime_options.GetOrDefault(Opt::Experimental);is_low_memory_mode_ = runtime_options.Exists(Opt::LowMemoryMode);madvise_random_access_ = runtime_options.GetOrDefault(Opt::MadviseRandomAccess);plugins_ = runtime_options.ReleaseOrDefault(Opt::Plugins);agents_ = runtime_options.ReleaseOrDefault(Opt::AgentPath);// TODO Add back in -agentlib// for (auto lib : runtime_options.ReleaseOrDefault(Opt::AgentLib)) {// agents_.push_back(lib);// }float foreground_heap_growth_multiplier;if (is_low_memory_mode_ && !runtime_options.Exists(Opt::ForegroundHeapGrowthMultiplier)) {// If low memory mode, use 1.0 as the multiplier by default.foreground_heap_growth_multiplier = 1.0f;} else {foreground_heap_growth_multiplier =runtime_options.GetOrDefault(Opt::ForegroundHeapGrowthMultiplier) +kExtraDefaultHeapGrowthMultiplier;}XGcOption xgc_option = runtime_options.GetOrDefault(Opt::GcOption);heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize),runtime_options.GetOrDefault(Opt::HeapGrowthLimit),runtime_options.GetOrDefault(Opt::HeapMinFree),runtime_options.GetOrDefault(Opt::HeapMaxFree),runtime_options.GetOrDefault(Opt::HeapTargetUtilization),foreground_heap_growth_multiplier,runtime_options.GetOrDefault(Opt::MemoryMaximumSize),runtime_options.GetOrDefault(Opt::NonMovingSpaceCapacity),runtime_options.GetOrDefault(Opt::Image),runtime_options.GetOrDefault(Opt::ImageInstructionSet),// Override the collector type to CC if the read barrier config.kUseReadBarrier ? gc::kCollectorTypeCC : xgc_option.collector_type_,kUseReadBarrier ? BackgroundGcOption(gc::kCollectorTypeCCBackground): runtime_options.GetOrDefault(Opt::BackgroundGc),runtime_options.GetOrDefault(Opt::LargeObjectSpace),runtime_options.GetOrDefault(Opt::LargeObjectThreshold),runtime_options.GetOrDefault(Opt::ParallelGCThreads),runtime_options.GetOrDefault(Opt::ConcGCThreads),runtime_options.Exists(Opt::LowMemoryMode),runtime_options.GetOrDefault(Opt::LongPauseLogThreshold),runtime_options.GetOrDefault(Opt::LongGCLogThreshold),runtime_options.Exists(Opt::IgnoreMaxFootprint),runtime_options.GetOrDefault(Opt::UseTLAB),xgc_option.verify_pre_gc_heap_,xgc_option.verify_pre_sweeping_heap_,xgc_option.verify_post_gc_heap_,xgc_option.verify_pre_gc_rosalloc_,xgc_option.verify_pre_sweeping_rosalloc_,xgc_option.verify_post_gc_rosalloc_,xgc_option.gcstress_,xgc_option.measure_,runtime_options.GetOrDefault(Opt::EnableHSpaceCompactForOOM),runtime_options.GetOrDefault(Opt::HSpaceCompactForOOMMinIntervalsMs));if (!heap_->HasBootImageSpace() && !allow_dex_file_fallback_) {LOG(ERROR) << "Dex file fallback disabled, cannot continue without image.";return false;}dump_gc_performance_on_shutdown_ = runtime_options.Exists(Opt::DumpGCPerformanceOnShutdown);if (runtime_options.Exists(Opt::JdwpOptions)) {Dbg::ConfigureJdwp(runtime_options.GetOrDefault(Opt::JdwpOptions));}callbacks_->AddThreadLifecycleCallback(Dbg::GetThreadLifecycleCallback());callbacks_->AddClassLoadCallback(Dbg::GetClassLoadCallback());jit_options_.reset(jit::JitOptions::CreateFromRuntimeArguments(runtime_options));if (IsAotCompiler()) {// If we are already the compiler at this point, we must be dex2oat. Don't create the jit in// this case.// If runtime_options doesn't have UseJIT set to true then CreateFromRuntimeArguments returns// null and we don't create the jit.jit_options_->SetUseJitCompilation(false);jit_options_->SetSaveProfilingInfo(false);}// Use MemMap arena pool for jit, malloc otherwise. Malloc arenas are faster to allocate but// can't be trimmed as easily.const bool use_malloc = IsAotCompiler();arena_pool_.reset(new ArenaPool(use_malloc, /* low_4gb */ false));jit_arena_pool_.reset(new ArenaPool(/* use_malloc */ false, /* low_4gb */ false, "CompilerMetadata"));if (IsAotCompiler() && Is64BitInstructionSet(kRuntimeISA)) {// 4gb, no malloc. Explanation in header.low_4gb_arena_pool_.reset(new ArenaPool(/* use_malloc */ false, /* low_4gb */ true));}linear_alloc_.reset(CreateLinearAlloc());BlockSignals();InitPlatformSignalHandlers();// Change the implicit checks flags based on runtime architecture.switch (kRuntimeISA) {case kArm:case kThumb2:case kX86:case kArm64:case kX86_64:case kMips:case kMips64:implicit_null_checks_ = true;// Installing stack protection does not play well with valgrind.implicit_so_checks_ = !(RUNNING_ON_MEMORY_TOOL && kMemoryToolIsValgrind);break;default:// Keep the defaults.break;}if (!no_sig_chain_) {// Dex2Oat's Runtime does not need the signal chain or the fault handler.if (implicit_null_checks_ || implicit_so_checks_ || implicit_suspend_checks_) {fault_manager.Init();// These need to be in a specific order. The null point check handler must be// after the suspend check and stack overflow check handlers.//// Note: the instances attach themselves to the fault manager and are handled by it. The manager// will delete the instance on Shutdown().if (implicit_suspend_checks_) {new SuspensionHandler(&fault_manager);}if (implicit_so_checks_) {new StackOverflowHandler(&fault_manager);}if (implicit_null_checks_) {new NullPointerHandler(&fault_manager);}if (kEnableJavaStackTraceHandler) {new JavaStackTraceHandler(&fault_manager);}}}std::string error_msg;java_vm_ = JavaVMExt::Create(this, runtime_options, &error_msg);if (java_vm_.get() == nullptr) {LOG(ERROR) << "Could not initialize JavaVMExt: " << error_msg;return false;}// Add the JniEnv handler.// TODO Refactor this stuff.java_vm_->AddEnvironmentHook(JNIEnvExt::GetEnvHandler);Thread::Startup();// ClassLinker needs an attached thread, but we can't fully attach a thread without creating// objects. We can't supply a thread group yet; it will be fixed later. Since we are the main// thread, we do not get a java peer.Thread* self = Thread::Attach("main", false, nullptr, false);CHECK_EQ(self->GetThreadId(), ThreadList::kMainThreadId);CHECK(self != nullptr);self->SetCanCallIntoJava(!IsAotCompiler());// Set us to runnable so tools using a runtime can allocate and GC by defaultself->TransitionFromSuspendedToRunnable();// Now we're attached, we can take the heap locks and validate the heap.GetHeap()->EnableObjectValidation();CHECK_GE(GetHeap()->GetContinuousSpaces().size(), 1U);if (UNLIKELY(IsAotCompiler())) {class_linker_ = new AotClassLinker(intern_table_);} else {class_linker_ = new ClassLinker(intern_table_);}if (GetHeap()->HasBootImageSpace()) {bool result = class_linker_->InitFromBootImage(&error_msg);if (!result) {LOG(ERROR) << "Could not initialize from image: " << error_msg;return false;}if (kIsDebugBuild) {for (auto image_space : GetHeap()->GetBootImageSpaces()) {image_space->VerifyImageAllocations();}}if (boot_class_path_string_.empty()) {// The bootclasspath is not explicitly specified: construct it from the loaded dex files.const std::vector<const DexFile*>& boot_class_path = GetClassLinker()->GetBootClassPath();std::vector<std::string> dex_locations;dex_locations.reserve(boot_class_path.size());for (const DexFile* dex_file : boot_class_path) {dex_locations.push_back(dex_file->GetLocation());}boot_class_path_string_ = android::base::Join(dex_locations, ':');}{ScopedTrace trace2("AddImageStringsToTable");GetInternTable()->AddImagesStringsToTable(heap_->GetBootImageSpaces());}if (IsJavaDebuggable()) {// Now that we have loaded the boot image, deoptimize its methods if we are running// debuggable, as the code may have been compiled non-debuggable.DeoptimizeBootImage();}} else {std::vector<std::string> dex_filenames;Split(boot_class_path_string_, ':', &dex_filenames);std::vector<std::string> dex_locations;if (!runtime_options.Exists(Opt::BootClassPathLocations)) {dex_locations = dex_filenames;} else {dex_locations = runtime_options.GetOrDefault(Opt::BootClassPathLocations);CHECK_EQ(dex_filenames.size(), dex_locations.size());}std::vector<std::unique_ptr<const DexFile>> boot_class_path;if (runtime_options.Exists(Opt::BootClassPathDexList)) {boot_class_path.swap(*runtime_options.GetOrDefault(Opt::BootClassPathDexList));} else {OpenDexFiles(dex_filenames,dex_locations,runtime_options.GetOrDefault(Opt::Image),&boot_class_path);}instruction_set_ = runtime_options.GetOrDefault(Opt::ImageInstructionSet);if (!class_linker_->InitWithoutImage(std::move(boot_class_path), &error_msg)) {LOG(ERROR) << "Could not initialize without image: " << error_msg;return false;}// TODO: Should we move the following to InitWithoutImage?SetInstructionSet(instruction_set_);for (uint32_t i = 0; i < kCalleeSaveSize; i++) {CalleeSaveType type = CalleeSaveType(i);if (!HasCalleeSaveMethod(type)) {SetCalleeSaveMethod(CreateCalleeSaveMethod(), type);}}}CHECK(class_linker_ != nullptr);verifier::MethodVerifier::Init();if (runtime_options.Exists(Opt::MethodTrace)) {trace_config_.reset(new TraceConfig());trace_config_->trace_file = runtime_options.ReleaseOrDefault(Opt::MethodTraceFile);trace_config_->trace_file_size = runtime_options.ReleaseOrDefault(Opt::MethodTraceFileSize);trace_config_->trace_mode = Trace::TraceMode::kMethodTracing;trace_config_->trace_output_mode = runtime_options.Exists(Opt::MethodTraceStreaming) ?Trace::TraceOutputMode::kStreaming :Trace::TraceOutputMode::kFile;}// TODO: move this to just be an Trace::Start argumentTrace::SetDefaultClockSource(runtime_options.GetOrDefault(Opt::ProfileClock));// Pre-allocate an OutOfMemoryError for the double-OOME case.self->ThrowNewException("Ljava/lang/OutOfMemoryError;","OutOfMemoryError thrown while trying to throw OutOfMemoryError; ""no stack trace available");pre_allocated_OutOfMemoryError_ = GcRoot<mirror::Throwable>....
......}
创建好ART虚拟机堆后,Runtime类的成员函数Init接着又创建了一个JavaVMExt实例。这个JavaVMExt实例最终是要返回给调用者的,使得调用者可以通过该JavaVMExt实例来和ART虚拟机交互。再接下来,Runtime类的成员函数Init通过Thread类的成员函数Attach将当前线程作为ART虚拟机的主线程,使得当前线程可以调用ART虚拟机提供的JNI接口
Runtime类的成员函数GetHeap返回的便是当前ART虚拟机的堆,也就是前面创建的ART虚拟机堆。通过调用Heap类的成员函数GetContinuousSpaces可以获得堆里面的连续空间列表。如果这个列表的第一个连续空间是一个Image空间,那么就调用ClassLinker类的静态成员函数CreateFromImage来创建一个ClassLinker对象。否则的话,上述ClassLinker对象就要通过ClassLinker类的另外一个静态成员函数CreateFromCompiler来创建。创建出来的ClassLinker对象是后面ART虚拟机加载加载Java类时要用到的。
后面我们分析ART虚拟机的垃圾收集机制时会看到,ART虚拟机的堆包含有三个连续空间和一个不连续空间。三个连续空间分别用来分配不同的对象。当第一个连续空间不是Image空间时,就表明当前进程不是Zygote进程,而是安装应用程序时启动的一个dex2oat进程。安装应用程序时启动的dex2oat进程也会在内部创建一个ART虚拟机,不过这个ART虚拟机是用来将DEX字节码编译成本地机器指令的,而Zygote进程创建的ART虚拟机是用来运行应用程序的。
接下来我们主要分析ParsedOptions类的静态成员函数Create和ART虚拟机堆Heap的构造函数,以便可以了解ART虚拟机的启动参数解析过程和ART虚拟机的堆创建过程。
Heap类的构造函数的
相关文章:
Android运行时ART加载OAT文件的过程
目录 一,概述 1.1 OAT是如何产生的 一,概述 OAT文件是一种Android私有ELF文件格式,它不仅包含有从DEX文件翻译而来的本地机器指令,还包含有原来的DEX文件内容。这使得我们无需重新编译原有的APK就可以让它正常地在ART里面运行,也就是我们不…...
Python读取comsol仿真导出数据并绘图
文章目录 comsol数据导出python读取文件python绘制云图python进一步分析数据 完整代码 当我们使用comsol,ansys等仿真工具进行仿真后,难免需要对仿真结果进行导出并进一步处理分析。 今天小姜以comsol的一个简单磁场仿真为例,详细介绍如何对c…...
cloudfare+gmail 配置 smtp 邮箱
这里介绍有一个域名后,不需要服务器,就可以实现 cloudfare gmail 的 邮箱收发。 为什么还需要 gmail 的 smtp 功能,因为 cloudfare 默认只是对 email 进行转发,就是只能收邮件而不能发送邮件,故使用 gmail 的功能来进…...
【翻译、转载】使用 LLM 构建 MCP
资料来源: https://modelcontextprotocol.io/tutorials/building-mcp-with-llms 本文仅仅是翻译。 使用 LLM 构建 MCP 利用 Claude 等大型语言模型(LLM)加速您的 MCP 开发! 本指南将帮助您使用 LLM 来构建自定义的模型上下文协…...
Python速成系列二
文章目录 Python 条件语句与循环结构详解一、条件语句(if-elif-else)1. 基本 if 结构2. if-else 结构3. if-elif-else 结构4. 嵌套条件语句5. 三元表达式(条件表达式) 二、循环结构1. while 循环2. for 循环3. 循环控制语句break …...
基于STM32的心电图监测系统设计
摘要 本论文旨在设计一种基于 STM32 微控制器的心电图监测系统,通过对人体心电信号的采集、处理和分析,实现对心电图的实时监测与显示。系统采用高精度的心电信号采集模块,结合 STM32 强大的数据处理能力,能够有效去除噪声干扰&a…...
线程池的线程数配置策略
目录 1. CPU密集型任务 2. IO密集型任务 3. 混合型任务 1. CPU密集型任务 特点:任务主要消耗CPU资源(如计算、加密、压缩)。 推荐线程数: 线程数 ≈ 物理核心数 1 / CPU - 1(不知道哪个√) 例如&#…...
分享一个Android中文汉字手写输入法并带有形近字联想功能
最近我写了一个Android版本的中文汉字手写输入法功能,并实现了汉字形近字联想功能,此手写输入法功能完全满足公司的需求。 之前小编用Android SurfaceView,运用canvas的Path画坐标轨迹,并结合使用一个叫汉王输入法的so库来识别手…...
C语言:文件操作
文件的概念 文件是计算机用于存储数据的工具,我们计算机磁盘上的数据是混乱的,但是我们计算机系统通过文件的方式记录数据在磁盘上的位置来将数据整齐划分。 文件的类型 文件有两种类型,数据文件与程序文件 程序文件是用来执行的文件&#…...
2024年第十五届蓝桥杯省赛B组Python【 简洁易懂题解】
2024年第十五届蓝桥杯省赛B组Python题解 一、整体情况说明 2024年第十五届蓝桥杯省赛B组Python组考试共包含8道题目,分为结果填空题和程序设计题两类。 考试时间:4小时编程环境:Python 3.x,禁止使用第三方库,仅可使…...
线程与进程深度解析:从fork行为到生产者-消费者模型
线程与进程深度解析:从fork行为到生产者-消费者模型 一、多线程环境下的fork行为与线程安全 1. 多线程程序中fork的特殊性 核心问题:fork后子进程的线程模型 当多线程程序中的某个线程调用fork时: 子进程仅包含调用fork的线程࿱…...
2025年第十六届蓝桥杯省赛B组Java题解【完整、易懂版】
2025年第十六届蓝桥杯省赛B组Java题解 题型概览与整体分析 题目编号题目名称题型难度核心知识点通过率(预估)A逃离高塔结果填空★☆☆数学规律、模运算95%B消失的蓝宝结果填空★★★同余定理、中国剩余定理45%C电池分组编程题★★☆异或运算性质70%D魔法…...
【NTN 卫星通信】NTN关键问题的一些解决方法(一)
1 概述 3GPP在协议23.737中对一些卫星通信需要面对的关键问题进行了探讨,并且讨论了初步的解决方法,继续来看看这些内容把。 问题包括: 1、大型卫星覆盖区域的移动性管理 2、移动卫星覆盖区域的移动性管理 3、卫星延迟 4、卫星接入的QoS …...
C++基础算法9:Dijkstra
1、概念 Dijkstra算法 是一种用于计算图中单源最短路径的算法,主要用于加权图(图中边的权重可以不同)中找出从起点到各个其他节点的最短路径。 Dijkstra算法的核心概念: 图的表示: 有向图:图的边是有方…...
5块钱的无忧套餐卡可以变成流量卡吗
电信的 5 块钱无忧套餐卡理论上可以变成流量卡,但会受到一些条件限制,以下是具体介绍: 中国电信无忧卡简介 中国电信无忧卡是电信推出的低月租套餐,月租仅 5 元,包含 200M 国内流量、来电显示和 189 邮箱,全…...
word页眉去掉线
直接双击页眉处于下面状态: 然后: 按CtrlshiftN即可!去除...
Spark,Idea中编写Spark程序 2
Idea中编写Spark程序 一、修改pom.xml文件 <build><sourceDirectory>src/main/scala</sourceDirectory><testSourceDirectory>src/test/scala</testSourceDirectory> <!-- 添加必要的插件以打包scala程序--><plugins><plu…...
GTID(全局事务标识符)的深入解析
GTID(全局事务标识符)的深入解析 GTID(Global Transaction Identifier)是 MySQL 5.6 版本引入的一项核心功能,旨在解决传统主从复制中的痛点。它通过为每个事务赋予一个全局唯一的标识符,彻底改变了复制的管理方式。 一、传统复制的痛点 在 GTID 出现之前,MySQL 主从…...
Circular Plot系列(一): 环形热图绘制
针对近期多个粉丝咨询环形图的绘制,我意识到,我们似乎没有真正介绍过circle图,但这一类图确是非常常用的图,所以这里详细学习一下circle的绘制,使用的是circlize包,功能很完善:安装包, #https:/…...
字符串匹配 之 KMP算法
文章目录 习题28.找出字符串中第一个匹配项的下标1392.最长快乐前缀 本博客充分参考灵神和知乎的另一位博主 灵神KMP算法模版 知乎博主通俗易懂讲解 对于给定一个主串S和一个模式串P,如果让你求解出模式串P在主串S中匹配的情况下的所有的开始下标简单的做法又称为Brute-Force算…...
「一针见血能力」的终极训练手册
缘起 和顶尖的高手接触以后,发现他们在表达沟通上面的能力真的太强了,仿佛有种一阵见血看问题的能力,这种拨开浓雾看本质的能力是嘈杂世界防止上当受骗的不二法门. 网上找了一些训练方法,可以试试训练锐化思维,提高表…...
Linux 入门:操作系统进程详解
目录 一.冯诺依曼体系结构 一). 软件运行前为什么要先加载?程序运行之前在哪里? 二).理解数据流动 二.操作系统OS(Operator System) 一).概念 二).设计OS的目的 三).如何理解操作系统…...
【2025软考高级架构师】——2024年05月份真题与解析
摘要 本文内容是关于2025年软考高级架构师考试的相关资料,包含2024年05月份真题与解析。其中涉及体系结构演化的步骤、OSI协议中能提供安全服务的层次、数据库设计阶段中进行关系反规范化的环节等知识点,还提及了软考高级架构师考试的多个模块ÿ…...
Mybatis执行流程知多少
思维导图: 一、MyBatis 执行流程概述 MyBatis 的执行流程可以大致分为以下几个关键步骤:配置加载、会话创建、SQL 执行和结果处理。下面我们将逐步详细介绍每个步骤。 二、配置加载 1. 配置文件的重要性 MyBatis 的配置文件是整个框架的基础,…...
码蹄集——偶数位、四边形坐标
目录 MT1039 偶数位 MT1051 四边形坐标 MT1039 偶数位 思路:直接使用按位操作符 一个整型数字是32位,十六进制表示为0x后跟8个字符,每个字符为0-e,代表0-15; 把偶数位改为0,就是用0去&偶数位,用1去&奇数位,即0xAAAAAAAA,A代表10,1010(从右往 左依次为0位,…...
Java 中使用 Callable 创建线程的方法
一、Callable 接口概述 Callable接口位于java.util.concurrent包中,与Runnable接口类似,同样用于定义线程执行的任务,但它具有以下独特特性: 支持返回值:Callable接口声明了一个call()方法,该方法会在…...
代码随想录算法训练营Day44
力扣1045.不相交的线【medium】 力扣53.最大子数组和【medium】 力扣392.判断子序列【easy】 一、力扣1045.不相交的线【medium】 题目链接:力扣1045.不相交的线 视频链接:代码随想录 题解链接:灵茶山艾府 1、思路 和1143.最长公共子序列一…...
Java大师成长计划之第12天:性能调优与GC原理
📢 友情提示: 本文由银河易创AI(https://ai.eaigx.com)平台gpt-4o-mini模型辅助创作完成,旨在提供灵感参考与技术分享,文中关键数据、代码与结论建议通过官方渠道验证。 在 Java 编程中,性能调优…...
【MySQL】索引(重要)
目录 一、索引本质: 索引的核心作用 索引的优缺点 二、预备知识: 硬件理解: 软件理解: MySQL与磁盘交互基本单位: 三、索引的理解: 理解page: 单个page: 多个page&#x…...
C++多态(上)
目录 一、多态的概念 二、多态的定义及实现 1. 多态的构成条件 2. 虚函数 3. 虚函数的重写 4. C11 override 和 final 4.1 final 关键字 4.2 override 关键字 5. 重载、覆盖(重写)、隐藏(重定义)的对比 三、抽象类 1. 概…...
【AI提示词】 复利效应教育专家
提示说明 一位拥有金融学和教育学背景的知识型内容创作者,擅长用简单易懂的语言向读者解释复杂概念 提示词 # Role: 复利效应教育专家## Profile - language: 中文 - description: 一位拥有金融学和教育学背景的知识型内容创作者,擅长用简单易懂的语言…...
嵌入式系统基础知识
目录 一、冯诺依曼结构与哈佛结构 (一)冯诺依曼结构 (二)哈佛架构 二、ARM存储模式 (一)大端模式 (二)小端模式 (三)混合模式 三、CISC 与 RISC &am…...
如何克服情绪拖延症?
引言 你是否也曾有过这样的经历? 明明手头有重要的工作,却总是忍不住刷手机、看视频,直到最后一刻才匆忙赶工? 你是否在心里暗暗发誓“明天一定好好干”,但第二天依旧重复着同样的拖延? 其实࿰…...
【操作系统】哲学家进餐问题
问题描述 哲学家进餐问题是并发编程中的一个经典问题,描述了五位哲学家围坐在一张圆桌旁,他们的生活由思考和进餐组成。在圆桌上有五个盘子,每位哲学家面前一个盘子,盘子之间有一支叉子。哲学家进餐需要同时使用左右两支叉子。问题…...
Kotlin协程解析
目录 一、协程的使用 二、协程的执行原理 2.1、挂起函数的反编译代码及执行分析 2.2、协程执行流程分析 2.2.1、createCoroutineUnintercepted方法 2.2.2、intercepted方法 2.2.3、resumeCancellableWith方法 2.3、Dispatcher----分发器的实现 2.3.1、Main 分发器的实…...
Nginx核心功能 02
目录 Nginx代理技术核心概念 (一)正向代理(Forward Proxy) 1. 基本定义 2. 技术原理 3. 应用场景 (二)反向代理(Reverse Proxy) 1. 基本定义 2. 技术原理 3. 应用场景 一、…...
聊聊对Mysql的理解
目录 1、Sql介绍 1.1、SQL的分类 1.2、数据库的三大范式 1.3、数据表的约束 1.4、约束的添加与删除 2、核心特性 3、主要组件 4、数据结构原理 5、索引失效 6、常用问题 7、优势与局限 前言 MySQL是一个开源的关系型数据库管理系统(RDBMS),由瑞典MySQL A…...
「Mac畅玩AIGC与多模态17」开发篇13 - 条件判断与分支跳转工作流示例
一、概述 本篇在多节点串联的基础上,进一步引入条件判断与分支跳转机制,实现根据用户输入内容动态走不同执行路径。开发人员将学习如何配置判断节点、定义分支规则,以及如何在工作流中引导执行方向,完成基础的逻辑控制。 二、环境准备 macOS 系统Dify 平台已部署并可访问…...
pycharm terminal 窗口打不开了
参考添加链接描述powershell.exe改为cmd.exe发现有一个小正方形,最大化可以看见了。...
JAVA:使用 MapStruct 实现高效对象映射的技术指南
1、简述 在 Java 开发中,对象之间的转换是一个常见的需求,尤其是在 DTO(数据传输对象)和实体类之间的转换过程中。手动编写转换代码既耗时又容易出错,而 MapStruct 是一个优秀的对象映射框架,可以通过注解生成高效的对象转换代码,从而大大提升开发效率。 本文将介绍 M…...
Linux线程深度解析:从基础到实践
Linux线程深度解析:从基础到实践 一、线程基础概念 1. 进程与线程定义 进程:一个正在运行的程序,是操作系统资源分配的最小单位(拥有独立的地址空间、文件描述符等资源),状态包括就绪、运行、阻塞。线程…...
【ROS2】launch启动文件如何集成到ROS2(Python版本)
一、简单实操 1.创建/打开一个功能包 mkdir -p my_ws/src cd my_ws/src ros2 pkg create my_pkg_example --build-type ament_python 2.创建Launch文件的存放目录 将所有启动文件都存储在launch包内的目录中。 目录结构如下所示: src/my_pkg_example/launch/…...
用 PyTorch 轻松实现 MNIST 手写数字识别
用 PyTorch 轻松实现 MNIST 手写数字识别 引言 在深度学习领域,MNIST 数据集就像是 “Hello World” 级别的经典入门项目。它包含大量手写数字图像及对应标签,非常适合新手学习如何搭建和训练神经网络模型。本文将基于 PyTorch 框架,详细拆…...
碰撞检测学习笔记
目录 SUMO 模拟碰撞 LimSim pygame模拟碰撞检测 SUMO 模拟碰撞 LimSim 多模态大语言模型(M)LLM的出现为人工智能开辟了新的途径,特别是提供增强的理解和推理能力,为自动驾驶开辟了新途径。本文介绍LimSim,LimSim的…...
Sway初体验
Sway(缩写自 SirCmpwn’s Wayland compositor[1])是一款专为 Wayland 设计的合成器,旨在与 i3 完全兼容。根据官网所述: Sway 是 Wayland 的合成器,也是 x11 的 i3 窗口管理器的替代品。它可以根据您现有的 i3 配置工作…...
《工业社会的诞生》章节
工业革命的技术前奏 早期工业技术双引擎: 【火药武器】:重塑战争形态与经济地理 新式青铜炮助力殖民扩张,开辟全球贸易网络 高桅帆船(西班牙大帆船)实现洲际航行 战争规模化倒逼中央集权,催生国家-商人…...
消息队列MQ
参考资料:https://cloud.tencent.com/developer/article/2335397 https://www.cnblogs.com/hahaha111122222/p/18457859 消息队列是大型分布式系统不可缺少的中间件,也是高并发系统的基石中间件 消息队列 消息队列 Message Queue 消息队列是利用高效可…...
LangChain4J-XiaozhiAI 项目分析报告
LangChain4J-XiaozhiAI 项目分析报告 GitHub 链接 1. 项目概述 本项目名为 “硅谷小智(医疗版)”,是一个基于 Java 技术栈和 LangChain4J 框架构建的 AI 聊天助手应用。其核心目标是利用大型语言模型(LLM)的能力&am…...
学习spring boot-拦截器Interceptor,过滤器Filter
目录 拦截器Interceptor 过滤器Filter 关于过滤器的前置知识可以参考: 过滤器在springboot项目的应用 一,使用WebfilterServletComponentScan 注解 1 创建过滤器类实现Filter接口 2 在启动类中添加 ServletComponentScan 注解 二,创建…...
【程序+论文】大规模新能源并网下的火电机组深度调峰经济调度
目录 1 主要内容 讲解重点 2 讲解视频及代码 1 主要内容 该视频为《大规模新能源并网下的火电机组深度调峰经济调度》代码讲解内容,该程序有完全对照的论文,以改进IEEE30节点作为研究对象,系统包括5个火电机组和2个新能源机组,…...