大厂面试题分享
大厂面试题分享
- Redis持久化方式
- AOF优缺点
- RDB优缺点
- 如何保证Redis和Myql的一致性
- 索引下推
- 输入url到浏览器发生了什么
- ReentranLock底层原理
- SpringBoot 的启动流程
Redis持久化方式
Redis提供了两种主要的持久化机制,分别是AOF(Append-Only File)和RDB(Redis Database)
- AOF日志:每执行一条写操作命令,就把该命令以追加的方式写入到一个文件里
- RDB快照:将某一个时刻的内存数据,以二进制的方式写入磁盘
- AOF持久化:是将Redis的操作命令追加到一个只追加文件中。通过记录所有的写操作命令,AOF文件可以重建整个数据集。AOF持久化适用于数据的持久性和故障恢复。
这里以 [set name xiaolin」命令作为例子,Redis 执行了这条命令后,记录在 AOF 日志里的内容如下
Redis 提供了3种写回硬盘的策略,在 Redis.conf配置文件中的 appendfsync 配置项可以有以下3种参数可填:
- Always,这个单词的意思是「总是」,所以它的意思是每次写操作命令执行完后,同步将 AOF 日志数据写回硬盘:
- Everysec,这个单词的意思是「每秒」,所以它的意思是每次写操作命令执行完后,先将命令写入到AOF 文件的内核缓冲区,然后每隔一秒将缓冲区里的内容写回到硬盘;
- No,意味着不由 Redis 控制写回硬盘的时机,转交给操作系统控制写回的时机,也,就是每次写操作命令执行完后,先将命令写入到 AOF 文件的内核缓冲区,再由操作系统决定何时将缓冲区内容写回硬盘。
- RDB持久化:是将Redis的内存数据以快照的方式写入磁盘文件。该机制会在指定的时间间隔或者达到一定的数据变化量时,将当前数据库的数据集合保存到磁盘上的一个二进制文件中。RDB持久化适用于数据备份和恢复,以及冷启动时快速加载数据。
AOF优缺点
优点:
- 提供了更好的数据安全性,因为它默认每接受到一个写命令就会追加到文件末尾,即使Redis服务器宕机,也只会丢失最后一次写入前的数据;
- AOF支持多种同步策略(如everysec、always等),可以根据需要调整数据安全性和性能之间的平衡。同时,AOF文件在Redis启动时可以通过重写机制优化,减少文件体积,加快恢复速度。
缺点:
因为记录了每一个写操作,所以AOF文件通常比RDB文件更大,消耗更多的磁盘空间。并且,频繁的磁盘IO操作(尤其是同步策略设置为always时)可能会对Redis的写入性能造成一定影响。
RDB优缺点
优点:
- RDB通过快照的形式保存某一时刻的数据状态,文件体积小,备份和恢复的速度非常快。
- RDB是在主线程之外通过fork子进程来进行的,不会阻塞服务器处理命令请求,对Redis服务的性能影响较小。最后,由于是定期快照,RDB文件通常比AOF文件小得多。
缺点:
- RDB方式在两次快照之间,如果Redis服务器发生故障,这段时间的数据将会丢失。并且,如果在RDB创建快照到恢复期间有写操作,恢复后的数据可能与故障前的数据不完全一致
如何保证Redis和Myql的一致性
如何保证Redis和Myql的一致性
索引下推
在MySQL5.6之前,当查询使用到复合索引时,MySQL会先根据索引的最左前缀原则,在索引上查找到满足条件的记录的主键或行指针,然后再根据这些主键或行指针到数据表中查询完整的行记录。之后,MySQL再根据WHERE子句中的其他条件对这些行进行过滤。这种方式可能导致大量的数据行被检索出来,但实际上只有很少的行满足WHERE子句中的所有条件。
从MySQL5.6开始引入的一个特性,索引下推通过减少回表的次数来提高数据库的查询效率;
注意:索引下推是为了减少回表而发明的。
索引下推的产生一定围绕着回表,没有回表那就没必要产生索引下推,因为上面也说了索引下推的目的就是减少回表,而不是避免回表。(题外话:避免回表使用索引覆盖——建立覆盖索引)
为了解决这个问题,MySQL 5.6引入了索引下推优化。
索引下推(index condition pushdown )简称ICP,在Mysql5.6的版本上推出,用于优化查询。
需求: 建立了(name,age)组合索引,查询users表中 “名字第一个字是张,年龄为10岁的所有记录”。
SELECT * FROM users WHERE user_name LIKE '张%' AND user_age = 10;
根据最左前缀法则,该语句在搜索索引树的时候,只能匹配到名字第一个字是‘张’的记录,接下来是怎么处理的呢?当然就是从该记录开始,逐个回表,到主键索引上找出相应的记录,再比对 age
这个字段的值是否符合。
图1: 在 (name,age) 索引里面特意去掉了 age 的值,这个过程 InnoDB 并不会去看 age 的值,只是按顺序把“name 第一个字是’张’”的记录一条条取出来回表。因此,需要回表 4 次
MySQL 5.6引入了索引下推优化,可以在索引遍历过程中,对索引中包含的字段先做判断,过滤掉不符合条件的记录,减少回表次数。
图2: InnoDB 在 (name,age) 索引内部就判断了 age 是否等于 10,对于不等于 10 的记录,直接判断并跳过,减少回表次数.
总结
如果没有索引下推优化(或称ICP优化),当进行索引查询时,首先根据索引来查找记录,然后再根据where条件来过滤记录;
在支持ICP优化后,MySQL会在取出索引的同时,判断是否可以进行where条件过滤再进行索引查询,也就是说提前执行where的部分过滤操作,在某些场景下,可以大大减少回表次数,从而提升整体性能。
输入url到浏览器发生了什么
- 用户输入URL(Uniform Resource Locator):用户在浏览器的地址栏输入网址,例如:https://www.example.com。
- DNS解析:浏览器将解析输入的URL,首先检查其是否符合有效URL的规范。接下来,浏览器会通过DNS(Domain Name System)将域名解析为对应的IP地址。DNS解析过程可能涉及本地缓存、本地域名服务器、根域名服务器、顶级域名服务器和权威域名服务器。
- 获取MAC地址:当浏览器得到IP 地址后,数据传输还需要知道目的主机 MAC 地址,因为应用层下发数据给传输层,TCP 协议会指定源端口号和目的端口号,然后下发给网络层。网络层会将本机地址作为源地址,获取的IP 地址作为目的地址。然后将下发给数据链路层,数据链路层的发送需要加入通信双方的 MAC 地址,本机的 MAC 地址作为源 MAC 地址,目的 MAC 地址需要分情况处理。
通过将 IP 地址与本机的子网掩码相结合,可以判断是否与请求主机在同一个子网里,如果在同一个子网里,可以使用 APR 协议获取到目的主机的MAC 地址,如果不在一个子网里,那么请求应该转发给网关,由它代头转发,此时同样可以通过 ARP 协议来获取网关的 MAC地址,此时目的主机的 MAC 地址应该为网关的地址
- 建立TCP连接:在获取到目标服务器的IP地址后,浏览器会向该服务器发送一个TCP连接请求。这个过程通常包括“三次握手”。
- 发送HTTP请求:TCP连接建立后,浏览器会通过这个连接向服务器发送一个HTTP(Hypertext Transfer Protocol)请求。请求中包含了请求方法(例如:GET或POST)、请求的资源路径、HTTP版本、请求头(包含浏览器信息、语言、编码等信息)等。
- 服务器处理请求:服务器接收到浏览器的请求后,会根据请求的资源路径查找对应的资源,并进行相关处理(例如执行服务器脚本、查询数据库等)。
- 服务器响应:服务器处理完请求后,会生成一个HTTP响应,包含HTTP响应状态码(例如200表示成功),响应头(包含响应内容类型、编码等信息)和响应体(请求的资源内容,如HTML文档)。
- 浏览器接收响应:浏览器收到服务器的响应后,会根据响应头信息解析响应体中的内容。如果响应内容是HTML文档,浏览器会进行下一步的解析和渲染
ReentranLock底层原理
简单介绍一下AQS
AQS 核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制 AQS 是用 CLH 队列锁 实现的,
ReentrantLock 主要利用 CAS + AQS队列来实现,通过重写了 AQS 的 tryAcquire 和 tryRelease方法实现的 lock 和 unlock。
- 它的原理是基于AQS(AbstractQueuedSynchronizer ),多个线程抢锁时,如果争抢失败则会进入阻塞队列。等待唤醒,重新尝试加锁。
- AQS的子类有公平锁FairSync和非公平锁NofairSync,ReentrantLock的无参构造默认是非公平锁,有参构造参数是true可以设置成公平锁。
- 公平锁:ReentrantLock调用lock方法,最终会调用Sync类的tryAcquire函数,获取资源。当前线程只有在队列为空或者时队首节点的时候,才能获取资源,否则会被加入到阻塞队列中。
- 非公平锁:调用lock方法时 lock:直接利用CAS尝试将state从 0 改为 1,如果成功,拿锁直接走,如果失败了,执行sync的tryAcquire,不同的是tryAcquire还会调用nofairTryAcquire。在nofairTryAcquire中会再次判断当前锁是否被占用
- 如果当前锁没有占用(state==0),直接再次尝试将state从0 改为 1 如果成功,拿锁直接走。
- 如果当前锁被占用(state!=0):如果被自己占用,则计数器会state++(可重入锁),如果被其他线程占用加入AQS队列
- 公平锁和非公平锁的区别: 非公平锁在调用NonfairSync的lock的时候就会马上进行CAS抢锁,抢不到就和公平锁一样进入tryAcquire方法尝试抢锁,如果发现锁被释放了(state==0),非公平锁马上CAS抢锁,而不会管阻塞队列里是否有线程等待。而公平锁会排队等待。
SpringBoot 的启动流程
构造方法
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {this.resourceLoader = resourceLoader;Assert.notNull(primarySources, "PrimarySources must not be null");this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));// 获取应用类型,根据是否加载Servlet类判断是否是web环境this.webApplicationType = WebApplicationType.deduceFromClasspath();this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));// 读取META-INFO/spring.factories文件,获取对应的ApplicationContextInitializer装配到集合setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));// 设置所有监听器setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));// 推断main函数this.mainApplicationClass = deduceMainApplicationClass();
}
当启动springboot应用程序时,会先创建SpringApplication对象,在其构造方法中进行参数的初始化工作:
- 推断并设置当前web应用类型
- 获取所有初始化器、监听器。扫描所有META-INF/spring.factories文件中分别读取key为ApplicationContextInitializer、ApplicationListener 三种接口类型的实现类,并分别设置对应的属性,将文件中的内容放到缓存对象中,方便后续获取
- 定位main应用程序类
/*** Run the Spring application, creating and refreshing a new* {@link ApplicationContext}.** @param args the application arguments (usually passed from a Java main method)* @return a running {@link ApplicationContext}*/public ConfigurableApplicationContext run(String... args) {// 启动一个秒表计时器,用于统计项目启动时间StopWatch stopWatch = new StopWatch();stopWatch.start();// 创建启动上下文对象即spring根容器DefaultBootstrapContext bootstrapContext = createBootstrapContext();// 定义可配置的应用程序上下文变量ConfigurableApplicationContext context = null;/*** 设置jdk系统属性* headless直译就是无头模式,* headless模式的意思就是明确Springboot要在无鼠键支持的环境中运行,一般程序也都跑在Linux之类的服务器上,无鼠键支持,这里默认值是true;*/configureHeadlessProperty();/*** 获取运行监听器 getRunListeners, 其中也是调用了上面说到的getSpringFactoriesInstances 方法* 从spring.factories中获取配置*/SpringApplicationRunListeners listeners = getRunListeners(args);// 启动监听器listeners.starting(bootstrapContext, this.mainApplicationClass);try {// 包装默认应用程序参数,也就是在命令行下启动应用带的参数,如--server.port=9000ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);///*** 准备环境 prepareEnvironment 是个硬茬,里面主要涉及到* getOrCreateEnvironment、configureEnvironment、configurePropertySources、configureProfiles* environmentPrepared、bindToSpringApplication、attach诸多方法可以在下面的例子中查看*/ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);// 配置忽略的 beanconfigureIgnoreBeanInfo(environment);// 打印 SpringBoot 标志,即启动的时候在控制台的图案logo,可以在src/main/resources下放入名字是banner的自定义文件Banner printedBanner = printBanner(environment);// 创建 IOC 容器context = createApplicationContext();// 设置一个启动器,设置应用程序启动context.setApplicationStartup(this.applicationStartup);// 配置 IOC 容器的基本信息 (spring容器前置处理)prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);/*** 刷新IOC容器* 这里会涉及Spring容器启动、自动装配、创建 WebServer启动Web服务即SpringBoot启动内嵌的 Tomcat*/refreshContext(context);/*** 留给用户自定义容器刷新完成后的处理逻辑* 刷新容器后的扩展接口(spring容器后置处理)*/afterRefresh(context, applicationArguments);// 结束计时器并打印,这就是我们启动后console的显示的时间stopWatch.stop();if (this.logStartupInfo) {// 打印启动完毕的那行日志new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);}// 发布监听应用上下文启动完成(发出启动结束事件),所有的运行监听器调用 started() 方法listeners.started(context);// 执行runner,遍历所有的 runner,调用 run 方法callRunners(context, applicationArguments);} catch (Throwable ex) {// 异常处理,如果run过程发生异常handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);}try {// 所有的运行监听器调用 running() 方法,监听应用上下文listeners.running(context);} catch (Throwable ex) {// 异常处理handleRunFailure(context, ex, null);throw new IllegalStateException(ex);}// 返回最终构建的容器对象return context;}
复制代码
- 在SpringApplication对象创建完成后,开始执行run方法,来完成整个启动,启动过程中最主要有两个方法,一个是prepareContext,另外一个是refreshContext,之前的处理逻辑包含创建定时器、上下文对象的创建、banner的打印等各个准备工作,方便后续来进行调用
- 在prepareContext方法主要完成对上下文对象的初始化操作,包括属性的设置,比如把Environment环境变量设置给Spring容器
- 在refreshContext方法中会进行整个容器刷新过程,会调用Spring中AbstractApplicationContext#refresh方法,其中有13个关键的方法,来完成整个spring IOC容器的创建过程,这里会涉及Spring容器启动、自动装配、创建 WebServer启动Web服务即SpringBoot启动内嵌的 Tomcat
listeners.started(context);
发布容器已启动的事件callRunners(context, applicationArguments);
遍历运行器,并调用run方法
相关文章:

大厂面试题分享
大厂面试题分享 Redis持久化方式AOF优缺点RDB优缺点 如何保证Redis和Myql的一致性索引下推输入url到浏览器发生了什么ReentranLock底层原理SpringBoot 的启动流程 Redis持久化方式 Redis提供了两种主要的持久化机制,分别是AOF(Append-Only File…...

FPGA面试问题整理
1. 逻辑设计中竞争与冒险概念,如何识别和消除? 竞争:在组合逻辑电路中,信号经过多条路径到达输出端,每条路径经过的逻辑门不同存在时差,在信号变化的瞬间存在先后顺序。这种现象叫竞争。 冒险:由…...

3Done学习笔记
一、基本操作 1、旋转视角 使用左下角立方体选择; 右键可以拖动视角; 中间滑轮按住拖动整个舞台界面。 2、平移和旋转 右键选择移动,有两种方式。 第一种选择起始点,按照起始点位置移动到终止点(边、角、中心点…...

AI学习指南深度学习篇-卷积层详解
AI学习指南深度学习篇-卷积层详解 一、引言 随着人工智能技术的不断发展,深度学习作为人工智能领域的热门分支之一,正在逐渐成为各个领域的核心技术。而在深度学习中,卷积神经网络(Convolutional Neural Network,CNN…...

2024年TI杯E题-三子棋游戏装置方案分享-jdk123团队-第二弹 手搓机械臂
第一弹赛题的选择与前期方案的准备 opencv调用摄像头bug的解决 机械臂的组装 采用三个舵机,组成一个三自由度的机械臂。 并且利用电磁吸盘的方式,完成对棋子的抓取工作,后面的事实证明,在预算不足的情况下,队友手搓…...

如何在Java、C、Ruby语言中使用Newscatcher API
Newscatcher 世界实时新闻聚合API 一款强大的数据服务工具,它通过先进的网络爬虫技术,实时从全球超过70,000个新闻源聚合新闻内容。这个API能够提供全面、多角度的新闻报道,包括但不限于标题、作者、发布日期、全文内容以及媒体资源链接。它使…...

集合: Collection的成员方法和相关实现类
Collection: - List(有序【指的是存储和取出的顺序是一致的】且可以发生重复,且有索引的概念) - ArrayList: 底层数据结构是数组,查询快,增删慢,线程不安全的,效率高。 - …...

过滤器与监听器:深入了解 Java Web 开发中的核心概念
在 Java Web 开发中,过滤器(Filter)和监听器(Listener)是两个重要的组件,它们帮助开发者在请求处理的各个阶段进行预处理和后处理。这篇博客将深入探讨这两个概念,并展示它们如何在实际应用中发…...

【Linux学习】动静态库从原理到制作
🍑个人主页:Jupiter. 🚀 所属专栏:Linux从入门到进阶 欢迎大家点赞收藏评论😊 目录 🍑动静态库🐟动静态库的制作与使用🚀生成静态库🔒生成动态库 🦌动态库的查…...

WPF篇(10)-Label标签+TextBlock文字块+TextBox文本框+RichTextBox富文本框
Label标签 Label控件继承于ContentControl控件,它是一个文本标签,如果您想修改它的标签内容,请设置Content属性。我们曾提过ContentControl的Content属性是object类型,意味着Label的Content也是可以设置为任意的引用类型的。 案…...

JavaFX对话框控件-ChoiceDialog
JavaFX对话框控件-ChoiceDialog 常用属性titlecontentTextinitOwnergraphicheaderTextdefaultValuechoicesdialogPane 常用事件显示事件setOnShowing显示事件setOnShown弹框按钮点击 综合案例自定义下拉框内容 与Alert大部分功能类似按钮不可以自定义多一个下拉框 常用属性 …...

一文了解BTC中的二层协议中Nervos network,CKB,RGB++,UTXO stack 之间的关系
注:该内容不构成投资建议,有些内容摘抄其他地方,如侵权,请联系删除。 Nervos network Nervos Network 是一个开源的区块链生态项目,该项目提供一套解决方案来应对区块链扩展性和互操作性的问题。 Nervos Network 成立…...

Oracle(47)如何创建和使用集合?
在PL/SQL中,集合(Collection)是一种复合数据类型,用于存储一组相关的数据项。集合主要有三种类型:关联数组(Associative Arrays)、嵌套表(Nested Tables)和可变数组&…...

SpringIOC和SpringAOC
lombok插件 XML<!-- 加载资源文件 --><context:property-placeholder location"classpath:jdbc.properties"></context:property-placeholder><!-- 注入数据源 --><bean id"dataSource" class"com.mchange.v2.c3p0.ComboP…...

static关键字详解
文章目录 static使用示例static底层原理静态初始化顺序静态与线程安全 static static是Java中的一个关键字,用于定义类级别的成员,类级别的成员是指那些属于整个类,而不是特定对象实例的成员。在Java中,类级别的成员包括静态变量…...

使用 Java RestClient 与 Elasticsearch 进行索引管理的示例
文章目录 准备工作测试连接创建索引查询索引是否存在删除索引总结 在这篇博客中,我将和大家分享如何使用 Java RestClient 与 Elasticsearch 进行简单的索引管理操作。如果你在开发过程中需要对海量数据进行高效搜索和分析,Elasticsearch 可能是个不错的…...

编程-设计模式 10:外观模式
设计模式 10:外观模式 定义与目的 定义:外观模式(Facade Pattern)提供了一个统一的接口,用来访问子系统中的一群接口。它定义了一个高层接口,让子系统更容易使用。目的:简化复杂的子系统的使用…...

非范型ArrayList和泛型List<T>
ArrayList 是 C# 中的一个非泛型集合类,它属于 System.Collections 命名空间。它提供了动态数组的功能,允许你在运行时添加、删除和访问元素。然而,需要注意的是,ArrayList 并不是类型安全的,因为你可以向其中添加任何…...

魔众文库系统v7.0.0版本推荐店铺功能,管理菜单逻辑优化
推荐店铺功能,管理菜单逻辑优化 [新功能] RandomImageProvider 逻辑升级重构,支持更丰富的随机图片生成 [新功能] 资源篮订单参数字段 [新功能] 首页推荐店铺功能,需要在后台 文库系统 → 文库店铺 开启推荐 [系统优化] Grid 快捷编辑请求…...

03、流程控制语句
01、位运算符 一、位运算符:是针对二进制数据(补码)的运算。(0看成false,1看成true) &:按位与——求出两个数字对应的二进制,有0则0 | :按位或——求出两个数字对应的二进制,有1则1 ^ :按位异或 ——求出两个数字对应的二进制,…...

[Android] [解决]Bottom Navigation Views Activity工程带来的fragment底部遮盖的问题
创建了Bottom Navigation Views Activity之后,在fragment_home.xml,加了一个RecyclerView, 后来添加了item之后发现底部会被盖住一部分。 解决:在layout里面加两句: android:paddingBottom"?attr/actionBarSize&…...

Armv8/Armv9架构中的原子性
B2.2 Arm 架构中的原子性 原子性是内存访问的一个特性,描述为原子访问。Arm 架构描述涉及两种类型的原子性:单次复制原子性和多次复制原子性。在 Arm 架构中,内存访问的原子性要求取决于内存类型,以及访问是显式的还是隐式的。有关更多信息,请参见: 单次复制原子性的要求…...

读零信任网络:在不可信网络中构建安全系统15协议和过滤
1. 协议 1.1. IKE/IPSec 1.1.1. 因特网密钥交换协议(Internet Key Exchange,IKE)用于执行IPSec认证和密钥交换 1.1.1.1. 通常以后台守护进程的方式实现,使用预共享密钥或X.509证书来认证对端并创建一个安全会话 1.1.2. IKEv1与IKEv2 1.1.2.1…...

C语言学习笔记 Day11(指针--中2)
Day11 内容梳理: 目录 Chapter 7 指针 7.6 指针 & 函数 (1)形参改变实参的值 (2)字符数组作为函数参数 1)合并字符串 2)删掉字符串中空格 (3)指针作为函数返…...

Golang 并发编程
Golang 并发编程 Goroutine 什么是协程 创建 Goroutine 主 goroutine (main函数)退出后,其它的工作 goroutine 也会自动退出 package mainimport ("fmt""time" )func myFunc() {i : 0for {ifmt.Println("func: …...

【数据结构详解】——选择排序(动图详解)
目录 🕒 1. 直接选择排序🕒 2. 堆排序 🕒 1. 直接选择排序 💡 算法思想:第一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始(末尾)位置…...

杂项命令(笔记)
ifconfig :http://t.csdnimg.cn/gT2AR echo :http://t.csdnimg.cn/6DSoO ps和top的区别 http://t.csdnimg.cn/f1XWt...

代码随想录算法训练营Day38||完全背包问题、leetcode 518. 零钱兑换 II 、 377. 组合总和 Ⅳ 、70. 爬楼梯 (进阶)
一、完全背包问题 相较于01背包,完全背包的显著特征是每个物品可以用无数次,遍历顺序也不需要为了保证每个物品只去一次而倒序遍历。 #include<iostream> #include<vector> using namespace std; int main(){int N,V;cin>>N>>V…...

超越链端:Web3的无边界技术革命
Web3,作为互联网技术的第三代变革,正以其去中心化、开放透明的特性,重新定义着我们的数字生活。在这一背景下,“链端”概念逐渐成为热点,意味着我们不仅仅局限于区块链技术本身,而是探索其在更广泛领域的应…...

127. Go反射基本原理
文章目录 反射基础 - go 的 interface 是怎么存储的?iface 和 eface 的结构体定义(runtime/iface.go):_type 是什么?itab 是什么? 反射对象 - reflect.Type 和 reflect.Value反射三大定律Elem 方法reflect.…...