Java对象分配原理

Java对象模型: OOP-Klass模型

在正式探讨JVM对象的创建前,先简单地介绍一下hotspot中实现的Java的对象模型。在JVM中,并没有直接将Java对象映射成C++对象,而是采用了oop-klass模型,主要是不希望每个对象中都包含有一份虚函数表,其中:

  1. OOP(Ordinary Object Point),表示对象的实例信息
  2. Klass,是Java类的在C++中的表示,用来描述Java类的信息

简单地说,一个Java类在JVM中被拆分为了两个部分:数据和描述信息,分别对应OOP和Klass。

在具体的JVM源码中,当加载一个Class时,会创建一个InstanceKlass对象,实例化的对象则对应InstanceOopDesc,其中InstanceKlass存放在元空间,InstanceOopDesc存放在堆中。

对象创建过程

首先先来看InstanceOopDesc的数据结构,InstanceOopDesc继承了OopDesc,数据结构如下

// 此处为了方便阅读,改写了一下代码
class instanceOopDesc : public oopDesc {private:volatile markOop _mark;union _metadata {Klass* _klass;narrowKlass _compressed_klass;} _metadata;
};

其中_metadata指向该对象的InstanceKlass,而_mark中则存储了对象运行时的状态数据,数据结构如下(图中为32位的情况下的数据,64位也大同小异)

32 bits:
--------
hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object)
JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object)
size:32 ------------------------------------------>| (CMS free block)
PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object)

每一行都代表了一种情况,描述了哈希码、GC分代年龄、锁等状态信息,如下:

hash: 哈希码
age: 分代年龄
biased_lock: 偏向锁标识位
lock: 锁状态标识位
JavaThread*: 持有偏向锁的线程ID
epoch: 偏向时间戳

instanceOopDesc其实保存的是对象的头部信息,除了头部信息,对象还有数据,对象数据紧跟着头部后面,图示如下:

 

1. 入口

 

上图截取了一段程序字节码,红线所框对应了Java中new操作的字节码,Java中的new操作对应了字节码的三个操作,本文主要讲述第一个操作(new)。字节码中new操作对应JVM中的InterpreterRuntime::_new,代码如下,

// hotspot/share/interpreter/interpreterRuntime.cpp
IRT_ENTRY(void, InterpreterRuntime::_new(JavaThread* thread, ConstantPool* pool, int index))Klass* k = pool->klass_at(index, CHECK);InstanceKlass* klass = InstanceKlass::cast(k);klass->check_valid_for_instantiation(true, CHECK); // 校验:接口/抽象类/Class不能实例化klass->initialize(CHECK); // 初始化klassoop obj = klass->allocate_instance(CHECK); // 分配实例thread->set_vm_result(obj);
IRT_END
里面主要包含了两个部分:初始化klass和分配实例

2. 初始化klass

// hotspot/share/oops/instanceKlass.cpp
void InstanceKlass::initialize(TRAPS) {if (this->should_be_initialized()) {initialize_impl(CHECK);} else {assert(is_initialized(), "sanity check");}
}

在这里我们继续看initialize_impl()方法

