认识Java虚拟机

java虚拟机

Java为什么是跨平台的

Java 实现 "一次编写、到处运行(Write Once, Run Anywhere,WORA)" 的跨平台特性,本质是基于分层抽象的架构设计------ 通过标准化的字节码作为中间层、平台定制化的 Java 虚拟机(JVM)作为执行层、统一化的核心类库作为接口层,三层协同屏蔽不同操作系统、硬件架构的底层差异。

一、传统编译型语言(如 C/C++)的平台绑定问题

C/C++ 等语言采用 "源码直接编译为本地机器码" 的单步编译模型

编译器会将源代码直接编译为与特定平台(如 Windows、Linux)强绑定的机器码,这些机器码依赖于具体的 CPU 指令集和系统调用,换平台后因指令格式、系统接口不兼容而无法运行,必须重新编译,无法直接运行。

这就是传统语言跨平台性差的核心原因------执行指令与平台深度耦合。

二、Java 跨平台的核心实现逻辑(三层架构支撑)

Java 核心依托 "源码编译 - 字节码抽象 - JVM 适配执行"的三层结构化架构,从技术底层实现了 "代码编写过程" 与 "平台执行环境" 的解耦,具体流程如下:

  1. 第一层:源码编译为平台无关的 Java 字节码
    开发者编写的.java源码,经 JDK 内置的 javac 编译器编译后,生成Java 字节码文件(.class) ------ 字节码并非面向具体硬件的本地机器码,而是遵循 JVM 规范的 "中间指令集":
    • 字节码仅与 JVM 指令集绑定,不依赖任何操作系统(Windows/Linux/macOS)或 CPU 架构;
    • 同一套 .java 源码,在任意平台上通过 javac 编译生成的 .class 文件完全一致(字节码的二进制格式、指令逻辑无差异),实现 "一次编译,处处可用"。
  2. 第二层:平台专属 JVM 实现字节码的本地化执行
    字节码无法直接被 CPU 执行,需依托 Java 虚拟机(JVM)完成从字节码到本地机器码的转换,这是跨平台的核心适配层:
    • JVM 的 "平台专属特性":不同操作系统 / 硬件架构有对应的 JVM 实现(如 Windows 的jvm.dll、Linux 的libjvm.so、macOS 的libjvm.dylib),这些 JVM 均遵循统一的 JVM 规范,但底层会适配目标平台的系统调用、硬件指令;
    • 字节码的执行方式:JVM 通过解释执行与即时编译(JIT)混合模式处理字节码:
      解释执行:启动初期逐行将字节码翻译为本地机器码并执行,保证快速启动,且翻译逻辑由平台专属 JVM 实现,天然适配当前系统;
      JIT 编译:运行时识别 "热点代码"(频繁执行的方法 / 循环),一次性将其编译为本地机器码并缓存,后续执行直接复用缓存的机器码;
  3. 第三层:统一核心类库的底层适配
    Java 核心类库(java.lang、java.io、java.net等)为跨平台提供上层支撑:
    • 上层 API 完全统一:开发者调用的类库接口在所有平台上的调用方式完全一致,无需关注底层差异;
    • 底层实现平台化:类库的核心逻辑由 JVM 底层封装,不同平台的 JVM 会适配自身的系统特性(如 Windows 的文件路径分隔符\、Linux 的/,由File.separator统一封装;Windows 的注册表操作、Linux 的文件权限管理,均由 JVM 内部实现差异化适配)。

三、Java 跨平台的非绝对性(例外场景)

Java 的跨平台性是基于 JVM 抽象层的相对特性,以下场景会打破跨平台能力,本质是突破了 JVM / 字节码的抽象层,直接依赖平台特性:

  • 使用 JNI/JNA 调用本地代码:若通过 JNI(Java Native Interface)/JNA 调用 C/C++ 编写的本地方法,这些本地代码会编译为特定平台的机器码,导致程序依赖目标平台;
  • 硬编码平台专属特性:如硬编码 Windows 专属路径(C:\xxx)、调用 Linux 专属系统命令(ls/grep)、依赖 Windows 的COM组件等;
  • 字节码版本不兼容:编译 .java 源码的 JDK 版本高于运行时 JVM 版本,因高版本字节码包含低版本 JVM 不支持的指令;
  • 依赖平台专属 JVM 扩展:如使用 Oracle JDK 的 Windows 专属桌面扩展、Linux 专属的系统监控 API 等。

