当前位置: 首页 > news >正文

Android面试指南(七)

目录

一、JVM概览

二、JVM面试题精选

2.1、Android平台的虚拟机是基于栈的吗?

2.2、为什么dex文件比class文件更适合移动端?

2.3、能否自己实现一个叫java.lang.Object的类?

2.4、所有被new出来的实例,都是放在堆中的吗?

2.5、GC为什么会导致应用程序卡顿

2.6、双重检测的单例为什么要加volatile关键字?

一、JVM概览

JVM的概念:Java虚拟机是一种规范,它是个抽象概念

  • 并不是真正的“机器”,而是由软件实现
  • 可以有多种不同的实现方式,比如HotSpot VM(Sun JDK、Open JDK)

JVM的职责:在硬件上运行JVM语言(可以编译成Java字节码的语言)

  • 执行字节码指令
  • 加载字节码中的Class结构
  • 分配和回收代码运行时的内存

二、JVM面试题精选

2.1、Android平台的虚拟机是基于栈的吗?

 推荐阅读:带你认识JVM

Android平台的虚拟机并非基于栈结构,Android采用基于寄存器的虚拟机设计。

①、Java运行时数据区,结构如下:

  • 堆:存放对象实例数据,垃圾回收主要作用域
  • 方法区:存储加载的类信息,线程共享
  • 线程私有区域:
    • 程序计数器:记录字节码执行位置
    • 虚拟机栈:管理方法调用状态
    • 本地方法栈:支持Native方法执行

②、数据结构“栈”简介:

栈的核心特性为先进后出(LIFO),典型应用场景包括:

  • 方法调用栈:后调用的方法先结束执行
  • Android Activity管理:栈顶Activity唯一可见,关闭后下层Activity才显示
  • 虚拟机栈:通过栈帧记录方法执行状态,每个栈帧对应一个方法调用

③、运行时栈帧三要素:

  • 局部变量表:存储方法参数与局部变量(非静态方法包含this引用)
  • 操作数栈:编译期确定容量,用于字节码执行时的中间结果存储
  • 方法出口:记录返回地址与返回值
  • 注意:题目中"基于栈的虚拟机"特指操作数栈而非虚拟机栈

④、基于栈的虚拟机编译后特征:

  • Java代码扩展为字节码
  • 字节码单元长度:1-2字节不等(如iadd为单字节,bipush为双字节)
  • 字节码包括 机器码(二进制指令)和助记符(人类可读符号)

⑤、Dalvik虚拟寄存器的核心设计:

  • 虚拟寄存器替代操作数栈
  • 插槽数量由编译期计算确定
  • 保留程序计数器功能

编译器优化机制:

  • 寄存器复用:无后续引用的变量可被覆盖
  • 内存节约:动态调整插槽数量确保最少资源占用

⑥、两种虚拟机的设计对比

基于栈的虚拟机设计具有以下特点:

  • 压缩代码体积:JVM设计初衷要求跨平台并支持嵌入式设备,早期设备内存和存储空间有限,需减少代码体积。同时支持远程传输字节码,压缩体积可降低传输开销。
  • 字节码实现简单:生成字节码过程更简单,编译器实现难度降低。操作时无需考虑寄存器绝对地址,仅需通过入栈出栈完成数据操作。
  • 指令简洁性:单条指令体积小,例如操作栈元素无需指定寄存器地址。
  • 高可移植性:基于寄存器的虚拟机需将虚拟寄存器映射到物理寄存器,增加移植难度;而栈式设计无需考虑平台寄存器差异,指令通用性强。

基于寄存器的虚拟机设计具有以下特点:

  • 性能与内存效率:指令条数少,数据移动和临时结果存放次数减少,虚拟寄存器可映射到物理寄存器
  • 无需考虑可移植性:程序统一运行于Android系统,与栈式设计跨平台需求形成对比
  • 代码体积优化:通过DEX文件格式压缩字节码

2.2、为什么dex文件比class文件更适合移动端?

推荐阅读:class文件与dex文件解析

1)、class文件

class文件物理结构为连续字节单元,存储字节码指令及数据。每个单元对应特定指令或信息,如aaa()这个方法的字节码指令。

class文件逻辑结构包含以下部分:

  • 文件头:存储文件基础信息
  • 常量池:作为资源仓库,存储字符串、符号引用等
  • 类信息:定义类的元数据
  • 字段表:记录成员变量信息
  • 方法表:存储方法定义及字节码
  • 属性表:保存注解、源文件名等附加信息