// hotspot/share/oops/instanceKlass.cpp
void InstanceKlass::initialize_impl(TRAPS) {HandleMark hm(THREAD);link_class(CHECK); // 链接classbool wait = false;// Step 1{Handle h_init_lock(THREAD, init_lock());ObjectLocker ol(h_init_lock, THREAD, h_init_lock() != NULL);Thread *self = THREAD;// Step 2while(is_being_initialized() && !is_reentrant_initialization(self)) {wait = true;ol.waitUninterruptibly(CHECK);}// Step 3if (is_being_initialized() && is_reentrant_initialization(self)) {DTRACE_CLASSINIT_PROBE_WAIT(recursive, -1, wait);return;}// Step 4if (is_initialized()) {DTRACE_CLASSINIT_PROBE_WAIT(concurrent, -1, wait);return;}// Step 5if (is_in_error_state()) {DTRACE_CLASSINIT_PROBE_WAIT(erroneous, -1, wait);ResourceMark rm(THREAD);const char* desc = "Could not initialize class ";const char* className = external_name();size_t msglen = strlen(desc) + strlen(className) + 1;char* message = NEW_RESOURCE_ARRAY(char, msglen);if (NULL == message) {// Out of memory: can't create detailed error messageTHROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), className);} else {jio_snprintf(message, msglen, "%s%s", desc, className);THROW_MSG(vmSymbols::java_lang_NoClassDefFoundError(), message);}}// Step 6set_init_state(being_initialized);set_init_thread(self);}// Step 7if (!is_interface()) {Klass* super_klass = super();if (super_klass != NULL && super_klass->should_be_initialized()) {super_klass->initialize(THREAD);}if (!HAS_PENDING_EXCEPTION && has_nonstatic_concrete_methods()) {initialize_super_interfaces(THREAD);}if (HAS_PENDING_EXCEPTION) {Handle e(THREAD, PENDING_EXCEPTION);CLEAR_PENDING_EXCEPTION;{EXCEPTION_MARK;// Locks object, set state, and notify all waiting threadsset_initialization_state_and_notify(initialization_error, THREAD);CLEAR_PENDING_EXCEPTION;}DTRACE_CLASSINIT_PROBE_WAIT(super__failed, -1, wait);THROW_OOP(e());}}AOTLoader::load_for_klass(this, THREAD);// Step 8{assert(THREAD->is_Java_thread(), "non-JavaThread in initialize_impl");JavaThread* jt = (JavaThread*)THREAD;DTRACE_CLASSINIT_PROBE_WAIT(clinit, -1, wait);PerfClassTraceTime timer(ClassLoader::perf_class_init_time(),ClassLoader::perf_class_init_selftime(),ClassLoader::perf_classes_inited(),jt->get_thread_stat()->perf_recursion_counts_addr(),jt->get_thread_stat()->perf_timers_addr(),PerfClassTraceTime::CLASS_CLINIT);call_class_initializer(THREAD);}// Step 9if (!HAS_PENDING_EXCEPTION) {set_initialization_state_and_notify(fully_initialized, CHECK);{debug_only(vtable().verify(tty, true);)}}else {// Step 10 and 11Handle e(THREAD, PENDING_EXCEPTION);CLEAR_PENDING_EXCEPTION;JvmtiExport::clear_detected_exception((JavaThread*)THREAD);{EXCEPTION_MARK;set_initialization_state_and_notify(initialization_error, THREAD);CLEAR_PENDING_EXCEPTION;JvmtiExport::clear_detected_exception((JavaThread*)THREAD);}DTRACE_CLASSINIT_PROBE_WAIT(error, -1, wait);if (e->is_a(SystemDictionary::Error_klass())) {THROW_OOP(e());} else {JavaCallArguments args(e);THROW_ARG(vmSymbols::java_lang_ExceptionInInitializerError(),vmSymbols::throwable_void_signature(),&args);}}DTRACE_CLASSINIT_PROBE_WAIT(end, -1, wait);
}

2.1 链接