JVM、JRE与JDK的核心关系及协同机制

JVM、JRE与JDK的关系如图所示:

Java 虚拟机(JVM)、Java 运行时环境(JRE)、Java 开发工具包(JDK)是 Java 技术体系的核心组件,三者定位清晰、差异显著,呈现层层嵌套的层级结构(JDK⊇JRE⊇JVM),功能互补且协同闭环,共同支撑 Java 程序从开发构建、本地自测到部署运行的全生命周期流程。其中,JVM 是底层核心执行引擎,是跨平台特性的关键载体;JRE 是程序运行的最小必要环境,提供基础运行支撑;JDK 是面向开发者的全量工具套件,覆盖开发全流程需求。

一、Java 虚拟机(JVM):跨平台执行的核心引擎

JVM 的本质是一套遵循统一 Java 虚拟机规范、适配不同操作系统与硬件架构的定制化软件抽象实现,属于虚拟计算模型,不直接与开发者交互。其核心职责是将平台无关的.class字节码文件翻译为当前操作系统可识别的本地机器码。

关键特性与依赖关系如下:

  • 平台适配性:针对不同操作系统及硬件架构,存在专属 JVM 实现,但所有实现均严格遵循统一的 Java 虚拟机规范,确保字节码执行标准一致,这是 Java 跨平台特性的核心技术基础;
  • 运行依赖性:JVM 无法独立完成字节码执行,需依赖核心类库(如 java.lang 基础类包、java.io 输入输出类包)与运行时支撑组件(如类加载器、垃圾回收调度模块、线程同步机制等),上述组件与 JVM 共同构成功能完整的运行环境(JRE)。

二、Java 运行时环境(JRE):程序运行的最小必备环境

JRE 的核心定位是保障已编译的 Java 程序正常运行,是运行 Java 应用的最小依赖集合,普通用户仅需安装 JRE 即可满足程序运行需求。JRE 不包含任何开发工具,核心功能聚焦两大维度:

  • 字节码执行支撑:内置 JVM,通过 JVM 完成字节码到本地机器码的转换与执行,承接跨平台适配的核心逻辑;
  • 底层服务封装:通过核心类库提供统一的标准化 API 调用能力,同时封装运行时数据区管理、垃圾回收机制、异常处理逻辑等底层服务,屏蔽不同操作系统的底层实现差异,确保 Java 程序跨平台部署时的运行一致性。

三、Java 开发工具包(JDK):全流程开发的工具载体

JDK 是面向 Java 开发者的全量开发工具套件,在 JRE 的基础上额外集成了开发全流程所需的工具组件,是支撑 Java 开发活动的完整环境。

核心工具组件及功能:

  • 核心编译工具:javac 编译器,负责将开发者编写的 .java 源代码编译为平台无关的 .class 字节码文件;
  • 辅助开发工具:包括 javadoc(提取代码注释生成标准化 API 文档)、jdb(程序运行时断点调试与问题定位)、jar(将.class文件及依赖打包为可分发的归档文件)等;
  • 运行环境复用:JDK 内置完整 JRE,可直接运行编译后的字节码程序进行本地自测,完成 Java 程序的编写、编译、调试、打包、部署等全流程开发工作,是支撑 Java 开发活动的全量环境。

四、三者的协同机制:全生命周期闭环

三者的协同逻辑形成闭环且分工明确:开发者依托JDK中的javac编译器将.java源代码编译为.class字节码;后续无论是开发者进行本地自测,还是普通用户部署运行程序,均依赖JRE中的JVM完成字节码的跨平台执行;在此过程中,JVM通过调用JRE内置的核心类库,完成标准化API的底层平台适配,最终实现Java一次编写、到处运行的核心目标。

三者的功能边界清晰:JVM是字节码执行的核心引擎,JRE是程序运行的基础支撑环境,JDK是全流程开发的工具载体。这种嵌套互补的架构设计,既满足了开发者对Java程序开发全流程的工具需求,又保障了Java程序在跨平台运行时的稳定性与一致性。

Java虚拟机的结构

一、类加载子系统

类加载子系统是 JVM 处理类加载的核心模块,其职责是完成 "加载 - 链接 - 初始化" 三个完整阶段:加载阶段从文件系统、网络等数据源读取 Class 字节码文件,并生成对应的 Class 对象;链接阶段包含验证(校验字节码合法性)、准备(为类静态变量分配内存并设置默认值)、解析(将符号引用转换为直接引用);初始化阶段则执行类构造器 <clinit>() 方法,完成静态变量赋值和静态代码块执行。