①、文件头

文件头包含三个字段:

  • 魔术字(4字节):标识合法class文件(固定为0xCAFEBABE)
  • 次版本号(2字节)
  • 主版本号(2字节)

②、常量池

常量池首字段为容量计数器(2字节),数值为实际常量数+1。后续存储cp_info类型常量,包括字面量(字符串、数值)和符号引用(类名、方法描述符)。

cp_info存储数据类型分为两类:

  • 字面量:代码中直接量(字符串、final常量值)
  • 符号引用:类/接口全限定名、字段/方法描述符

常量池中的字符串存储通过两级引用实现:

  • CONSTANT_String_info(tag=8):仅存储指向UTF-8常量的索引
  • CONSTANT_Utf8_info(tag=1):实际存储字符串值(含长度字段及字节数组)

③、类信息

类信息包含:

  • 访问标志(2字节):记录public/final等修饰符
  • this_class/super_class(各2字节):指向常量池中的类定义
  • 接口计数及引用:记录实现接口数量及常量池索引

④、字段表

字段表结构:

  • 字段计数器
  • field_info数组:含字段名、类型(均引用常量池)、修饰符及属性(如final常量的初始化值)

⑤、方法表

方法表核心结构:

  • code属性:包含操作数栈深度(stack)、局部变量表大小(locals)及字节码指令
  • LineNumberTable:映射字节码与源码行号
  • LocalVariableTable:记录局部变量生命周期(start/length)、名称及类型

⑥、属性信息

属性信息的结构由属性名和属性值组成。示例中包含三个属性:

  • SourceFile:指向编写代码的源文件JAVA文件
  • 注解属性:标记类为已过时
  • 运行时可见的圆柱体:记录类上添加的圆柱体注解

2)、dex文件

dex文件通过Dx工具将多个class文件合并生成。与class文件对比差异如下:

  • class文件结构:单个JAR包包含多个独立class文件
  • dex文件结构:单个APK/AR文件仅含1-3个dex文件

dex文件结构分为三部分:

  • 文件头:包含元数据信息
  • 索引区:包含string/tab/proto/field/method等索引区域
  • 数据区:存储类定义及实际数据

①、文件头:

dex文件头结构包含以下关键字段:

  • 魔术和版本号:占8字节
  • 校验码:检测文件损坏或篡改
  • 签名值:采用SHA-1签名验证文件完整性
  • 文件总大小/头大小:定位索引区起始位置
  • 字节序标记:标识高位/低位字节排列方式
  • size/off字段:精确划分文件逻辑区域

②、索引区数据结构

索引区通过偏移量实现数据定位,核心结构包括:

string_ids:

  • 字符串索引:4字节偏移量字段,指向数据区字符串实际存储位置

type_ids:

类型索引:4字节字段,关联字符串索引区的类型名称

field_ids:

  • 字段索引:包含三个关键部分
  • class idx/type idx:指向类型索引
  • name索引:关联字符串索引区的字段名

proto_ids:

  • 方法原型索引:包含三部分结构
  • 短描述符:关联字符串索引
  • 返回值类型:指向类型索引
  • 参数类型偏移量:定位参数列表
  • 作用:实现参数/返回值相同方法的原型复用

3)、索引区和数据区

索引区存储各类索引指针,数据区存储实际值。两者共同实现类似class文件常量池的资源管理功能。

4)、class文件和dex文件对比

对比维度

class文件

dex文件

常量池机制

每个类独立常量池导致代码冗余

全局数据区+索引实现常量复用

文件组织方式

每个类独立文件

多类合并为1-3个文件

字节码架构

基于栈的指令集

基于虚拟寄存器的指令集

优化效果

无压缩优势

减少安装包体积及IO操作开销

2.3、能否自己实现一个叫java.lang.Object的类?

推荐阅读:带你认识ClassLoader

可以通过编译,但不会被加载。本题考察自定义类加载器与双亲委派模型的关系。核心考点在于理解class loader的加载机制以及双亲委派模型的实现原理。

1)、字节码加载

字节码通过类加载器加载到方法区,形成类的运行时数据结构。

2)、方法区

①、方法区的作用

方法区存储类的元信息,包括常量池、字段、方法和属性等逻辑结构。

②、方法区的实现

方法区在JAVA 8前实现为永久代,与堆共享线程但无垃圾回收机制;JAVA 8后改为元空间,直接分配于本地内存且无容量限制。

