我们现场更新时,通常都是提前将后端jar 包替换了,然后到了更新的时间,只需要更新相关的前端文件和修改各种配置,就行了。
但是最近一次更新操作中,忽然发现,提前更新后端包,会存在依赖丢失问题????
文章目录
- 结论
- docker 容器的挂载逻辑
- 一、挂载类型与实时性
- 二、实时同步的实现机制
- 三、例外情况与注意事项
- 四、验证实时性的方法
- java -jar 是干什么
- 一、JAR包运行的本质
- 二、实际实现流程
- 三、常见问题与底层机制
- 四、扩展:调试与优化
- 总结
- java 启动时会将jar 包中那些内容导入内存中
- 一、类文件与元数据
- 二、静态资源与配置文件
- 三、类加载机制的关键阶段
- 四、动态加载与内存管理
- 五、优化与注意事项
结论
就是不能换(因为判断修改是否影响的逻辑比较复杂),之前认为能换,是认为jar 包线上系统不会重新调用相关内容,结合下述相关知识,可以知道,在java 服务运行时,至少有动态加载这种情况时,(开发是动静分离的,静态资源应该可以忽略)服务会重新搜索jar 包的相关.class 文件,重新生成类对象。
docker 容器的挂载逻辑
Docker 容器的挂载逻辑是实时同步的,其数据交互机制依赖于宿主机的文件系统通知功能。具体逻辑如下:
一、挂载类型与实时性
- 绑定挂载(Bind Mount)
将宿主机目录直接映射到容器内,修改任一端(宿主机或容器)的文件会立即同步到另一端。这是最常用的实时挂载方式,适用于开发环境中的代码热更新场景12。 - 卷挂载(Volume Mount)
由 Docker 管理的持久化存储卷,数据存储在宿主机特定路径(如<font style="color:rgba(0, 0, 0, 0.9);">/var/lib/docker/volumes</font>
)。与绑定挂载类似,修改实时同步,但更适合生产环境的数据持久化需求49。 - 临时文件系统(tmpfs)
数据仅存储在内存中,容器重启后丢失。虽然数据不持久化,但容器运行期间的读写操作也是实时生效的25。
二、实时同步的实现机制
- 文件系统层映射
Docker 通过 Linux 的命名空间(Namespace)和控制组(cgroup)技术,将宿主机的文件系统路径直接映射到容器内部,底层依赖宿主机文件系统的 inotify 机制实现实时通知911。 - 写时复制(Copy-on-Write)优化
仅当文件被修改时触发同步,未修改的文件仍共享宿主机的原始数据层,避免不必要的性能开销7。
三、例外情况与注意事项
- 网络文件系统(NFS)延迟
若挂载的是远程存储(如 NFS),同步时效性受网络环境影响,可能出现短暂延迟12。 - 容器内应用缓存
某些应用程序(如数据库)可能缓存数据到内存,导致文件系统修改不会立即体现在应用层,需手动刷新或重启服务38。 - 权限问题
若宿主机与容器内用户权限不匹配(如 UID/GID 不一致),可能导致挂载目录无法实时写入610。
四、验证实时性的方法
可通过以下命令快速测试:
bash# 宿主机操作
echo "test" > /host/path/file.txt # 进入容器查看
docker exec -it container_name cat /container/path/file.txt
若容器内能立即看到更新内容,则证明挂载实时生效12。
总结:Docker 的挂载机制默认是实时同步的,但需结合具体挂载类型和应用场景综合判断。对于关键业务,建议通过日志或主动监控进一步验证数据一致性
java -jar 是干什么
通过<font style="color:rgba(0, 0, 0, 0.9);">java -jar</font>
命令运行Java JAR包的本质是通过JVM加载JAR文件中的主类并执行其**<font style="color:rgba(0, 0, 0, 0.9);">main()</font>**
方法,其实现流程可分为以下核心步骤:
一、JAR包运行的本质
- JAR文件结构
JAR包本质是ZIP格式的压缩文件,包含:- 编译后的
<font style="color:rgba(0, 0, 0, 0.9);">.class</font>
文件; - 资源文件(如图片、配置文件);
<font style="color:rgba(0, 0, 0, 0.9);">META-INF/MANIFEST.MF</font>
清单文件(指定主类、依赖路径等元数据)18。
- 编译后的
- 清单文件的关键作用
<font style="color:rgba(0, 0, 0, 0.9);">MANIFEST.MF</font>
中的<font style="color:rgba(0, 0, 0, 0.9);">Main-Class</font>
属性决定了入口类,JVM通过此属性定位并调用指定类的<font style="color:rgba(0, 0, 0, 0.9);">main()</font>
方法。若未正确配置此属性,会报错“找不到主清单属性”14。 - 类加载机制
JVM将JAR包视为一个类路径(Classpath),通过内置的<font style="color:rgba(0, 0, 0, 0.9);">JarClassLoader</font>
加载其中的类。若依赖其他JAR,需通过<font style="color:rgba(0, 0, 0, 0.9);">-cp</font>
参数显式指定路径79。
二、实际实现流程
- 环境检查
- 检查系统是否安装兼容版本的JRE/JDK(通过
<font style="color:rgba(0, 0, 0, 0.9);">java -version</font>
验证)18。 - 确保
<font style="color:rgba(0, 0, 0, 0.9);">JAVA_HOME</font>
环境变量指向正确的JDK路径19。
- 检查系统是否安装兼容版本的JRE/JDK(通过
- 命令行解析
<font style="color:rgba(0, 0, 0, 0.9);">java -jar</font>
命令触发JVM启动,并解析参数:
java -jar yourfile.jar [args...]
- <font style="color:rgba(0, 0, 0, 0.9);">若需指定堆内存或垃圾回收策略,可添加</font>`<font style="color:rgba(0, 0, 0, 0.9);">-Xmx</font>`<font style="color:rgba(0, 0, 0, 0.9);">、</font>`<font style="color:rgba(0, 0, 0, 0.9);">-XX:+UseG1GC</font>`<font style="color:rgba(0, 0, 0, 0.9);">等参数</font>[<font style="color:rgb(22, 119, 255);">1</font>](https://docs.pingcode.com/baike/254964)[<font style="color:rgb(22, 119, 255);">13</font>](https://blog.csdn.net/AttleeTao/article/details/104149794)<font style="color:rgba(0, 0, 0, 0.9);">。</font>
- JAR包加载与清单读取
- JVM解压JAR包至临时目录(或直接从内存读取)。
- 解析
<font style="color:rgba(0, 0, 0, 0.9);">META-INF/MANIFEST.MF</font>
文件,提取<font style="color:rgba(0, 0, 0, 0.9);">Main-Class</font>
和<font style="color:rgba(0, 0, 0, 0.9);">Class-Path</font>
(若存在依赖库)48。
- 主类加载与执行
- 通过反射机制加载
<font style="color:rgba(0, 0, 0, 0.9);">Main-Class</font>
指定的类。 - 调用该类的
<font style="color:rgba(0, 0, 0, 0.9);">public static void main(String[] args)</font>
方法,传递命令行参数<font style="color:rgba(0, 0, 0, 0.9);">args</font>
913。
- 通过反射机制加载
- 依赖处理
- 内嵌依赖:若使用Fat JAR(包含所有依赖),直接加载内部类。
- 外部依赖:通过
<font style="color:rgba(0, 0, 0, 0.9);">-cp</font>
参数或<font style="color:rgba(0, 0, 0, 0.9);">MANIFEST.MF</font>
中的<font style="color:rgba(0, 0, 0, 0.9);">Class-Path</font>
指定外部JAR路径79。
三、常见问题与底层机制
- 权限问题
Linux系统需确保JAR文件有执行权限(<font style="color:rgba(0, 0, 0, 0.9);">chmod +x yourfile.jar</font>
)813。 - 版本兼容性
JVM版本需与编译JAR包的JDK版本匹配,否则可能报错<font style="color:rgba(0, 0, 0, 0.9);">UnsupportedClassVersionError</font>
17。 - 进程管理
- 前台运行:直接执行
<font style="color:rgba(0, 0, 0, 0.9);">java -jar</font>
,关闭终端则进程终止。 - 后台运行:使用
<font style="color:rgba(0, 0, 0, 0.9);">nohup java -jar &</font>
或<font style="color:rgba(0, 0, 0, 0.9);">javaw</font>
(Windows)实现守护进程313。
- 前台运行:直接执行
四、扩展:调试与优化
- 调试命令
添加<font style="color:rgba(0, 0, 0, 0.9);">-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005</font>
启用远程调试3。 - 性能优化
- 通过
<font style="color:rgba(0, 0, 0, 0.9);">-Xms</font>
和<font style="color:rgba(0, 0, 0, 0.9);">-Xmx</font>
调整堆内存大小。 - 使用
<font style="color:rgba(0, 0, 0, 0.9);">-XX:+HeapDumpOnOutOfMemoryError</font>
生成内存溢出快照113。
- 通过
总结
运行JAR包的本质是JVM根据清单文件定位入口类并执行其主方法,而流程涵盖环境检查、文件解析、类加载到程序执行的完整链路。理解这一机制有助于解决依赖冲突、版本不兼容等问题,并优化部署流程。
java 启动时会将jar 包中那些内容导入内存中
ava 启动时加载 JAR 包中的内容到内存的过程主要涉及类加载机制和资源管理机制。以下是具体会导入内存的内容及加载逻辑:
一、类文件与元数据
- Class 文件
JAR 包中的<font style="color:rgba(0, 0, 0, 0.9);">.class</font>
文件会被类加载器按需加载到内存中,并生成对应的<font style="color:rgba(0, 0, 0, 0.9);">java.lang.Class</font>
对象。- 加载顺序:主类及其直接依赖优先加载,其他类延迟加载(例如通过
<font style="color:rgba(0, 0, 0, 0.9);">new</font>
或反射调用时触发加载)12。 - 存储区域:类元信息(如方法、字段结构)存储在方法区(JDK8+ 为元空间 Metaspace),静态变量存储在堆内存中的 Class 对象中。
- 加载顺序:主类及其直接依赖优先加载,其他类延迟加载(例如通过
- MANIFEST.MF 文件
该文件位于<font style="color:rgba(0, 0, 0, 0.9);">META-INF</font>
目录下,包含 JAR 包的元数据(如主类定义、类路径等)。启动时会解析此文件以确定入口类和依赖关系812。
二、静态资源与配置文件
- 配置文件
如<font style="color:rgba(0, 0, 0, 0.9);">application.properties</font>
、<font style="color:rgba(0, 0, 0, 0.9);">XML</font>
等文件,通过<font style="color:rgba(0, 0, 0, 0.9);">ClassLoader.getResourceAsStream()</font>
读取到内存中。但并非所有文件都会被自动加载,需显式调用资源读取代码才会导入11。- 示例:Spring 项目启动时,通过
<font style="color:rgba(0, 0, 0, 0.9);">FileSystemXmlApplicationContext</font>
加载外部配置文件11。
- 示例:Spring 项目启动时,通过
- 静态资源
图片、模板等非类文件通常通过类路径访问,按需加载到内存。例如:
java复制
InputStream is = MyClass.class.getResourceAsStream("/images/logo.png");
三、类加载机制的关键阶段
- 加载(Loading)
将<font style="color:rgba(0, 0, 0, 0.9);">.class</font>
文件读入内存,生成<font style="color:rgba(0, 0, 0, 0.9);">Class</font>
对象。类加载器层级包括:- Bootstrap ClassLoader:加载核心 JDK 类(如
<font style="color:rgba(0, 0, 0, 0.9);">java.lang.*</font>
)。 - Extension ClassLoader:加载
<font style="color:rgba(0, 0, 0, 0.9);">jre/lib/ext</font>
目录下的扩展 JAR。 - System ClassLoader:加载应用类路径(含用户 JAR 包)12。
- Bootstrap ClassLoader:加载核心 JDK 类(如
- 连接(Linking)
- 验证:检查字节码是否符合 JVM 规范。
- 准备:为静态变量分配内存并赋默认值(如
<font style="color:rgba(0, 0, 0, 0.9);">int</font>
初始化为 0)。 - 解析:将符号引用转为直接引用(如方法实际内存地址)12。
- 初始化(Initialization)
执行静态代码块和显式静态变量赋值(如<font style="color:rgba(0, 0, 0, 0.9);">static int a = 5;</font>
)。
四、动态加载与内存管理
- 动态加载
可通过<font style="color:rgba(0, 0, 0, 0.9);">URLClassLoader</font>
在运行时动态加载外部 JAR 包中的类(需调用<font style="color:rgba(0, 0, 0, 0.9);">addURL()</font>
方法)7。例如:
java复制
URLClassLoader loader = new URLClassLoader(new URL[]{new File("lib/external.jar").toURI().toURL()});
Class<?> clazz = loader.loadClass("com.example.ExternalClass");
- 内存区域
- 堆(Heap):存储对象实例和数组。
- 方法区(Metaspace):存储类元信息、常量池。
- 栈(Stack):存储线程方法调用的局部变量和操作数612。
五、优化与注意事项
- 内存参数
可通过 JVM 参数控制内存分配:
bashjava -Xms512m -Xmx1024m -jar app.jar # 设置堆内存初始和最大值[6]()
- 资源释放
未使用的类可能被垃圾回收(需满足类卸载条件),但 Metaspace 默认不会主动释放,需配置<font style="color:rgba(0, 0, 0, 0.9);">-XX:MetaspaceSize</font>
和<font style="color:rgba(0, 0, 0, 0.9);">-XX:MaxMetaspaceSize</font>
。
总结:Java 启动时主要加载 JAR 包中的类文件、静态资源和配置文件到内存,具体内容取决于代码的显式调用和类依赖关系。类加载器层级和 JVM 内存模型共同决定了资源在内存中的分布与管理。