加载后的类信息会存储在方法区中,方法区同时还存放运行时常量池,这是 Class 文件中常量池的内存映射,包含字符串字面量、数字常量、符号引用等,且运行时常量池具备动态性,可在运行时向池中添加新常量。

二、JVM 运行时数据区(核心内存区域)

JVM 运行时数据区是 Java 程序执行的核心内存载体,依据内存访问权限与生命周期特性,可明确划分为线程私有区域和线程共享区域两大类,各类区域的功能定位与结构特性如下:

(一)线程私有区域

线程私有区域的内存空间与线程生命周期严格绑定,随线程创建而初始化,随线程终止而销毁,不存在多线程并发访问冲突,包含以下三个核心区域:

  • 程序计数器:作为当前线程执行字节码的行号指示器,每个线程均拥有独立的程序计数器实例,其核心职责是存储当前线程正在执行的指令地址。该区域是 JVM 规范中唯一未定义 OutOfMemoryError 异常的内存区域,因其占用内存空间极小且大小固定,无需动态扩展。
  • Java 虚拟机栈(线程栈):每个线程创建时会伴随一个私有虚拟机栈,其生命周期与所属线程完全绑定。虚拟机栈以栈帧为基本组成单元,栈帧的生命周期与 Java 方法的执行周期严格绑定:方法开始执行时会在所属线程的虚拟机栈中创建一个对应的栈帧,当方法正常完成执行或因异常终止时,该栈帧会从虚拟机栈中出栈。栈帧内部封装了方法执行所需的全部核心数据,具体包括:局部变量表(编译期确定容量,存储方法局部变量、方法入参,支持基本数据类型、对象引用类型及 returnAddress 类型);操作数栈(作为方法执行过程中临时数据的存储与运算空间,支持指令执行时的操作数入栈、出栈及运算);动态链接(指向运行时常量池中当前方法的符号引用,运行时可将其转换为直接引用,保障方法调用的动态绑定);方法出口(记录方法执行完毕后需返回的程序地址,确保恢复上层调用方法的执行上下文)。
  • 本地方法栈:功能定位与 Java 虚拟机栈类似,核心为虚拟机调用的 Native 方法提供运行支撑。在 HotSpot 虚拟机中,本地方法栈与 Java 虚拟机栈并未进行物理分离,而是采用 "物理合并、逻辑区分" 的实现方式,共享同一块内存区域但各自维护独立的栈帧结构。其异常机制与 Java 虚拟机栈一致:栈深度超出限制时抛出 StackOverflowError,内存扩展失败时抛出 OOM 异常。

(二)线程共享区域

线程共享区域的内存空间由所有线程共同访问,生命周期与 JVM 一致(虚拟机启动时创建,关闭时销毁),是多线程并发访问的核心区域,包含以下三个核心部分:

  • Java 堆:作为 JVM 中内存占比最大的区域,Java 堆在虚拟机启动时初始化,由所有线程共享,核心用途是存储 Java 对象实例。为优化垃圾回收效率,Java 堆采用分代存储设计:分为新生代和老年代,其中新生代进一步划分为 Eden 区和两个大小相等的 Survivor 区(From Survivor / S0、To Survivor / S1)。新生代主要存储刚创建的短生命周期对象,老年代主要存储经过多次垃圾回收后仍存活的长生命周期对象。当 Java 堆无法为新创建的对象分配内存,且堆空间已达到预设的最大容量无法进一步扩展时,会抛出 OOM 异常。
  • 方法区:JVM 规范定义的独立逻辑内存区域(因功能与内存管理机制独立于堆,常被称为 "非堆"),核心用于存储已被虚拟机加载的类元数据、静态变量、常量、方法数据及运行时常量池。HotSpot 虚拟机对方法区的实现存在显著版本差异:JDK 1.7 及之前版本通过 "永久代" 实现,永久代是独立于 Java 堆的虚拟机内存区域;JDK 1.8 及以后版本废除永久代,改用 "元空间" 替代,元空间的内存来源于进程的本地内存(操作系统直接分配给 JVM 进程的内存空间),其大小默认受限于操作系统总内存。方法区的垃圾回收效率低,仅针对无用类和废弃常量进行回收。
  • 直接内存:不属于 JVM 运行时数据区的规范定义范畴,是 Java NIO 为提升 I/O 操作性能引入的堆外内存机制。直接内存由程序直接向操作系统申请,绕开了 JVM 堆的内存管理,访问速度优于 Java 堆,其大小不受 JVM 堆的 -Xmx 参数限制,但受限于操作系统总内存及 JVM 进程可申请的最大内存;JVM 的垃圾回收机制无法主动回收直接内存,需通过 Cleaner 机制或手动调用 Unsafe.freeMemory () 方法释放。