// hotspot/share/oops/instanceKlass.cpp
bool InstanceKlass::link_class_impl(bool throw_verifyerror, TRAPS) {if (is_linked()) {return true;}assert(THREAD->is_Java_thread(), "non-JavaThread in link_class_impl");JavaThread* jt = (JavaThread*)THREAD;// 先链接父类Klass* super_klass = super();if (super_klass != NULL) {if (super_klass->is_interface()) {ResourceMark rm(THREAD);Exceptions::fthrow(THREAD_AND_LOCATION,vmSymbols::java_lang_IncompatibleClassChangeError(),"class %s has interface %s as super class",external_name(),super_klass->external_name());return false;}InstanceKlass* ik_super = InstanceKlass::cast(super_klass);ik_super->link_class_impl(throw_verifyerror, CHECK_false);}// 链接该类的所有借口Array<Klass*>* interfaces = local_interfaces();int num_interfaces = interfaces->length();for (int index = 0; index < num_interfaces; index++) {InstanceKlass* interk = InstanceKlass::cast(interfaces->at(index));interk->link_class_impl(throw_verifyerror, CHECK_false);}if (is_linked()) {return true;}// 验证 & 重写{HandleMark hm(THREAD);Handle h_init_lock(THREAD, init_lock());ObjectLocker ol(h_init_lock, THREAD, h_init_lock() != NULL);if (!is_linked()) {if (!is_rewritten()) {{bool verify_ok = verify_code(throw_verifyerror, THREAD);if (!verify_ok) {return false;}}if (is_linked()) {return true;}// 重写类rewrite_class(CHECK_false);} else if (is_shared()) {SystemDictionaryShared::check_verification_constraints(this, CHECK_false);}// 重写完成后链接方法link_methods(CHECK_false);// 初始化vtable和itableClassLoaderData * loader_data = class_loader_data();if (!(is_shared() &&loader_data->is_the_null_class_loader_data())) {ResourceMark rm(THREAD);vtable().initialize_vtable(true, CHECK_false);itable().initialize_itable(true, CHECK_false);}// 将类的状态标记为已链接set_init_state(linked);if (JvmtiExport::should_post_class_prepare()) {Thread *thread = THREAD;assert(thread->is_Java_thread(), "thread->is_Java_thread()");JvmtiExport::post_class_prepare((JavaThread *) thread, this);}}}return true;
}

class链接的过程就是这样,主要步骤总结如下:

  1. 链接父类和实现的接口
  2. 重写类
  3. 初始化vtable和itable
  4. 将类的状态标记为已链接

关于重写类和初始化vtable、itable的内容有空新开一章,本文就不描述具体细节了。

2.2 初始化过程

这段初始化klass步骤在JVM规范中有详细描述,假设当前类(接口)为C,它持有一个独有的初始化锁LC

  1. 同步锁LC,防止并发导致多次初始化
  2. 如果有其他线程正在初始化C,就释放LC并阻塞当前线程直到那个线程完成初始化
  3. 如果是执行初始化的是当前线程,则表明是递归请求,释放LC并正常完成初始化
  4. 如果C已经被初始化了,则释放LC并正常完成初始化
  5. 如果C的对象处于一个错误状态,则释放LC并抛出NoClassDefFoundError异常
  6. 记录C正在被当前线程初始化并释放LC,初始化类中所有final static字段
  7. 如果C是一个类,初始化其父类和接口
  8. 判断C是否打开断言
  9. 执行类(接口)的初始化方法
  10. 标记C已经完全初始化,并唤醒所有的等待线程
  11. 如果初始化失败,则抛出异常,并将C标记为错误,同时唤醒所有的等待线程

上文为JVM11规范中的步骤,实际中可以看到hotspot在实现时和规范所写略有偏差,但基本差不多。

3. 分配实例

// hotspot/share/oops/instanceKlass.cpp
instanceOop InstanceKlass::allocate_instance(TRAPS) {bool has_finalizer_flag = has_finalizer(); // 是否存在非空finalize()方法int size = size_helper(); // 类的大小instanceOop i;i = (instanceOop)Universe::heap()->obj_allocate(this, size, CHECK_NULL); // 分配对象if (has_finalizer_flag && !RegisterFinalizersAtInit) {i = register_finalizer(i, CHECK_NULL);}return i;
}
在这里我们比较关注的是堆空间分配对象环节,

3.1 堆空间分配对象

代码如下:

// hotspot/share/gc/shared/memAllocator.cpp
oop MemAllocator::allocate() const {oop obj = NULL;{Allocation allocation(*this, &obj);HeapWord* mem = mem_allocate(allocation);if (mem != NULL) {obj = initialize(mem);}}return obj;
}
很容易可以看到,此处的主流程分为两个部分,内存分配和初始化。

