Docker 里的 Java 微服务到底怎么跑?JVM、类加载、内存模型一篇讲透!
大家好,今天咱们用最通俗、最落地的方式,把 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 到底怎么跑起来?
流程非常清晰,一步都不会少:
- 容器启动
- 执行命令:java -jar xxx.jar
- JVM 启动
- JVM 去找 Jar 包里的入口类
- 开始类加载
- 把类放进内存
- 执行 main 方法
- 微服务启动成功
下面我们把最核心的 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 个步骤
- 加载:读取 .class 文件,变成二进制流
- 验证:检查类是否合法
- 准备:给静态变量分配内存
- 解析:符号引用变直接引用
- 初始化:执行静态代码,给静态变量赋值
只有执行到 new、调用静态方法、main方法 时,类才会被初始化。
七、双亲委派模型:类加载器的上下级规则
Java 有 3 层类加载器,上级优先加载,这就是双亲委派。
-
启动类加载器(Bootstrap)
加载 JRE/lib 下的核心类,比如 Object、String。 -
扩展类加载器(Extension)
加载 JRE/lib/ext 里的类。 -
应用类加载器(AppClassLoader)
加载我们自己写的业务类、Jar 包中的类。
为什么要双亲委派?
- 安全:防止别人篡改核心类(比如自己写一个 Object 替换 JDK 的)
- 唯一:保证一个类在 JVM 里只被加载一次
- 稳定:避免重复加载、类冲突
简单说:先问上级,上级找不到,自己再加载。
八、回到 Docker:完整运行链路
最后把今晚所有内容串成一条完整链路,你一看就懂:
- 编写微服务 → 打包 Jar
- Dockerfile 使用 FROM openjdk:8 基础镜像(自带 JDK)
- 把 Jar 复制进容器
- 启动容器 → 执行 java -jar
- JVM 启动
- 读取 Jar 包 META-INF/MANIFEST.MF 找到 Main-Class
- 应用类加载器开始加载主类
- 按照双亲委派规则加载所有依赖类
- 类信息进入元空间
- 对象创建进入堆内存
- 方法执行进入虚拟机栈
- main 方法运行 → Spring 启动 → 微服务成功运行
整个流程,就是这么简单、清晰、底层逻辑完全闭环。
九、总结(超精简版)
- Docker 不跑 Java,只提供环境
- 基础镜像已经预装 JDK,所以你不用装
- JVM 是 Java 真正的执行引擎
- 对象放堆,类信息放元空间
- 类加载是懒加载,遵循双亲委派
- Docker + JVM + 类加载 + 内存模型 → 微服务稳定运行
更多推荐




所有评论(0)