三、垃圾回收系统

垃圾回收系统(GC)是 Java 虚拟机(JVM)的核心组件之一,其核心职责是自动化识别并回收 "不可达" 对象占用的内存资源,避免内存泄漏,保障程序运行的稳定性与内存利用率。GC 的回收范围具有明确边界:核心回收目标是 Java 堆(所有分代区域都是 GC 的核心工作区);方法区(元空间)的回收仅针对无用类卸载和常量池清理,且并非所有 JVM 实现都支持类卸载;直接内存不在 JVM GC 的管辖范围内,无法被主动回收。

四、执行引擎

执行引擎是 JVM 执行字节码的核心模块,其设计直接决定了 Java 程序的启动速度与运行效率。现代 JVM 采用解释器与即时编译器(JIT)的混合执行模式:解释器可逐行解释执行字节码,启动速度快但执行效率低;JIT 编译器会识别程序中的热点代码(频繁执行的方法或循环),将其一次性编译为与本地平台适配的机器码并缓存,后续执行时直接调用缓存的机器码,大幅提升执行效率。

Java程序启动机制与虚拟机参数设置

Java 程序的启动依赖 JDK/JRE 提供的专用启动器,核心逻辑是通过启动器触发 Java 虚拟机(JVM)初始化,再由 JVM 加载执行字节码文件;而启动过程中配置的 JVM 参数可实现性能调优与故障排查,具体机制、命令格式及参数用途如下:

核心启动逻辑

JAVA_HOME/bin/java(Windows 系统为JAVA_HOME/bin/java.exe,Linux/macOS 系统为JAVA_HOME/bin/java)是 JDK/JRE 内置的 Java 启动器可执行程序,也是用户触发 JVM 启动的核心外部入口。

运行 Java 程序的本质是:调用该启动器程序后,启动器会先加载 JVM 核心动态链接库,解析传入的配置参数,再通过JNI_CreateJavaVM函数完成 JVM 实例的初始化与进程启动,最终由 JVM 加载并执行目标 Java 类的字节码文件。

其中,JAVA_HOME 是操作系统级环境变量,其值需指向 JDK 的安装根目录(示例:Windows 系统默认路径 C:\Program Files\Java\jdk1.8.0_261);bin/java 则是 JDK 安装目录下bin子目录中的核心启动程序,是连接用户操作与 JVM 启动的关键载体。

标准命令行格式

执行包含 public static void main(String[] args) 方法的 .class 主类文件以启动 Java 程序时,标准命令行格式为:
java [-options] class [args...]

  • java 命令:调用 JAVA_HOME/bin/java 可执行程序
    需确保 JAVA_HOME 环境变量配置正确以及 JAVA_HOME/bin 目录已添加至系统PATH环境变量(否则需输入java程序的完整路径执行,如 Windows 系统下需执行 C:\Program Files\Java\jdk1.8.0_261\bin\java.exe)。
  • -options\]:JVM 启动参数 可选配置项,用于定制 JVM 运行时特性(如内存分配、垃圾收集策略、日志输出等),按 JVM 规范分类如下: * 以 - 开头:标准参数(所有 JVM 实现均支持,兼容性强),示例:-version(查看 JVM 版本); * 以 -X 开头:非标准参数(仅 HotSpot JVM 特有,不保证跨 JVM 实现或跨版本兼容); * 以 -XX 开头:实验性参数(HotSpot JVM 的实验性配置,用于调优和故障排查,稳定性未完全验证,生产环境需谨慎使用);

    必选参数,指包含 main(String[] args) 方法的目标 Java 类的全限定名;
  • args...\]:程序运行参数 可选配置项,用于向主类的 main(String\[\] args) 方法传递运行时参数,参数会按输入顺序封装到args字符串数组中。多个参数以空格分隔,若参数本身包含空格,需用双引号包裹。

HotSpot(热点虚拟机)是 Oracle JDK 与 OpenJDK 官方默认的 JVM 实现,上述-X和-XX开头的参数是 HotSpot 的核心配置项,按用途可分为基础内存配置、性能调优和故障排查三大类,具体说明及示例如下:

(一)基础内存配置类参数

-Xms:JVM 堆内存初始值(建议与 -Xmx 接近,减少内存动态扩展开销);

-Xmx:JVM 堆内存最大值(超出该值会触发 OutOfMemoryError);

-Xmn:设置新生代内存区域固定大小(等价于同时设置 -XX:NewSize 和 -XX:MaxNewSize 为同一值,避免新生代内存动态扩容);

-XX:MaxNewSize:新生代内存区域最大值(与 -Xmn 功能重叠,优先用 -Xmn)

-XX:SurvivorRatio:新生代中 Eden 区与单个 Survivor 区的内存比例(HotSpot 默认 8);

-XX:MaxPermSize:永久代(JDK8 前)最大值(JDK8 后被 Metaspace 替代,需用 -XX:MaxMetaspaceSize);

(二)性能调优类参数

用于调整 JVM 底层运行机制,优化内存分配、垃圾回收效率、代码编译策略等核心性能指标:

  • 垃圾收集器选择(适配不同业务的 GC 停顿需求)
    -XX:+UseParallelGC:启用并行垃圾收集器,适用于追求高吞吐量的后台服务;
    -XX:+UseG1GC:启用 G1 垃圾收集器,适用于大内存、低 GC 停顿的场景;
  • JIT 编译策略优化(平衡启动速度与运行效率)
    -XX:CompileThreshold:设置方法被调用次数阈值,达到后触发 JIT 编译(阈值越低,热点代码编译越早);

(三)故障排查类参数

用于输出 JVM 运行日志、生成诊断文件或监控核心运行过程,助力定位内存溢出、类加载异常、GC 异常等问题:

  • GC 日志打印(输出垃圾回收的详细过程)
    -XX:+PrintGC:打印简易 GC 日志(仅核心内存变化、耗时),建议配合 - Xloggc 输出到文件;
    -XX:+PrintGCDetails:打印 GC 详细日志(含分代内存变化、收集器类型、耗时细分),适用于深度分析 GC 问题;
    -XX:+PrintGCTimeStamps:输出 GC 时间戳(以 JVM 启动为基准的秒数,便于分析 GC 发生顺序),建议搭配 PrintGCDetails;
    -XX:+PrintGCDateStamps 输出 GC 的具体日期时间戳,生产环境推荐;
    -verbose:gc 开启基础 GC 日志打印(等价于 -XX:+PrintGC),建议搭配 -Xloggc 输出到文件;
    -Xloggc:<路径> 指定 GC 日志的存储文件路径(避免控制台刷屏),例如 -Xloggc:./gc.log;
  • 内存溢出诊断(OOM 时生成堆转储文件,分析内存泄漏)
    -XX:+HeapDumpOnOutOfMemoryError:触发 OutOfMemoryError 时自动生成堆转储(dump)文件;
    -XX:HeapDumpPath=./dump.hprof:指定堆转储文件的存储路径与文件名(需确保 JVM 有该路径的写入权限);
  • 类加载过程追踪(定位类加载相关问题)
    -verbose:class(等价于-XX:+TraceClassLoading):打印类加载 / 卸载的详细信息,定位类加载冲突、重复加载、类找不到等问题。
  • JVM 参数验证(确认参数实际生效情况)
    -XX:+PrintVMOptions:打印虚拟机接受到的命令行显式参数;
    -XX:+PrintCommandLineFlags:打印传递给虚拟机的显式和隐式参数,隐式参数是 JVM 启动时根据系统环境或默认策略自动设置的参数。

使用 -XX:+PrintGC 这个参数启动 Java 虚拟机后,只要遇到GC,就会打印日志,如下所示:

该日志显示,共进行了两次 GC,每次 GC 占用一行:

  • 第一次是轻量 GC(Minor GC):GC 发生前,Java 堆年轻代的已占用内存约为 4MB(4997K),GC 后年轻代已占用内存为 912KB;括号内的 239616K 是本次 GC 回收区域(年轻代)的总容量;最后显示的是本次 GC 花费的时间。
  • 第二次是全量 GC(Full GC):GC 发生前,整个 Java 堆的已占用内存为 912KB,GC 后整个堆已占用内存为 610KB;括号内的 239616K 是整个 Java 堆的总容量;最后显示的是本次 Full GC 花费的时间。

如果需要更加详细的信息,则可以使用 -XX:+PrintGCDetails 参数