3.1.1 内存分配

直接打开代码,如下:

// hotspot/share/gc/shared/memAllocator.cpp
HeapWord* MemAllocator::mem_allocate(Allocation& allocation) const {if (UseTLAB) {HeapWord* result = allocate_inside_tlab(allocation);if (result != NULL) {return result;}}return allocate_outside_tlab(allocation);
}
在这段代码中,我们可以看到一个很耳熟的东西——TLAB(ThreadLocalAllocBuffer),默认情况下TLAB是打开状态,而且其对Java性能提升非常显著。首先,先简单介绍一下TLAB的概念,

因为JVM堆空间是所有线程共享的,因此分配一个对象时会锁住整个堆,这样效率就会比较低下。因此JVM在eden区分配了一块空间作为线程的私有缓冲区,这个缓冲区称为TLAB。不同线程不共享TLAB,因此在TLAB中分配对象时是无需上锁的,从而可以快速分配。

在这段代码中,内存分配划分为了两个部分——TLAB内分配和TLAB外分配。

a. TLAB内分配

我们先来看看TLAB内分配的过程,

// hotspot/share/gc/shared/memAllocator.cpp
HeapWord* MemAllocator::allocate_inside_tlab(Allocation& allocation) const {HeapWord* mem = _thread->tlab().allocate(_word_size);if (mem != NULL) {return mem;}return allocate_inside_tlab_slow(allocation);
}

同样的在TLAB的分配的过程中,也被拆成了两种情况,一种是直接使用线程现有的TLAB来进行分配,代码如下,在下面的这段代码中,我们可以看到TLAB的分配就只是简单地将top指针向上增加了size大小,并且将原先top的位置分配给了obj,因此分配效率可以说是极速了。(事实上,TLAB就是通过start、top、end等指针标记了TLAB的存储信息以及分配空间)

// hotspot/share/gc/shared/threadLocalAllocBuffer.inline.hpp
inline HeapWord* ThreadLocalAllocBuffer::allocate(size_t size) {invariants(); // 校验TLAB是否合法HeapWord* obj = top();if (pointer_delta(end(), obj) >= size) {set_top(obj + size);invariants();return obj;}return NULL;
}

接下来我们来看看TLAB内的慢分配,

// hotspot/share/gc/shared/memAllocator.cpp
HeapWord* MemAllocator::allocate_inside_tlab_slow(Allocation& allocation) const {HeapWord* mem = NULL;ThreadLocalAllocBuffer& tlab = _thread->tlab();if (ThreadHeapSampler::enabled()) {tlab.set_back_allocation_end();mem = tlab.allocate(_word_size);if (mem != NULL) {allocation._tlab_end_reset_for_sample = true;return mem;}}// 如果TLAB的剩余空间大于阈值,则保留TLAB,这样就会进入TLAB外分配。在这里,每次TLAB分配失败,该TLAB都会调大该阈值,以防线程重复分配同样大小的对象if (tlab.free() > tlab.refill_waste_limit()) {tlab.record_slow_allocation(_word_size);return NULL;}// 计算一个新的TLAB的大小,公式=min{可用空间,期待空间+对象占据空间,最大TLAB空间}size_t new_tlab_size = tlab.compute_size(_word_size);// 清理原先的TLAB。会将剩余的未使用空间填充进一个假数组,创造EDEN连续的假象,并且将start、end、top等指针全部置为空tlab.clear_before_allocation();if (new_tlab_size == 0) {return NULL;}// 创建一个新的TLAB,空间可能在min_tlab_size到new_tlab_size之间size_t min_tlab_size = ThreadLocalAllocBuffer::compute_min_size(_word_size);mem = _heap->allocate_new_tlab(min_tlab_size, new_tlab_size, &allocation._allocated_tlab_size);if (mem == NULL) {return NULL;}// 将分配的空间数据全部清0if (ZeroTLAB) {Copy::zero_to_words(mem, allocation._allocated_tlab_size);}// 将mem位置分配word_size大小给objtlab.fill(mem, mem + _word_size, allocation._allocated_tlab_size);return mem;
}