3)、Java双亲委派模型

类加载器层级

加载内容

关联关系

启动类加载器

jre/lib核心类库(如rt.jar)

无父加载器

扩展类加载器

jre/lib/ext目录库

父加载器为启动类加载器

应用类加载器

用户编写的代码

父加载器为扩展类加载器

自定义类加载器

额外class文件

父加载器为应用类加载器

4)、Android双亲委派模型

类加载器层级

加载内容

关联关系

BootClassLoader

framework层class

无父加载器

PathClassLoader

用户代码编译的dex文件

父加载器为BootClassLoader

DexClassLoader

自定义dex文件

父加载器为PathClassLoader

5)、ClassLoader的源码

findClass方法由子类实现具体加载逻辑,loadClass方法通过递归委派父加载器优先加载,未命中缓存时调用自身findClass。

6)、加载自己实现的Object类

自定义Object类在双亲委派模型下无法被加载,因父加载器会优先加载系统核心类库中的Object类。

7)、双亲委派模型的优点

  • 层次化优先级:确保核心类库优先加载
  • 避免重复加载:已加载类直接返回缓存结果
  • 沙箱安全机制:防止外部代码篡改核心逻辑

2.4、所有被new出来的实例,都是放在堆中的吗?

本题核心:

  • 堆栈内存区别:堆是线程共享的运行时数据区,栈是线程私有的运行空间
  • 栈存储优势:自动内存管理、无GC开销、访问速度更快
  • 适用条件:实例生命周期不超过方法作用域且不涉及多线程访问时适合栈存储

1)、栈中是否能存放实例

概念

实现方式

可行性

标量替换

将对象字段平移到局部变量表

可实现堆栈数据等价转换

栈上分配

在栈帧中直接分配实例空间

通过局部变量表存储实例字段

方法存储

类方法始终存放在方法区

与实例存储位置无关

2)、栈中存放实例的好处

  • 栈帧销毁自动回收实例:方法执行完毕后栈帧出栈,实例随之销毁
  • 无GC开销:实例回收不依赖垃圾收集机制,减少系统资源消耗
  • 性能优势:避免了堆内存分配和回收带来的性能损耗

3)、什么情况实例适合存放在堆中

场景特征

存储要求

原因分析

实例被方法返回

必须存于堆中

保证方法外部可访问

实例被多线程共享

必须存于堆中

栈是线程私有空间

实例生命周期限于方法内部

可存于栈中

满足栈上分配条件

逃逸分析用于判定实例作用域范围

决定存储位置

分析实例是否逃逸方法作用域

逃逸分析:分析实例是否逃逸出作用域

2.5、GC为什么会导致应用程序卡顿

推荐阅读:带你认识JVM

GC(垃圾回收)是JVM面试中的重要知识点。GC导致应用程序卡顿的核心原因是GC线程触发用户工作线程停止(STW机制)。分析该问题需掌握以下关键点:

  • STW(Stop-The-World)机制:垃圾回收时暂停所有用户线程
  • JAVA堆特性:垃圾回收主要发生在堆内存区域
  • GC知识体系:掌握回收策略、检测算法及实现技术

GC线程与用户工作线程

线程交互场景

问题本质

后果

GC开始时实例非垃圾,GC过程中引用被删除

回收不彻底

残留垃圾需下次GC清理

GC开始时实例未建立引用,GC过程中被引用

误回收有效对象

引发运行时空指针异常

上述问题统称为并行GC的脏实例问题,是STW机制需要解决的核心矛盾。

STW(Stop-The-World)是解决脏实例问题的强制措施,其特性包括:

  • 同步暂停:GC执行时冻结所有用户线程
  • 卡顿根源:线程暂停导致用户感知延迟
  • 优化方向:
    • 减少GC频率(降低STW触发次数)
    • 缩小回收范围(如分代回收策略)
    • 并行/并发回收(多线程协同或与用户线程交错执行)

1)、分代回收

分代回收是JVM经典优化策略,其内存划分与运作机制如下:

  • 年轻代(占堆1/3):
    • 新生区(Eden,占年轻代8/10):新对象初始分配区域
    • 幸存区(S0/S1,各占年轻代1/10):存放经历GC存活的对象
  • 老年代(占堆2/3):存储长生命周期对象
  • 晋升规则:
    • 同龄对象占幸存区超50%时晋升
    • 分代年龄超过阈值(默认15)时晋升
    • 大对象直接分配至老年代
  • GC触发逻辑:
    • Young GC:清理年轻代(新生区+幸存区)
    • Full GC:清理整个堆(含老年代)