GC (System.gc()) \[PSYoungGen: 4997K-\>840K(72704K)\] 4997K-\>848K(239616K), 0.0015757 secs\] \[Times: user=0.00 sys=0.00, real=0.00 secs

PSYoungGen:表示本次回收的区域;

4997K->840K(72704K):这三个数字分别对应GC之前占用年轻代的大小,GC之后年轻代占用,以及整个年轻代的大小;

4997K->848K(239616K):这三个数字分别对应GC之前占用堆内存的大小,GC之后堆内存占用,以及整个堆内存的大小。;

0.0015757 secs:本次 GC 的总耗时。

实操案例

以下通过完整代码示例,演示 JVM 启动参数配置、程序参数传递的核心用法,以及如何通过 JVM API 读取配置的堆内存参数

java 复制代码
public class SimpleArgs {
    public static void main (String[] args) {
        for (int i = 0; i < args.length; i++) {
            System.out.println("参数" + (i + 1) + ":" + args[i]);
            //获取JVM中堆的大小  换算成M兆
            System.out.println("-Xmx" + Runtime.getRuntime().maxMemory() / 1000 / 1000 + "M");
        }
    }
}

其中 Runtime.getRuntime().maxMemory() 是 JVM 提供的标准 API,用于获取当前 JVM 实例配置的最大堆内存(字节),其返回值与启动参数 -Xmx 强关联;

在 CMD 或终端执行以下的编译与运行操作

首先需要进入代码所在目录,执行 javac 命令将 .java 源码编译为 .class 字节码文件

bash 复制代码
javac SimpleArgs.java

javac 是 JDK 内置的 Java 源码编译器,核心作用是将 .java 源文件编译为 .class 字节码文件。

使用该命令时需确保 JAVA_HOME/bin 目录已添加至系统 PATH 环境变量(否则需输入 javac 程序的完整路径,如 Windows 系统下为 C:\Program Files\Java\jdk1.8.0_261\bin\javac.exe);javac 命令后接的参数为 Java 源文件的路径(相对路径或绝对路径),且路径需指向具体的 .java 源文件;若源文件声明了包名,需保证源文件的目录结构与包名完全一致。

执行 java 命令,启动JVM

bash 复制代码
java -Xmx10M SimpleArgs abc

通过 -Xmx10M 限制 JVM 最大堆内存为 10MB,运行 SimpleArgs 类并传入参数 abc

在 IDEA 中设置 JVM 参数的位置如下:

执行程序打印结果如下:

查看类字节码信息的 javap 命令

bash 复制代码
javap target/classes/cn/tx/SimpleHeap

javap 是 JDK 内置的 Java 类文件反汇编工具(也称为字节码解析器),核心作用是读取编译后的 .class 字节码文件,解析并输出类的底层结构信息(如类的访问修饰符、成员变量、方法定义、字节码指令、常量池、行号表等)。

javap 后接的是用于定位字节码文件(.class)的标识,主要分为两种场景:

  1. 直接接字节码文件的路径(直观定位)
    可填写字节码文件的相对路径或绝对路径,且可省略 .class 后缀(javap 会自动补全),直接指向目标 .class 文件。
  2. 接类的全包名(间接定位)
    需配合 -cp(-classpath)参数指定字节码文件的根目录,javap 会遵循 "包名 = 目录结构" 的规则,从指定根目录下查找对应的 .class 文件。

示例命令中的 target/classes/ 是 Maven 构建工具的默认编译输出根目录,编译后的 .class 文件会按包名生成对应的子目录结构并存放于此

相关推荐
一切尽在,你来4 分钟前
C++多线程教程-1.2.2 C++标准库并发组件的设计理念
开发语言·c++
雀啼春5 分钟前
Java中的数据类型
java
2401_8384725113 分钟前
使用Python处理计算机图形学(PIL/Pillow)
jvm·数据库·python
80530单词突击赢14 分钟前
C++关联容器深度解析:set/map全攻略
java·数据结构·算法
m0_5613596714 分钟前
代码热更新技术
开发语言·c++·算法
兩尛20 分钟前
c++知识点1
java·开发语言·c++
凯子坚持 c20 分钟前
Qt常用控件指南(9)
开发语言·qt
ONE_PUNCH_Ge22 分钟前
Go 语言泛型
开发语言·后端·golang
舟舟亢亢23 分钟前
JVM复习笔记——下
java·jvm·笔记