b. TLAB外分配

// hotspot/share/gc/shared/memAllocator.cpp
HeapWord* MemAllocator::allocate_outside_tlab(Allocation& allocation) const {allocation._allocated_outside_tlab = true;HeapWord* mem = _heap->mem_allocate(_word_size, &allocation._overhead_limit_exceeded);if (mem == NULL) {return mem;}NOT_PRODUCT(_heap->check_for_non_bad_heap_word_value(mem, _word_size));size_t size_in_bytes = _word_size * HeapWordSize;_thread->incr_allocated_bytes(size_in_bytes);return mem;
}
这里的核心关注点只有一个——堆内存分配,此处以openjdk11的默认GC——G1为例,看一看分配的过程。
// hotspot/share/gc/g1/g1CollectedHeap.cpp
HeapWord* G1CollectedHeap::mem_allocate(size_t word_size,bool* gc_overhead_limit_was_exceeded) {assert_heap_not_locked_and_not_at_safepoint();if (is_humongous(word_size)) {return attempt_allocation_humongous(word_size);}size_t dummy = 0;return attempt_allocation(word_size, word_size, &dummy);
}

在G1中,对象的分配分为了两种形式:大对象分配、普通分配。由于代码比较长,简单描述大对象的分配过程如下:

  1. 检查是否需要GC,如需要则触发GC,因为大对象消耗堆的速度非常快
  2. 计算大对象需要占据多少区块,尝试分配连续的空闲区块
  3. 如果没有足够的连续空间,找到一块包含空闲和使用中的连续区块,尝试扩展
  4. 尝试GC,如果失败达到阈值则分配失败,进行下一步的普通分配

接下来的普通分配过程较为复杂,本文就不再深入探究了。

3.1.2 初始化对象

代码如下

// hotspot/share/gc/shared/memAllocator.cpp
oop ObjAllocator::initialize(HeapWord* mem) const {mem_clear(mem);return finish(mem);
}其中mem_clear()方法比较简单,就是将对象除头部以外的数据全部置为0,代码如下,
// hotspot/share/gc/shared/memAllocator.cpp
void MemAllocator::mem_clear(HeapWord* mem) const {const size_t hs = oopDesc::header_size();oopDesc::set_klass_gap(mem, 0);Copy::fill_to_aligned_words(mem + hs, _word_size - hs);
}

接下来看看finish()函数,

// hotspot/share/gc/shared/memAllocator.cpp
oop MemAllocator::finish(HeapWord* mem) const {assert(mem != NULL, "NULL object pointer");if (UseBiasedLocking) {oopDesc::set_mark_raw(mem, _klass->prototype_header());} else {oopDesc::set_mark_raw(mem, markOopDesc::prototype());}oopDesc::release_set_klass(mem, _klass);return oop(mem);
}

还记得对象头中有两个属性mark和metadata吗?finish()方法就是设置对象的头部数据。

3.2 注册finalize()方法

由于平时几乎很少用到finalize(),且内部逻辑比较复杂,因此本文暂时不探究finalize的注册机制。

4. 整体流程

整个JVM对象分配的整体流程大致如下,

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/439818.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

动手学PaddlePaddle(4):MNIST(手写数字识别)

本次练习将使用 PaddlePaddle 来实现三种不同的分类器&#xff0c;用于识别手写数字。三种分类器所实现的模型分别为 Softmax 回归、多层感知器、卷积神经网络。 您将学会 实现一个基于Softmax回归的分类器&#xff0c;用于识别手写数字 实现一个基于多层感知器的分类器&#…

