1. 引言

在程序执行过程中,链接(Linking)与加载(Loading)是两个至关重要的环节。链接的目的是将多个目标文件组合成一个可执行模块,而加载则是将该模块载入内存中,准备执行。

本文将从开发者的角度出发,深入浅出地讲解动态链接(Dynamic Linking)与动态加载(Dynamic Loading)的区别和应用场景,帮助你理解它们在 Java 程序运行机制中的作用。

2. 链接(Linking)

链接是指将程序的不同模块或函数连接起来,以便程序可以顺利执行的过程。链接器(Linker)会将多个目标文件(Object File)合并成一个完整的可执行文件。简单来说,链接就是把代码和数据“拼”在一起。

链接可以发生在两个阶段:

  • 编译时(Compile Time):源代码编译成目标代码时完成链接
  • 加载时(Load Time):程序被加载进内存时进行链接

根据链接发生的时间,链接可以分为:

  • 静态链接(Static Linking)
  • 动态链接(Dynamic Linking)

下图展示了静态链接与动态链接的区别:

StaticVsDynamicLinking

静态链接:在编译阶段就将依赖库直接复制进可执行文件中
动态链接:依赖库在运行时才被链接,多个程序可以共享一个库

2.1 静态链接

静态链接是在程序编译阶段完成的。链接器将所有目标文件和命令行参数整合,生成一个完整的可执行文件。

优点:程序独立,运行时不依赖外部库
缺点:体积大,更新库需重新编译程序

2.2 动态链接

动态链接旨在解决静态链接带来的资源浪费问题。例如,每个程序都可能使用 printf() 函数,如果都复制一份,不仅浪费磁盘空间,也占用内存。

动态链接的处理方式是:

  • 编译时只记录依赖库信息
  • 运行时由动态链接器将程序与共享库绑定

例如,Linux 下的 .so 文件、Windows 下的 .dll 文件,就是动态链接库。

3. 加载(Loading)

加载是指将程序从磁盘加载到内存中执行的过程。程序在运行前必须被加载到内存,才能被 CPU 执行。

动态加载(Dynamic Loading)

动态加载是指程序在运行时根据需要加载某些模块或库。加载完成后,程序可以访问这些模块中的函数和变量,执行完毕后还可以卸载它们。

✅ 应用场景:

  • 插件系统(如 Apache Web Server 加载 .dso 文件)
  • 按需加载资源,节省内存开销

⚠️ 动态加载的关键在于“按需”,不是所有模块一开始就加载到内存中。

在 Java 中,我们可以通过 ClassLoader 实现类的动态加载。例如:

ClassLoader classLoader = new MyClassLoader();
Class<?> loadedClass = classLoader.loadClass("com.example.MyClass");

4. 动态链接 vs 动态加载

先来看一下链接和加载的关系:

LoadingAndLinking

流程如下:

  1. 编译器将源码翻译成目标模块
  2. 链接器将多个目标模块合并成可执行模块
  3. 加载器将可执行模块加载进内存执行

4.1 动态加载的特点

  • 在运行时按需加载模块
  • 适用于大型程序或插件系统
  • 可以减少内存占用和启动时间

4.2 动态链接的特点

  • 在运行时将程序与共享库绑定
  • 多个程序共享一个库
  • 通过“存根(Stub)”记录函数在哪个库中定义

✅ 类似之处:两者都延迟加载模块
❌ 区别在于:动态链接是程序与库之间的绑定,而动态加载是程序主动加载模块

5. 总结

本文介绍了链接与加载的基本概念,以及它们在程序执行过程中的作用。

我们重点讨论了:

  • 链接的两种类型:静态链接 vs 动态链接
  • 加载的两种方式:静态加载 vs 动态加载
  • 动态链接与动态加载的区别与联系

动态链接用于共享库,减少冗余代码
动态加载用于按需加载模块,节省内存资源

作为 Java 开发者,理解这些机制有助于我们更好地掌握类加载器(ClassLoader)、JVM 启动过程、以及大型系统模块化设计中的底层原理。在实际开发中,尤其是在做插件化架构、热部署、微服务模块加载时,这些概念尤为重要。


原始标题:Dynamic Linking vs. Dynamic Loading

« 上一篇: 弱监督学习概述
» 下一篇: 离线并发控制