分代回收是基于以下经验形成的优化策略:

  • 短生命周期对象占多数:大多数实例在年轻代即被回收
  • 长生命周期对象筛选机制:存活时间长的实例会被转移到老年代,降低回收频率
  • 跨代引用现象稀少:老年代引用年轻代的情况属于少数现象,确保分代策略可行性

新生区的关键特性包括:

  • 新实例分配原则:新创建实例优先分配至新生区,大对象直接进入老年代
  • 空间管理要求:必须保持新生区无严重内存碎片
  • 回收机制:每次Young GC会清空整个新生区,确保内存连续性和回收效率

幸存区的运行机制:

特性

实现方式

优势

缺陷

空间结构

两个等大区域轮换使用

算法实现简单

浪费50%空间

回收流程

存活实例复制到to区后清空原区域

避免内存碎片

空间利用率低

适用场景

作为年轻代到老年代的过渡区域

保证内存连续性

需严格控制区域大小

老年代的核心特征:

  • 存储对象类型:长生命周期实例和大对象
  • 回收频率:触发GC的频率最低
  • 设计意义:通过集中管理特殊对象,确保年轻代保持轻量级GC

分代回收的主要优势包括:

  • 差异化回收机制:根据对象生命周期采用不同回收策略
  • 算法适配性:各代可选择最适合的垃圾回收算法
  • 性能优化:避免全堆回收带来的性能损耗

2)、垃圾检测算法

分代回收机制仅说明GC会清除垃圾实例保留非垃圾实例,判断实例是否为垃圾需依赖垃圾检测算法。

①、引用计数法

  • 原理:每个实例维护引用计数器,被引用时加1,解除引用时减1,计数器为0的实例判定为垃圾
  • 优点:实现简单,OC语言采用类似机制
  • 缺陷:
    • 循环引用问题:互相引用的实例计数器永不为0导致内存泄漏
    • 效率问题:需遍历堆中所有实例检查计数器

②、可达性分析法

  • 原理:从GC Roots出发遍历实例,未被访问到的实例判定为垃圾
  • 特点:可解决循环引用问题,JAVA主流垃圾检测算法
  • 类比:实例如气球,GC Roots为地面,无连接的气球(实例)会被GC(风)回收

③、GC Roots

GC Roots类型

说明

局部变量表

当前执行方法的参数、临时变量

方法区静态变量

类静态变量

方法区常量池

常量引用的堆实例

本地方法栈变量

JNI调用native方法引用的堆实例

同步锁持有对象

被锁对象

3)、垃圾回收算法

①、复制算法

  • 流程:
    • 将内存分为等大两区域,仅使用其一
    • 标记阶段识别存活实例
    • 将存活实例复制至另一区域并连续排列
    • 清空原区域
  • 优点:保证内存连续性,算法逻辑简单
  • 缺点:浪费50%内存空间,新生代幸存区采用类似机制

②、标记清除法

  • 流程:直接释放标记的垃圾实例内存
  • 优点:效率高,无移动/复制操作
  • 缺点:内存碎片化严重,安卓平台主要采用此算法

③、标记整理法

  • 流程:释放垃圾实例后整理存活实例至连续空间
  • 优点:解决内存碎片问题
  • 缺点:运行效率低于标记清除法,实现复杂度更高
  • 注意:三种算法无绝对优劣,选择取决于对内存碎片或停顿时间的容忍度

4)、垃圾回收器

垃圾回收器可分为串行、并行、并发三类,每类有代表性实现。

①、串行回收器

  • 特点:单线程GC,工作线程完全暂停
  • 代表:
    • Serial:年轻代,复制算法,JDK - 3前唯一年轻代回收器
    • Serial Old:老年代,标记整理法,Client模式默认回收器
  • 缺点:STW时间最长

②、并行回收器

  • 特点:多线程GC提升吞吐量,JAVA 8默认回收器
  • 代表:
    • ParNew:年轻代,复制算法
    • Parallel Old:老年代,标记整理法

③、并发回收器

回收器

特点

算法

CMS

分代模型,追求短停顿,标记阶段分初始标记(STW)、并发标记、重新标记(STW)

标记清除法

G1

分区回收,逻辑分代,可预期STW

区域间复制+整体标记整理

ZGC

JDK11引入,无分代概念

染色指针技术