动手学PaddlePaddle(5):迁移学习

本次练习&#xff0c;用迁移学习思想&#xff0c;结合paddle框架&#xff0c;来实现图像的分类。 相关理论&#xff1a; 1. 原有模型作为一个特征提取器: 使用一个用ImageNet数据集提前训练&#xff08;pre-trained)好的CNN&#xff0c;再除去最后一层全连接层(fully-connecte…

Apollo进阶课程㉓丨Apollo规划技术详解——Motion Planning with Environment

原文链接&#xff1a;进阶课程㉓丨Apollo规划技术详解——Motion Planning with Environment 当行为层决定要在当前环境中执行的驾驶行为时&#xff0c;其可以是例如巡航-车道&#xff0c;改变车道或右转&#xff0c;所选择的行为必须被转换成路径或轨迹&#xff0c;可由低级反…

Java对象模型-oop和klass

oop-klass模型 Hotspot 虚拟机在内部使用两组类来表示Java的对象和类。 oop(ordinary object pointer)&#xff0c;用来描述对象实例信息。klass&#xff0c;用来描述 Java 类&#xff0c;是虚拟机内部Java类型结构的对等体 。 JVM内部定义了各种oop-klass&#xff0c;在JV…

Apollo进阶课程㉔丨Apollo 规划技术详解——Motion Planning Environment

原文链接&#xff1a;进阶课程㉔丨Apollo 规划技术详解——Motion Planning Environment 自动驾驶汽车核心技术包括环境感知、行为决策、运动规划与控制等方面。其中&#xff0c;行为决策系统、运动规划与控制系统作为无人驾驶汽车的“大脑”&#xff0c;决定了其在不同交通驾…

一步步编写操作系统 26 打开A20地址线

打开A20地址线 还记得实模式下的wrap-around吗&#xff1f;也就是地址回绕。咱们一起来复习一下。实模式下内存访问是采取“段基址:段内偏移地址”的形式&#xff0c;段基址要乘以16后再加上段内偏移地址。实模式下寄存器都是16位的&#xff0c;如果段基址和段内偏移地址都为1…

一步步编写操作系统 27 处理器微架构之流水线简介

了解处理器内部硬件架构&#xff0c;有助于理解软件运行原理&#xff0c;因为这两者本身相辅相成&#xff0c;相互依存。就像枪和狙击手&#xff0c;枪的操作和外形设计都是要根据人体工学&#xff0c;让人不仅操作容易&#xff0c;而且携带也要轻便&#xff0c;做到能随时射出…

Apollo进阶课程㉚丨Apollo ROS背景介绍

原文链接&#xff1a;进阶课程㉚丨Apollo ROS背景介绍 ROS是机器人学习和无人车学习最好Linux平台软件&#xff0c;资源丰厚。无人车的规划、控制算法通常运行在Linux系统上&#xff0c;各个模块通常使用ROS进行连接。 上周阿波君为大家详细介绍了「进阶课程㉙Apollo控制技术详…

一步步编写操作系统 30 cpu的分支预测简介

人在道路的分岔口时要预测哪条路能够到达目的地&#xff0c;面对众多选择时&#xff0c;计算机也一样要抉择&#xff0c;毕竟计算机的运行方式是以人的思路来设计的&#xff0c;计算机中的抉择其实就是人在抉择。 cpu中的指令是在流水线上执行。分支预测&#xff0c;是指当处理…

【HDU - 5492】Find a path(dp,tricks)

题干&#xff1a; Frog fell into a maze. This maze is a rectangle containing NN rows and MM columns. Each grid in this maze contains a number, which is called the magic value. Frog now stays at grid (1, 1), and he wants to go to grid (N, M). For each step,…

Apollo进阶课程㉜丨Apollo ROS原理—1

