大家好,今天咱们用最通俗、最落地的方式,把 Docker 容器 + Java 微服务 + JVM 运行原理 从头到尾讲清楚。
很多老开发天天用容器、天天打 Jar 包,但真被问“代码到底怎么跑起来的”,反而说不明白。
这篇就一次性把底层逻辑全部拆开,让你彻底懂透。

—## 一、先搞懂核心:Docker 到底在干嘛?
Docker 容器本质就是一个独立、隔离、轻量的小操作系统
它不负责运行 Java,它只负责提供一个干净的环境

真正跑 Java 代码的,永远是:
JDK / JRE → JVM

所以关系非常简单:

Docker = 房子
JDK/JRE = 房子里装好的“运行环境”
JVM = 执行代码的核心引擎
你的 Jar 包 = 要跑的业务程序


二、为什么 Dockerfile 里看不到安装 JDK?

这是大家最疑惑的点:
我的 Dockerfile 明明没装 JDK,为啥服务能跑?

答案就是:基础镜像已经帮你装好了!

你们的 Dockerfile 第一行一定长这样:

FROM openjdk:8-jdk-alpine
FROM openjdk:8-jre-slim

这个 FROM 就是:
拿别人提前做好的镜像当底层系统
里面已经:

  • 安装好了 JDK8 / JRE8
  • 配置好了 JAVA_HOME
  • 配置好了 PATH
  • 能直接执行 java 命令

你不需要再写 yum install、apt install,直接拿来用

这就是容器化最方便的地方:环境预制,一键继承


三、容器启动后,Java 到底怎么跑起来?

流程非常清晰,一步都不会少:

  1. 容器启动
  2. 执行命令:java -jar xxx.jar
  3. JVM 启动
  4. JVM 去找 Jar 包里的入口类
  5. 开始类加载
  6. 把类放进内存
  7. 执行 main 方法
  8. 微服务启动成功

下面我们把最核心的 JVM 内存、类加载、双亲委派 全部讲透。


四、JVM 运行时内存:数据到底存在哪?

JVM 启动后会把内存分成 5 块,每块存的东西完全不同:

1. 堆内存(Heap)

所有对象都存在这里!

  • new 出来的对象
  • 字符串常量(JDK7+)
  • 集合、Service、Controller、实体类…

GC 垃圾回收主要就是回收这里。

2. 元空间 Metaspace(JDK8+)

替代了以前的永久代。
存:

  • 类的结构信息
  • 方法信息
  • 字段信息
  • 静态变量
  • 常量池

元空间使用本地内存,不占堆内存。

3. 虚拟机栈

每个线程一个栈。
存:

  • 方法调用栈帧
  • 局部变量
  • 方法参数
  • 返回值

4. 本地方法栈

给 native 方法用的,比如底层 C++ 代码。

5. 程序计数器

记录当前线程执行到哪一行字节码,保证线程切换不乱。


五、什么是直接内存(本地内存)?

不属于 JVM 管理,但是 Java 可以用。
常见场景:

  • NIO
  • Netty
  • 堆外缓存
  • 直接 ByteBuffer

它的好处是快,坏处是不受 JVM 内存限制,Docker 环境下要特别注意别把容器内存打满。


六、最关键:JVM 怎么加载类?(类加载机制)

当你执行 java -jar 时,JVM 并不会一次性把所有类都加载。
它是:用到谁,加载谁 —— 懒加载。

类加载的 5 个步骤

  1. 加载:读取 .class 文件,变成二进制流
  2. 验证:检查类是否合法
  3. 准备:给静态变量分配内存
  4. 解析:符号引用变直接引用
  5. 初始化:执行静态代码,给静态变量赋值

只有执行到 new调用静态方法main方法 时,类才会被初始化。


七、双亲委派模型:类加载器的上下级规则

Java 有 3 层类加载器,上级优先加载,这就是双亲委派。

  1. 启动类加载器(Bootstrap)
    加载 JRE/lib 下的核心类,比如 Object、String。

  2. 扩展类加载器(Extension)
    加载 JRE/lib/ext 里的类。

  3. 应用类加载器(AppClassLoader)
    加载我们自己写的业务类、Jar 包中的类。

为什么要双亲委派?

  • 安全:防止别人篡改核心类(比如自己写一个 Object 替换 JDK 的)
  • 唯一:保证一个类在 JVM 里只被加载一次
  • 稳定:避免重复加载、类冲突

简单说:先问上级,上级找不到,自己再加载


八、回到 Docker:完整运行链路

最后把今晚所有内容串成一条完整链路,你一看就懂:

  1. 编写微服务 → 打包 Jar
  2. Dockerfile 使用 FROM openjdk:8 基础镜像(自带 JDK)
  3. 把 Jar 复制进容器
  4. 启动容器 → 执行 java -jar
  5. JVM 启动
  6. 读取 Jar 包 META-INF/MANIFEST.MF 找到 Main-Class
  7. 应用类加载器开始加载主类
  8. 按照双亲委派规则加载所有依赖类
  9. 类信息进入元空间
  10. 对象创建进入堆内存
  11. 方法执行进入虚拟机栈
  12. main 方法运行 → Spring 启动 → 微服务成功运行

整个流程,就是这么简单、清晰、底层逻辑完全闭环。


九、总结(超精简版)

  • Docker 不跑 Java,只提供环境
  • 基础镜像已经预装 JDK,所以你不用装
  • JVM 是 Java 真正的执行引擎
  • 对象放堆,类信息放元空间
  • 类加载是懒加载,遵循双亲委派
  • Docker + JVM + 类加载 + 内存模型 → 微服务稳定运行

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