5)、G1分区回收

  • 内存划分:将堆分为等大小区域,含逻辑年轻代、老年代及Humongous区域(存放大对象)
  • 算法特性:
    • 区域间采用复制算法
    • 整体表现为标记整理算法
  • 优势:
    • 细粒度回收:仅处理部分区域
    • 可预测停顿:支持设置最大GC时间参数

6)、Android平台的GC

①、Dalvik

Dalvik虚拟机采用标记清除算法,而Android Runtime引入分代回收策略。

Dalvik早期采用全站回收机制,无分区或分代设计,且GC过程全程STW(Stop-The-World),平均单次STW耗时约100毫秒,这是早期安卓系统卡顿的主要原因。

安卓2.3版本引入并行CMS算法和分区回收机制,单次GC的STW时间缩短至5毫秒以内,显著提升系统流畅度。

Dalvik堆包含以下核心组件:

  • Active堆与Zygote堆:分别存储新实例和系统预加载实例
  • 两个Heap Bitmap:
    • Live Bitmap:记录上次GC存活实例
    • Mark Bitmap:记录本次GC标记结果
  • Mark栈:辅助实现地址有序的增量标记算法
  • Card Table:以128字节为单位跟踪堆修改,解决并发标记的脏实例问题

②、ART

ART堆结构升级:

  • Image Space:映射系统类库镜像,进程间共享
  • Allocation Space:替代Active堆,存储新实例
  • Large Object Space:离散地址空间专存大对象

ART的GC策略分级:

  • Sticky GC:仅回收两次GC间新产生的垃圾
  • Partial GC:回收Allocation Space和Large Object Space
  • Full GC:覆盖除Image Space外的全部堆区域

GC策略升级路径:

  • Sticky GC → - Partial GC → - 堆扩容 → - 回收软引用 → - Full GC → - OOM异常

2.6、双重检测的单例为什么要加volatile关键字?

双重检测单例模式是JVM面试中的常见题目,重点考察volatile关键字在并发编程中的作用。该题目涉及并发编程与JMM(Java内存模型)的核心知识点。

1)、指令重排序

  • 指令重排序的定义:编译器或处理器为优化性能而改变指令执行顺序
  • 指令重排序对单例模式的影响:可能导致对象未完全初始化就被使用
  • volatile关键字的作用机制:通过内存屏障禁止指令重排序
  • JMM规范对指令重排序的限制

2)、计算机硬件内存模型

  • CPU执行速度与内存读写速度不匹配导致性能瓶颈
  • 高速缓存作为CPU与内存之间的缓冲层
  • 多线程环境下缓存一致性问题:当多个CPU核心同时操作同一变量时可能出现数据不一致
  • 缓存一致性协议用于解决多核CPU的缓存同步问题
  • JVM作为软件实现的计算机需要解决类似的线程安全问题

3)JMM

Java内存模型(JMM)的核心要点包括:

  • 每个线程拥有独立的工作内存
  • 主内存与工作内存通过数据副本交换信息
  • JMM规范定义了变量在主内存与工作内存间的交互规则
  • 工作内存的实现方式可能包括硬件缓存、寄存器或编译器优化
  • JMM抽象模型统一了不同硬件平台的内存访问行为

4)、并发编程3大要素

①、原子性

并发编程原子性的关键点:

  • 操作不可分割性是原子性的核心特征
  • synchronized关键字可保证代码块原子性
  • 锁机制是实现原子性的主要手段

②、可见性

变量可见性的实现方式:

  • volatile关键字强制变量修改立即同步到主内存
  • synchronized关键字在加锁时清空工作内存并重新读取主内存值
  • 锁释放前会将工作内存变量刷新回主内存

③、有序性

程序有序性的保障机制:

  • synchronized关键字保证代码块执行顺序的可预期性
  • volatile关键字通过内存屏障防止指令重排序
  • happens-before原则是JMM有序性的理论基础

5)、双重检测单例的分析

双重检测的单例模式也称为懒汉式单例模式。假设不加volatile关键字,分析其潜在问题。

①、原子性分析

  • 双重检测单例的原子性问题:通过将第二重检测和实例创建代码置于synchronized同步块中,可保证原子性。
  • 不加volatile关键字的影响:即使未加volatile,并发调用getInstance时创建多个实例的概率较低。

②、可见性分析

  • synchronized的可见性保证:加锁前清空工作内存并同步主内存变量值,释放锁前刷新变量值至主内存。
  • 同步块内代码的可见性:可视为直接使用主内存中的最新变量值,因此可见性问题较少出现。