原文链接&#xff1a;进阶课程㉜丨Apollo ROS原理—1 ROS在开发过程中&#xff0c;基于功能把整个自动驾驶系统分成多个模块&#xff0c;每个模块负责自己消息的接收、处理、发布。当模块需要联调时&#xff0c;通过框架可以把各个模块快速的集成到一起。 上周阿波君为大家详细…

Ubuntu下安装Chrome浏览器的两个方法

一、通过直接下载安装Google Chrome浏览器deb包。 打开Ubuntu终端&#xff0c;以下为32位版本&#xff0c;使用下面的命令。 wget https://dl.google.com/linux/direct/google-chrome-stable_current_i386.deb 以下为64位版本&#xff0c;使用下面的命令。 wget https://dl.…

Apollo进阶课程㉝丨Apollo ROS原理—2

原文链接&#xff1a;进阶课程㉝丨Apollo ROS原理—2 在ROS系统中&#xff0c;从数据的发布到订阅节点之间需要进行数据的拷贝。在数据量很大的情况下&#xff0c;很显然这会影响数据的传输效率。所以Apollo项目对于ROS第一个改造就是通过共享内存来减少数据拷贝&#xff0c;以…

Java 10 常用集合继承关系图

概述 集合类存放的都是对象的引用&#xff0c;而非对象本身&#xff0c;出于表达上的便利&#xff0c;我们称集合中的对象就是指集合中对象的引用。 类图如下&#xff1a; 1、Iterable与Iterator接口之间的区别 我看到好多网上的文章类图里面Collection 是继承Iterator接口&a…

【CodeForces - 673D】Bear and Two Paths(构造,tricks)

题干&#xff1a; Bearland has n cities, numbered 1 through n. Cities are connected via bidirectional roads. Each road connects two distinct cities. No two roads connect the same pair of cities. Bear Limak was once in a city a and he wanted to go to a cit…

Apoll进阶课程㉞丨Apollo ROS原理—3

原文链接&#xff1a;进阶课程㉞丨Apollo ROS原理—3 机器人操作系统(ROS)是一个成熟而灵活的机器人编程框架。ROS提供了所需的工具&#xff0c;可以轻松访问传感器数据&#xff0c;处理数据&#xff0c;并为机器人的电机和其它执行器生成适当的响应。整个ROS系统被设计为在计…

SM3密码杂凑算法原理

目录 1.概述 2、算法描述 2.1 概述 2.2 填充 2.3 迭代压缩 2.3 消息扩展 2.4 压缩函数 2.5 杂凑值 1.概述 SM3是我国采用的一种密码散列函数标准&#xff0c;由国家密码管理局于2010年12月17日发布。相关标准为“GM/T 0004-2012 《SM3密码杂凑算法》”。 在商用密码体…

动手学无人驾驶(1):交通标志识别

今天主要介绍无人驾驶当中深度学习技术的应用。 本文是根据博客专家AdamShan的文章整理而来&#xff0c;在此表示感谢。 关于深度学习的图像分类技术&#xff0c;网上已有很多关于深度学习的课程&#xff08;如吴恩达老师的深度学习专项课程&#xff09;&#xff0c;故本文不对…

《操作系统真象还原》-阅读笔记(上)

第一章 配置bochs&#xff0c;进入bochs simulator后一直是黑屏&#xff0c;原来默认是调试模式&#xff0c;需要输入C&#xff08;continue&#xff09;来让调试继续。 第二章 主讲MBR及进入MBR前的步骤 1.实模式只能访问1MB的内存空间。 2.BIOS在ROM中。 3.开机上电后CS&a…

Apollo进阶课程㉟丨Apollo ROS原理—4

原文链接&#xff1a;进阶课程㉟丨Apollo ROS原理—4 ROS是一个强大而灵活的机器人编程框架&#xff0c;从软件构架的角度说&#xff0c;它是一种基于消息传递通信的分布式多进程框架。 ROS本身是基于消息机制的&#xff0c;可以根据功能把软件拆分成为各个模块&#xff0c;每…