Hotspot源码解析-第二十章-基础类型的数组类型对象的创建与分配

20.2 基础类型的数组类型创建

该函数的入口在init.cpp->init_globals(),然后再调用universe.cpp->universe2_init()函数,实际执行的函数是Universe::genesis,所以从这开始源码的解析。解析前先了解一下Klass的概念,大家思考个问题:我们编写的java类在JVM中是以何种形式存在的呢?

答:其实他就是以Klass类存在的,Klass类就是java类在jvm中存在的形式。下图为Klass类的继承结构

图20-10
在这里插入图片描述

20.2.1 universe.cpp

20.2.1.1 Universe::genesis
void Universe::genesis(TRAPS) {ResourceMark rm;{ FlagSetting fs(_bootstrapping, true);{ MutexLocker mc(Compile_lock);// 计算Object类的虚函数表占用内存大小,实现细节看`章节20.2.1.2`compute_base_vtable_size();if (!UseSharedSpaces) {// 下面创建8个基础类型的数组类型的对象,细节看`章节20.2.2`_boolArrayKlassObj      = TypeArrayKlass::create_klass(T_BOOLEAN, sizeof(jboolean), CHECK);_charArrayKlassObj      = TypeArrayKlass::create_klass(T_CHAR,    sizeof(jchar),    CHECK);_singleArrayKlassObj    = TypeArrayKlass::create_klass(T_FLOAT,   sizeof(jfloat),   CHECK);_doubleArrayKlassObj    = TypeArrayKlass::create_klass(T_DOUBLE,  sizeof(jdouble),  CHECK);_byteArrayKlassObj      = TypeArrayKlass::create_klass(T_BYTE,    sizeof(jbyte),    CHECK);_shortArrayKlassObj     = TypeArrayKlass::create_klass(T_SHORT,   sizeof(jshort),   CHECK);_intArrayKlassObj       = TypeArrayKlass::create_klass(T_INT,     sizeof(jint),     CHECK);_longArrayKlassObj      = TypeArrayKlass::create_klass(T_LONG,    sizeof(jlong),    CHECK);// 将已创建的数组类型对象都存储到_typeArrayKlassObjs数组中,方便后面使用_typeArrayKlassObjs[T_BOOLEAN] = _boolArrayKlassObj;_typeArrayKlassObjs[T_CHAR]    = _charArrayKlassObj;_typeArrayKlassObjs[T_FLOAT]   = _singleArrayKlassObj;_typeArrayKlassObjs[T_DOUBLE]  = _doubleArrayKlassObj;_typeArrayKlassObjs[T_BYTE]    = _byteArrayKlassObj;_typeArrayKlassObjs[T_SHORT]   = _shortArrayKlassObj;_typeArrayKlassObjs[T_INT]     = _intArrayKlassObj;_typeArrayKlassObjs[T_LONG]    = _longArrayKlassObj;// 根加载器的CLDClassLoaderData* null_cld = ClassLoaderData::the_null_class_loader_data();// 在null_cld中创建一个Array对象的数组,长度为2,元素默认值都为NULL_the_array_interfaces_array = MetadataFactory::new_array<Klass*>(null_cld, 2, NULL, CHECK);// 在null_cld中创建一个Array对象的数组,长度为0_the_empty_int_array        = MetadataFactory::new_array<int>(null_cld, 0, CHECK);// 在null_cld中创建一个Array对象的数组,长度为0_the_empty_short_array      = MetadataFactory::new_array<u2>(null_cld, 0, CHECK);// 在null_cld中创建一个Array对象的数组,长度为0_the_empty_method_array     = MetadataFactory::new_array<Method*>(null_cld, 0, CHECK);// 在null_cld中创建一个Array对象的数组,长度为0_the_empty_klass_array      = MetadataFactory::new_array<Klass*>(null_cld, 0, CHECK);}}// 下面代码逻辑将放在 `章节20.4` 和 `章节20.5` 中讲// 将系统类(System/Object/Class/String等等)表示的字符串都以符号symbol的对象存储到虚拟机的符号表中,关于符号表的介绍,可以看`章节19.2`,为什么要创建符号表呢?因为这些系统类是公共的,后面都会用到的,所以统一放符号表中。这块实现看`章节20.4`vmSymbols::initialize(CHECK);// 各字典(其实就是一个hash table)和基础类的预加载,字典主要用在后续类加载过程中,比如占位符字典表、解析出错表,具体实现细节看`章节20.5`SystemDictionary::initialize(CHECK);// 取出 Object KlassKlass* ok = SystemDictionary::Object_klass();// 把 null 和 -2147483648 作为字符串,预先存储到字符串表_the_null_string            = StringTable::intern("null", CHECK);_the_min_jint_string       = StringTable::intern("-2147483648", CHECK);if (UseSharedSpaces) {// Verify shared interfaces array.assert(_the_array_interfaces_array->at(0) ==SystemDictionary::Cloneable_klass(), "u3");assert(_the_array_interfaces_array->at(1) ==SystemDictionary::Serializable_klass(), "u3");} else {// 设置共享的接口类数组,0号位是Cloneable klass,1号位是Serializable klass_the_array_interfaces_array->at_put(0, SystemDictionary::Cloneable_klass());_the_array_interfaces_array->at_put(1, SystemDictionary::Serializable_klass());}// 设置基础类型的数组类型的超类为Objectinitialize_basic_type_klass(boolArrayKlassObj(), CHECK);initialize_basic_type_klass(charArrayKlassObj(), CHECK);initialize_basic_type_klass(singleArrayKlassObj(), CHECK);initialize_basic_type_klass(doubleArrayKlassObj(), CHECK);initialize_basic_type_klass(byteArrayKlassObj(), CHECK);initialize_basic_type_klass(shortArrayKlassObj(), CHECK);initialize_basic_type_klass(intArrayKlassObj(), CHECK);initialize_basic_type_klass(longArrayKlassObj(), CHECK);} // end of core bootstrapping// Object 数组类型 Klass_objectArrayKlassObj = InstanceKlass::cast(SystemDictionary::Object_klass())->array_klass(1, CHECK);// 将该类型添加到其兄弟类型的列表中_objectArrayKlassObj->append_to_sibling_list();// 针对 jdk1.3、1.4、1.5或更高版本的特殊几个类的加载// Only 1.3 or later has the java.lang.Shutdown class.// Only 1.4 or later has the java.lang.CharSequence interface.// Only 1.5 or later has the java.lang.management.MemoryUsage class.if (JDK_Version::is_partially_initialized()) {uint8_t jdk_version;Klass* k = SystemDictionary::resolve_or_null(vmSymbols::java_lang_management_MemoryUsage(), THREAD);CLEAR_PENDING_EXCEPTION; // ignore exceptionsif (k == NULL) {k = SystemDictionary::resolve_or_null(vmSymbols::java_lang_CharSequence(), THREAD);CLEAR_PENDING_EXCEPTION; // ignore exceptionsif (k == NULL) {k = SystemDictionary::resolve_or_null(vmSymbols::java_lang_Shutdown(), THREAD);CLEAR_PENDING_EXCEPTION; // ignore exceptionsif (k == NULL) {jdk_version = 2;} else {jdk_version = 3;}} else {jdk_version = 4;}} else {jdk_version = 5;}JDK_Version::fully_initialize(jdk_version);}#ifdef ASSERTif (FullGCALot) {  // GC相关,先忽略,后续针对GC专题来讲// Allocate an array of dummy objects.// We'd like these to be at the bottom of the old generation,// so that when we free one and then collect,// (almost) the whole heap moves// and we find out if we actually update all the oops correctly.// But we can't allocate directly in the old generation,// so we allocate wherever, and hope that the first collection// moves these objects to the bottom of the old generation.// We can allocate directly in the permanent generation, so we do.int size;if (UseConcMarkSweepGC) {warning("Using +FullGCALot with concurrent mark sweep gc ""will not force all objects to relocate");size = FullGCALotDummies;} else {size = FullGCALotDummies * 2;}objArrayOop    naked_array = oopFactory::new_objArray(SystemDictionary::Object_klass(), size, CHECK);objArrayHandle dummy_array(THREAD, naked_array);int i = 0;while (i < size) {// Allocate dummy in old generationoop dummy = InstanceKlass::cast(SystemDictionary::Object_klass())->allocate_instance(CHECK);dummy_array->obj_at_put(i++, dummy);}{// Only modify the global variable inside the mutex.// If we had a race to here, the other dummy_array instances// and their elements just get dropped on the floor, which is fine.MutexLocker ml(FullGCALot_lock);if (_fullgc_alot_dummy_array == NULL) {_fullgc_alot_dummy_array = dummy_array();}}assert(i == _fullgc_alot_dummy_array->length(), "just checking");}#endif// Initialize dependency array for null class loaderClassLoaderData::the_null_class_loader_data()->init_dependencies(CHECK);}
20.2.1.2 Universe::compute_base_vtable_size
void Universe::compute_base_vtable_size() {// 计算Object类的虚函数表占用内存大小_base_vtable_size = ClassLoader::compute_Object_vtable();
}

classLoader.cpp

在Java中所有的类都是默认继承自Object类的,所以这里要计算Object类的初始 vtable的大小,默认JDK_1_2_Object_vtable_size = 5,为什么是5呢?因为Object提供了5个可使用和继承的方法,如下:

public native int hashCode();

public boolean equals(Object obj) {
return (this == obj);
}

protected native Object clone() throws CloneNotSupportedException;

public String toString() {
return getClass().getName() + “@” + Integer.toHexString(hashCode());
}

protected void finalize() throws Throwable { }

int ClassLoader::compute_Object_vtable() {int JDK_1_2_Object_vtable_size = 5; // 5个// 占用内存为5 * 每个的大小(vtableEntry::size())return JDK_1_2_Object_vtable_size * vtableEntry::size();
}

20.2.2 typeArrayKlass.hpp/cpp

20.2.2.1 create_klass
const char* TypeArrayKlass::external_name(BasicType type) {switch (type) {// 看着下面这些[开头的类型是不是很熟悉,在Java中打印基础类型的数组对象时,就会出现下面符号中的一种,也就是虚拟机层面给各基础类型数组的类型简称case T_BOOLEAN: return "[Z";case T_CHAR:    return "[C";case T_FLOAT:   return "[F";case T_DOUBLE:  return "[D";case T_BYTE:    return "[B";case T_SHORT:   return "[S";case T_INT:     return "[I";case T_LONG:    return "[J";default: ShouldNotReachHere();}return NULL;
}static inline Klass* create_klass(BasicType type, int scale, TRAPS) {// 创建type类型的基础类型的数组类型TypeArrayKlass* tak = create_klass(type, external_name(type), CHECK_NULL);assert(scale == (1 << tak->log2_element_size()), "scale must check out");return tak;}TypeArrayKlass* TypeArrayKlass::create_klass(BasicType type,const char* name_str, TRAPS) {Symbol* sym = NULL;if (name_str != NULL) {// 把name_str先创建一个符号,并加入到符号表SymbolTable中,看到符号表是不是眼熟,可以回来看`章节19.2`sym = SymbolTable::new_permanent_symbol(name_str, CHECK_NULL);}// 拿到null加载器的CLD,也就是Java中所谓的根加载器ClassLoaderData* null_loader_data = ClassLoaderData::the_null_class_loader_data();// 给数组类型的Klass分配内存,看`章节20.2.2.2`TypeArrayKlass* ak = TypeArrayKlass::allocate(null_loader_data, type, sym, CHECK_NULL);// 将当前创建的klass ak,添加到根加载器的CLD的klasses链表中null_loader_data->add_class(ak);// Call complete_create_array_klass after all instance variables have been initialized./** 通过Java我们知道,每个类都有一个对应的java.lang.Class对象,同理在JVM中也需要有这么一个对象,在JVM中,* 该对象用InstanceMirrorKlass的对象来表示,所以这一步就是创建该数组类型的InstanceMirrorKlass对象。* 由`图20-10`,得到InstanceMirrorKlass是继承自InstanceKlass的,并且在InstanceMirrorKlass中定义了很多* 操作InstanceKlass类对象的函数,所以在Java中,java.lang.Class可以用来做反射处理。*/complete_create_array_klass(ak, ak->super(), CHECK_NULL);return ak;
}
20.2.2.2 allocate
TypeArrayKlass* TypeArrayKlass::allocate(ClassLoaderData* loader_data, BasicType type, Symbol* name, TRAPS) {assert(TypeArrayKlass::header_size() <= InstanceKlass::header_size(),"array klasses must be same size as InstanceKlass");// 先计算要分配的内存大小int size = ArrayKlass::static_size(TypeArrayKlass::header_size());// 在元空间中分配内存,new 操作看`章节20.2.3.1`,TypeArrayKlass 构造函数实现看`章节20.2.2.3`return new (loader_data, size, THREAD) TypeArrayKlass(type, name);
}
20.2.2.3 TypeArrayKlass
TypeArrayKlass::TypeArrayKlass(BasicType type, Symbol* name) : ArrayKlass(name) { // 调用父类构造函数set_layout_helper(array_layout_helper(type));assert(oop_is_array(), "sanity");assert(oop_is_typeArray(), "sanity");// 设置该数组的最大长度set_max_length(arrayOopDesc::max_array_length(type));assert(size() >= TypeArrayKlass::header_size(), "bad size");// 设置该数组类的CLD为根加载器Data,也就是说基础数组类型的加载都是通过根加载器去加载的set_class_loader_data(ClassLoaderData::the_null_class_loader_data());
}

父类ArrayKlass构造函数

ArrayKlass::ArrayKlass(Symbol* name) {set_name(name);// 设置名字// 这里默认设置超类为NULLset_super(Universe::is_bootstrapping() ? (Klass*)NULL : SystemDictionary::Object_klass());set_layout_helper(Klass::_lh_neutral_value);set_dimension(1);set_higher_dimension(NULL);set_lower_dimension(NULL);set_component_mirror(NULL);// Object类的vtable大小,这个在`章节20.2.1.2`中有介绍int vtable_size = Universe::base_vtable_size();set_vtable_length(vtable_size);set_is_cloneable(); // All arrays are considered to be cloneable (See JLS 20.1.5)JFR_ONLY(INIT_ID(this);)
}

20.2.3 klass.cpp

20.2.3.1 new创建并分配内存
void* Klass::operator new(size_t size, ClassLoaderData* loader_data, size_t word_size, TRAPS) throw() {// 细节看`章节20.2.4.1`return Metaspace::allocate(loader_data, word_size, /*read_only*/false,MetaspaceObj::ClassType, THREAD);
}

20.2.4 metaspace.cpp

20.2.4.1 allocate
MetaWord* Metaspace::allocate(ClassLoaderData* loader_data, size_t word_size,bool read_only, MetaspaceObj::Type type, TRAPS) {if (HAS_PENDING_EXCEPTION) {assert(false, "Should not allocate with exception pending");return NULL;  // caller does a CHECK_NULL too}assert(loader_data != NULL, "Should never pass around a NULL loader_data. ""ClassLoaderData::the_null_class_loader_data() should have been used.");// DumpSharedSpaces表示多Java进程共享,这里不涉及多Java进程共享,这一步不会走if (DumpSharedSpaces) {assert(type > MetaspaceObj::UnknownType && type < MetaspaceObj::_number_of_types, "sanity");Metaspace* space = read_only ? loader_data->ro_metaspace() : loader_data->rw_metaspace();MetaWord* result = space->allocate(word_size, NonClassType);if (result == NULL) {report_out_of_shared_space(read_only ? SharedReadOnly : SharedReadWrite);}if (PrintSharedSpaces) {space->record_allocation(result, type, space->vsm()->get_raw_word_size(word_size));}// Zero initialize.Copy::fill_to_aligned_words((HeapWord*)result, word_size, 0);return result;}// 确定要分配的内存内容的类型,类的创建都是ClassTypeMetadataType mdtype = (type == MetaspaceObj::ClassType) ? ClassType : NonClassType;// 尝试分配元数据。loader_data->metaspace_non_null()函数(实现细节看`章节20.3`)可以拿到Metaspace的指针指向的元空间对象,然后再通过allocate函数在元空间中分配内存,allocate分配看后面描述MetaWord* result = loader_data->metaspace_non_null()->allocate(word_size, mdtype);if (result == NULL) {tracer()->report_metaspace_allocation_failure(loader_data, word_size, type, mdtype);// 分配失败,可能是内存不足,所以启动GCif (is_init_completed()) {// 尝试GC 清空一些内存result = Universe::heap()->collector_policy()->satisfy_failed_metadata_allocation(loader_data, word_size, mdtype);}}// 启动GC 后还是失败,那就直接报告oom错误if (result == NULL) {report_metadata_oome(loader_data, word_size, type, mdtype, CHECK_NULL);}// Zero initialize.Copy::fill_to_aligned_words((HeapWord*)result, word_size, 0);// 返回分配后的内存首地址return result;
}

allocate 从SpaceManager中分配内存

MetaWord* Metaspace::allocate(size_t word_size, MetadataType mdtype) {// class_vsm() 和 vsm() 取出的都是SpaceManager对象,直接往下看if (is_class_space_allocation(mdtype)) {return  class_vsm()->allocate(word_size);} else {return  vsm()->allocate(word_size);}
}

从SpaceManager中分配

MetaWord* SpaceManager::allocate(size_t word_size) {MutexLockerEx cl(lock(), Mutex::_no_safepoint_check_flag);size_t raw_word_size = get_raw_word_size(word_size);BlockFreelist* fl =  block_freelists();MetaWord* p = NULL;// Allocation from the dictionary is expensive in the sense that// the dictionary has to be searched for a size.  Don't allocate// from the dictionary until it starts to get fat.  Is this// a reasonable policy?  Maybe an skinny dictionary is fast enough// for allocations.  Do some profiling.  JJJif (fl->total_size() > allocation_from_dictionary_limit) {p = fl->get_block(raw_word_size);}if (p == NULL) {// 直接看这一行p = allocate_work(raw_word_size);}return p;
}

正式分配

MetaWord* SpaceManager::allocate_work(size_t word_size) {assert_lock_strong(_lock);
#ifdef ASSERTif (Metadebug::test_metadata_failure()) {return NULL;}
#endif// Is there space in the current chunk?MetaWord* result = NULL;// For DumpSharedSpaces, only allocate out of the current chunk which is// never null because we gave it the size we wanted.   Caller reports out// of memory if this returns null.if (DumpSharedSpaces) {assert(current_chunk() != NULL, "should never happen");inc_used_metrics(word_size);return current_chunk()->allocate(word_size); // caller handles null result}// current_chunk() 函数拿到当前在用的chunk,这个chunk就是`章节20.3.2`中 initialize_first_chunk()函数实现里面设置的,然后再通过allocate分配,怎么分配的呢,继续往下看if (current_chunk() != NULL) {result = current_chunk()->allocate(word_size);}// 分配不成功,肯定是容量不够了,这一步就是扩容if (result == NULL) {result = grow_and_allocate(word_size);}if (result != NULL) {inc_used_metrics(word_size);assert(result != (MetaWord*) chunks_in_use(MediumIndex),"Head of the list is being allocated");}return result;
}

从Metachunk中分配

看代码实现,非常简单,就是把_top上移word_size个字,读者只需把它理解成一个带刻度的水缸,往里面加水时,刻度值上升,就这么简单。

MetaWord* Metachunk::allocate(size_t word_size) {MetaWord* result = NULL;// If available, bump the pointer to allocate.if (free_word_size() >= word_size) {result = _top;_top = _top + word_size;}return result;
}

20.3 创建和分配Metaspace

为什么要创建Metaspace呢?

答:在元空间分配元数据内存时都需要元空间对象Metaspace实例来操作?在第十八章中虽然有对元空间的创建与分配讲解,但是并没有对元空间实际存储数据逻辑和真正的内存空间分配描述,第十八章只是描述了对元空间实际分配元数据内存前的一些准备工作,比如划出一块大的空间作为元空间、再将大空间划成一块块小的chunk、再通过链表管理起来等等。这一章节就是讲具体的元数据的分配。

20.3.1 classLoaderData.cpp

20.3.1.1 metaspace_non_null
Metaspace* ClassLoaderData::metaspace_non_null() {assert(!DumpSharedSpaces, "wrong metaspace!");// If the metaspace has not been allocated, create a new one.  Might want// to create smaller arena for Reflection class loaders also.// The reason for the delayed allocation is because some class loaders are// simply for delegating with no metadata of their own.if (_metaspace == NULL) {MutexLockerEx ml(metaspace_lock(),  Mutex::_no_safepoint_check_flag);// Check again if metaspace has been allocated while we were getting this lock.if (_metaspace != NULL) {return _metaspace;}if (this == the_null_class_loader_data()) {assert (class_loader() == NULL, "Must be");// 直接看这一行,Metaspace的创建看`章节20.3.2`set_metaspace(new Metaspace(_metaspace_lock, Metaspace::BootMetaspaceType));} else if (is_anonymous()) {if (TraceClassLoaderData && Verbose && class_loader() != NULL) {tty->print_cr("is_anonymous: %s", class_loader()->klass()->internal_name());}set_metaspace(new Metaspace(_metaspace_lock, Metaspace::AnonymousMetaspaceType));} else if (class_loader()->is_a(SystemDictionary::reflect_DelegatingClassLoader_klass())) {if (TraceClassLoaderData && Verbose && class_loader() != NULL) {tty->print_cr("is_reflection: %s", class_loader()->klass()->internal_name());}set_metaspace(new Metaspace(_metaspace_lock, Metaspace::ReflectionMetaspaceType));} else {set_metaspace(new Metaspace(_metaspace_lock, Metaspace::StandardMetaspaceType));}}// 把创建后的metaspace对象实例返回return _metaspace;
}

20.3.2 metaspace.cpp

20.3.2.1 构造函数和初始始化

构造函数

Metaspace::Metaspace(Mutex* lock, MetaspaceType type) {initialize(lock, type);
}

初始化函数

void Metaspace::initialize(Mutex* lock, MetaspaceType type) {verify_global_initialization();// 创建 SpaceManager 对象,分配元数据(主要是符号、字符串等)需要用到,本章节的后面有对这对象创建的讲解_vsm = new SpaceManager(NonClassType, lock);if (using_class_space()) {// 创建 SpaceManager 对象,分配类时需要用到_class_vsm = new SpaceManager(ClassType, lock);} else {_class_vsm = NULL;}MutexLockerEx cl(SpaceManager::expand_lock(), Mutex::_no_safepoint_check_flag);// 这一步就是从`第十八章`中分配的空闲chunk链表中找出一个适合当前分配大小(前面讲过,MetaChunk块有三种大小,取一个适合自己的就行)的MetaChunk块对象,并把它添加到 SpaceManager 对象(在这里表示 _vsm)来管理initialize_first_chunk(type, NonClassType);// 同上,这是取出一块MetaChunk来存放类数据,并用 SpaceManager 对象(在这里表示 _class_vsm)管理if (using_class_space()) {initialize_first_chunk(type, ClassType);}_alloc_record_head = NULL;_alloc_record_tail = NULL;
}

创建SpaceManager对象

SpaceManager对象就是管理元数据分配在哪个chunk,这个对象的创建很简单,就是做一些初始值的赋值

SpaceManager::SpaceManager(Metaspace::MetadataType mdtype,Mutex* lock) :_mdtype(mdtype),_allocated_blocks_words(0),_allocated_chunks_words(0),_allocated_chunks_count(0),_lock(lock)
{initialize();
}void SpaceManager::initialize() {Metadebug::init_allocation_fail_alot_count();for (ChunkIndex i = ZeroIndex; i < NumberOfInUseLists; i = next_chunk_index(i)) {_chunks_in_use[i] = NULL;  //  把所有chunk已用状态设置为null}_current_chunk = NULL;if (TraceMetadataChunkAllocation && Verbose) {gclog_or_tty->print_cr("SpaceManager(): " PTR_FORMAT, this);}
}

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

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

相关文章

软件测试面试200问(含答案)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号【互联网杂货铺】&#xff0c;回复 1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 1、B/S架构和C/S架构区别 B/S 只需要有操作系统和浏览器就行&a…

阿里云云原生助力安永创新驱动力实践探索

云原生正在成为新质生产力变革的核心要素和企业创新的数字基础设施。2023 年 12 月 1 日&#xff0c;由中国信通院举办的“2023 云原生产业大会”在北京召开。在大会“阿里云云原生”专场&#xff0c;安永科技咨询合伙人王祺分享了对云原生市场的总览及趋势洞见&#xff0c;及安…

自动驾驶轨迹规划之碰撞检测(三)

欢迎大家关注我的B站&#xff1a; 偷吃薯片的Zheng同学的个人空间-偷吃薯片的Zheng同学个人主页-哔哩哔哩视频 (bilibili.com) 目录 1.基于圆覆盖 2.BVH 3.MATLAB自动驾驶工具箱 4 ROS内置的模型 自动驾驶轨迹规划之碰撞检测&#xff08;一&#xff09;-CSDN博客 自动驾…

ubuntu qt 运行命令行

文章目录 1.C实现2.python实现 1.C实现 下面是封装好的C头文件&#xff0c;直接调用run_cmd_fun()即可。 #ifndef GET_CMD_H #define GET_CMD_H#endif // GET_CMD_H #include <iostream> #include<QString> using namespace std;//system("gnome-terminal -…

【JavaEE进阶】 关于应用分层

文章目录 &#x1f38b;序言&#x1f343;什么是应⽤分层&#x1f38d;为什么需要应⽤分层&#x1f340;如何分层(三层架构)&#x1f384;MVC和三层架构的区别和联系&#x1f333;什么是高内聚低耦合⭕总结 &#x1f38b;序言 在我们进行项目开发时我们如果一股脑将所有代码都…

嵌入式软件分层的思想

文章目录 一、分层的目的二、分层逻辑图三、分层的架构图四、分层的优缺点五、举例六、 优化 一、分层的目的 “高内聚&#xff0c;低耦合的思想”&#xff0c;表示在设计和开发软件系统时&#xff0c;应该使模块之间的关系更加紧密&#xff0c;同时避免模块之间的依赖性过于紧…

【JavaEE】文件操作: File 类的用法和 InputStream, OutputStream 的用法

目录 1. File 概述 1.1 File的属性 1.2 File的构造方法 1.3 File的方法 2.读文件 2.1 InputStream 概述 2.2 FileInputStream 概述 2.3 正确打开和关闭文件的方式 2.4 不同方式读取文件代码示例 2.4 另一种方法:利用 Scanner 进行字符读取 3.写文件 3.1 OutputStre…

如何在苹果手机上进行文件管理

摘要 苹果手机没有像安卓系统那样内置文件管理器&#xff0c;但是可以通过使用克魔开发助手来实现强大的文件管理功能。本文介绍了如何使用克魔开发助手在电脑上管理和传输苹果手机的文件。 引言 很多朋友都在使用苹果手机&#xff0c;但是当需要查看手机中的文件时&#xf…

version-polling一款用于实时检测 web 应用更新的 JavaScript 库

为了解决后端部署之后&#xff0c;如何通知用户系统有新版本&#xff0c;并引导用户刷新页面以加载最新资源的问题。 实现原理 1.使用 Web Worker API 在浏览器后台轮询请求页面&#xff0c;不会影响主线程运行。 2.命中协商缓存&#xff0c;对比本地和服务器请求响应头etag字…

取消lodash.throttle中的默认执行完最后一次函数

我的场景: 我有一个列表,我考虑用户连续点击删除的情况&#xff0c;如果用户连续点击&#xff0c;可能会导致数据库中的数据被删除了&#xff0c;但是我还需要刷新数据列表才能反应到页面上&#xff0c;可是这时候用户又点击了同一条数据的删除按钮多次&#xff0c;导致发起了…

第二讲_HarmonyOS应用创建和运行

HarmonyOS应用创建和运行 1. 创建一个HarmonyOS应用2. 运行新项目2.1 创建本地模拟器2.2 启动本地模拟器2.3 在本地模拟器运行项目 1. 创建一个HarmonyOS应用 打开DevEco Studio&#xff0c;在欢迎页单击Create Project&#xff0c;创建一个新工程。 选择创建Application应用。…

服务器运维小技巧(一)——如何进行远程协助

服务器运维中经常会遇到一些疑难问题&#xff0c;需要安全工程师&#xff0c;或者其他大神远程协助。很多人会选择使用todesk或者向日葵等一些远控软件。但使用这些软件会存在诸多问题&#xff1a; 双方都需要安装软件&#xff0c;太麻烦需要把服务器的公钥/密码交给对方不知道…

【计算机硬件】2、指令系统、存储系统和缓存

文章目录 指令系统计算机指令的组成计算机指令执行过程指令的寻址方式&#xff08;怎么样找到操作数&#xff1f;&#xff09;1、顺序寻址2、跳跃寻址 指令操作数的寻址方式&#xff08;怎么样找到操作数&#xff1f;&#xff09;1、立即寻址方式2、直接寻址方式3、间接寻址方式…

鸿蒙开发-UI-布局-弹性布局

地方 鸿蒙开发-UI-布局 鸿蒙开发-UI-布局-线性布局 鸿蒙开发-UI-布局-层叠布局 文章目录 前言 一、基本概念 二、布局方向 1、主轴为水平方向 2、主轴为垂直方向 三、布局换行 四、对齐方式 1、主轴对齐方式 2、交叉轴对齐方式 2.1、容器组件设置交叉轴对齐 2.2、子组件设置交叉…

常见框架漏洞

1.什么是框架 Web框架(Web framework)或者叫做Web应用框架(Web application framework)&#xff0c;是用于进行Web开发的一套软件架构。大多数的Web框架提供了一套开发和部署网站的方式。为Web的行为提供了一套支持的方法。使用Web框架&#xff0c;很多的业务逻辑外的功能不需…

RT-Thread 瑞萨 智能家居网络开发:RA6M3 HMI Board 以太网+GUI技术实践

以太网HMI线下培训-环境准备 R A 6 M 3 H M I − B o a r d \textcolor{#4183c4}{RA6M3 HMI-Board} RA6M3HMI−Board 本次培训将使用&#xff0c;由RT-Thread与瑞萨电子及LVGL官方合作推出的一款高性价比图形评估套件。它采用了瑞萨电子的高性能RA6M3芯片&#xff0c;具备2D的…

【C语言编程之旅 3】刷题篇-函数

第一题 解析 数组的下标是从0开始的。 需要注意的是D&#xff1a;int a[] {1,2,3},数组可以通过初始化确定大小。 第二题 解析 B,数组的大小必须是常量 第三题 解析 /* 解题思路&#xff1a;本题目主要是考察函数的声明定义&#xff0c;题目比较简单&#xff0c;请参考以…

如何在Linux运行RStudio Server并实现Web浏览器远程访问

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 前言1. 安装RStudio Server2. 本地访问3. Linux 安装cpolar4. 配置RStudio server公网访问地址5. …

腊八节公益活动总结

春雨公益志愿服务队联合水城路社区红帆支部服务队于腊八节开展了公益活动&#xff0c;为环卫工人送上了热气腾腾的八宝粥以及新年福字。此次活动旨在为环卫工人营造温馨、充满爱心的氛围&#xff0c;让他们在冬日里感受到社会的温暖和关爱。 活动当天&#xff0c;志愿者们早早地…

C语言之【函数】篇章以及例题分析

文章目录 前言一、函数是什么&#xff1f;二、C语言中函数的分类1、库函数2、自定义函数 三、函数的参数1、实际参数&#xff08;实参&#xff09;2、形式参数&#xff08;形参&#xff09; 四、函数的调用1、传值调用2、传址调用3、专项练习3.1 素数判断3.2 闰年判断3.3 二分查…