③、有序性分析

  • synchronized同步块的有序性:代码层面逻辑有序,但指令重排序可能导致未加volatile时单例模式失效。
  • volatile的职责:禁止指令重排序,需进一步分析重排序的具体影响。

④、指令重排序

  • 指令重排序的定义:编译器和处理器为提高性能可能调整指令顺序,但需遵循以下原则:
    • as-if-serial原则:单线程下执行结果不变。
    • happens-before原则:不破坏指令间的依赖关系。
  • 可重排序的指令类型:无依赖关系的指令(如独立变量赋值)。
  • 不可重排序的指令类型:存在数据依赖或控制依赖的指令。

总结:

  • volatile的作用:防止指令重排序,避免并发时获取未完全初始化的实例。
  • 结论:双重检测单例模式中,volatile关键字不可或缺。

OK,今天的内容就到这里了,咱们下期再会!


文章转载自:

http://keof7a5U.wnpps.cn
http://BBrzlpBW.wnpps.cn
http://kI4fX6kZ.wnpps.cn
http://Ec0GEOav.wnpps.cn
http://6XHxYFRX.wnpps.cn
http://jGn9b3q3.wnpps.cn
http://GjuWT9Zx.wnpps.cn
http://KI98b0D8.wnpps.cn
http://yYylS2lH.wnpps.cn
http://xs2KgNqG.wnpps.cn
http://UkDrqC6H.wnpps.cn
http://r5c4gLR6.wnpps.cn
http://JFUdgRbu.wnpps.cn
http://fK333jhI.wnpps.cn
http://mEjdQI3a.wnpps.cn
http://nlhJppGj.wnpps.cn
http://7bsFYDQX.wnpps.cn
http://Tv1TVn47.wnpps.cn
http://2PxtNsFk.wnpps.cn
http://NxGPEghT.wnpps.cn
http://E2TM8PKl.wnpps.cn
http://2L1KXIdV.wnpps.cn
http://uGHWl5x8.wnpps.cn
http://CFf6TPZ5.wnpps.cn
http://z0bokVa6.wnpps.cn
http://4YaeMgye.wnpps.cn
http://el7r34Oq.wnpps.cn
http://KGjoQgvS.wnpps.cn
http://7lW8uGdA.wnpps.cn
http://zQcVtRp7.wnpps.cn
http://www.dtcms.com/a/374035.html

相关文章:

  • 西嘎嘎学习 - C++修饰符类型 - Day 5
  • 明远智睿RK3568核心板:199元解锁多行业智能新可能
  • LeetCode算法日记 - Day 36: 基本计算器II、字符串解码
  • linux系统address already in use问题解决
  • ArcGIS学习-17 实战-密度分析
  • 08 修改自己的Centos的软件源
  • 柯美bizhub 206复印机报 警告 维修召唤(M2) 维修召唤如何解决 如何维修
  • Vue3 页面切换白屏问题解决方案
  • [硬件电路-168]:Multisim - Multisim提供的用于学习参考电路有哪些?存放位置?
  • 使用kettle批量调用大模型
  • 【系统分析师】第1章-基础知识:绪论(核心总结)
  • docker-容器
  • ARM架构详解:从内核到异常处理
  • Redis缓存击穿、雪崩、穿透
  • Go正则表达式实战指南
  • 保持元素可见但不可访问的方法: `inert`
  • ClaudeCode稳定备用方案:API接入详解
  • 【教程】Ansible 环境部署
  • Linux-信号量
  • 3000h CeB₆ 灯丝加持的 Phenom XL G3 扫描电镜技术亮点
  • C语言scanf函数的空格问题
  • 【Git】使用GitCode的全局配置
  • 论文阅读:ACL 2023 MEETINGQA: Extractive Question-Answering on Meeting Transcripts
  • Docker Compose healthcheck介绍(监控容器中服务的实际健康状态)数据库健康检查pg_isready
  • 鸿蒙NEXT中SQLite数据库全面实战指南
  • Go语言文件处理实战指南
  • 【鸿蒙(openHarmony)ETS语言实现视频播放器的详细步骤】
  • SpringBoot教程(三十一) | SpringBoot集成SpringSecurity权限框架
  • 第四十九篇-Tesla P40+Fastllm+Hunyuan-A13B-Instruct+CPU+GPU混合部署推理
  • 安装docker遇到的问题1: [Errno 14] curl#35 - “TCP connection reset by peer“