简介:面向 32 位 Windows 系统的 Java 6 更新 6(JDK 1.6.0_06)开发工具包,适合需要在旧版 Java 环境下编译、调试和运行程序的开发者,尤其适用于维护遗留系统或学习 Java 6 特性的场景。压缩包共 690 个文件,约 40.89MB,以 exe 执行程序、dll 动态库、jar 工具包、properties 配置及 html/rtf 文档为主,包含完整的 bin 工具目录、jre 运行时、include 头文件及中文版 README 等说明材料。目前已有 324 人学习/浏览。内置编译器、调试器、文档生成器和 Java 虚拟机,并附带时区映射、安全证书等辅助资源,安装后即可搭建完整的 Java 6 开发运行环境,省去逐一配置组件的麻烦,是 32 位 Windows 平台上进行旧版 Java 应用开发与测试的实用基础工具。
1. JDK 1.6 在 Windows 上的现实价值:不是考古,是兼容老系统的刚需
直到今天,我电脑里还留着一个 jdk1.6.0_06 解压包。很多人觉得这版本早该进博物馆,但当你接手的某个老项目还跑在 Spring 2.x 或 Struts 1.x 上,编译产物必须挂在 1.6 的 JVM 里才能稳定运行,你就会明白这个老掉牙的 JDK 是你绕不开的最后一根救命稻草。这篇笔记记录我拆这个资源包时的完整落地思路:解压版怎么装、环境变量怎么配、多版本怎么共存、JVM 参数怎么调、哪些坑必须躲开。适合要维护老系统、做 JVM 底层分析,或者给老机器搭环境的从业者,新手跟着做也能少白折腾半天。
2. 安装与多版本共存:用解压版 JDK 6 和 JDK 8 同机切换
2.1 安装版与解压版的差别,以及为什么我推荐解压版
JDK 1.6 在 Windows 上常见两种形态:一种是官方安装向导,装完自动写注册表、配好 PATH,省事但霸道;另一种就是这个资源包里的解压版,整个 JDK 就是一层目录,根目录下能看到 bin、lib、jre 等子目录,没有任何安装动作。
我强烈建议在老系统维护场景里用解压版,理由很实际:安装版会把 java.exe、javac.exe 塞进 System32 或者注册表里,等你再装一个 JDK 8,两个版本会为了 PATH 里的优先级打得不可开交。解压版则完全没有这个副作用,它不碰系统全局设置,想用哪个版本就在脚本里把 JAVA_HOME 指到哪一层目录,切换成本几乎为零。
使用前先确认目录结构,解压后应该是这样:
C:\jdk1.6.0_06 ├─ bin\ # java.exe、javac.exe、javap.exe、jvisualvm.exe 等 ├─ jre\ ├─ lib\ # rt.jar 在这里的上层目录,实际是 jre\lib\rt.jar └─ COPYRIGHT我用的是 C 盘根目录下的C:\jdk1.6.0_06,路径短、无空格,后面所有脚本都能少踩引号相关的坑。如果你解压到了带空格的目录,比如C:\Program Files\Java\jdk1.6.0_06,那后面写脚本就必须给每个路径加双引号,非常容易漏。
2.2 JAVA_HOME、PATH、CLASSPATH:三个环境变量的正确写法
很多老教程会教你设置三个系统变量:JAVA_HOME、PATH、CLASSPATH。实际在 JDK 6 时代,CLASSPATH 已经不是必需品,但你维护的老项目里很可能有脚本依赖它,所以环境变量还是要配全。我一般会在 cmd 里用批处理来配,这样只对当前窗口生效,不影响系统全局。
@echo off rem 临时把当前命令行窗口的 JAVA_HOME 指向 JDK 6 set JAVA_HOME=C:\jdk1.6.0_06 set PATH=%JAVA_HOME%\bin;%PATH% set CLASSPATH=.;%JAVA_HOME%\lib;%JAVA_HOME%\jre\lib java -version javac -version逻辑说明:这个脚本只修改当前 cmd 窗口的环境变量。第二条命令把 JDK 6 的 bin 目录插到 PATH 最前面,这样输入 java、javac 时系统优先找到 JDK 6 的可执行文件。
参数说明:
set JAVA_HOME指向解压包的根目录,不要指到 bin 层。PATH里%JAVA_HOME%\bin是必需的,java.exe 和 javac.exe 都在这。CLASSPATH开头的.表示当前目录,老项目运行时经常需要加载当前目录下的类;后面两个 lib 路径是给老 API 兜底的,新项目不需要这么写。
如果你想把配置永久写入系统,可以在“我的电脑 → 属性 → 高级 → 环境变量”里手动加同样的 JAVA_HOME,并把%JAVA_HOME%\bin追加到系统 PATH。这里有一个最常见的错误:追加 PATH 时把原来的路径覆盖了,导致系统里其他依赖 Java 的工具全部失效。我在处理某次自动化构建环境时见过不下三次这种翻车,建议修改前先把原 PATH 的值复制到文本文件里保存。
2.3 写一个 switch-jdk.bat,实现 JDK 6 / JDK 8 一键切换
老项目维护者电脑上不止一个 JDK 是很正常的事。我一般是 JDK 6 和 JDK 8 共存,JDK 8 跑新服务,JDK 6 专门给老工程。手动改环境变量太麻烦,我写了一个切换脚本放在桌面:
@echo off set /p JDK_VER=请输入目标版本(6 或 8): rem 默认先指向 JDK 6,如果输入 8 则切换到 JDK 8 set "JAVA_HOME=C:\jdk1.6.0_06" if "%JDK_VER%"=="8" set "JAVA_HOME=C:\jdk1.8.0_191" rem 重置 PATH 基础部分,避免之前追加的 bin 路径层层堆叠 set "PATH=%SystemRoot%\system32;%SystemRoot%;%JAVA_HOME%\bin;%PATH%" java -version javac -version逻辑说明:脚本用set /p接收输入,默认走 JDK 6 分支;输入 8 时把 JAVA_HOME 换成 JDK 8 的目录。PATH 的前两段先重置到系统基础路径,再把当前版本的 bin 追加进去,这样每次执行脚本都从同一基线开始,不会因为反复运行导致 PATH 越来越长。
参数说明:
C:\jdk1.8.0_191是我本机的 JDK 8 目录,你按自己实际安装路径改。- 脚本最后同时输出 java 和 javac 版本,方便一眼确认切换成功。
- javac 输出的是编译版本,java 输出的是运行版本,两者不一致说明 PATH 里混入了别家的 Java。
3. 编译与运行参数:javac / java 的关键选项和编码坑
3.1 source 与 target 参数:别再看到“无效的发行版本”就蒙圈
JDK 6 的 javac 默认按当前 JDK 版本编译,生成的 class 是 50.0 版本,对应 Java 1.6。但你在新环境上用新版 JDK 去编译老项目时,最常见的报错是无效的目标发行版本: 1.6或者错误: 不支持发行版本 5。原因很简单:新版 javac 虽然支持-source 1.6 -target 1.6,但如果这个新版本已经在较新的迭代中移除了对旧 source 的支持,就会直接拒绝执行。
用 JDK 6 自带的 javac 是最稳的做法,但在 JDK 6 上编译时有一点要特别注意:必须显式声明 source 和 target,避免编译器用默认值猜测。示例:
cd /d D:\legacy_project\src D:\jdk1.6.0_06\bin\javac -encoding GBK -source 1.6 -target 1.6 -d D:\legacy_project\classes Main.java逻辑说明:-source 1.6告诉编译器源码用的是 Java 6 语法,-target 1.6要求生成的 class 字节码版本为 50.0。-d指定 class 输出目录,避免 .class 和 .java 混在一起。
参数说明:
-encoding GBK这个参数后面单独讲,但它是老项目编译的命门。-source和-target必须同时出现,只写一个容易引发 “source 1.6 需要 target 1.6” 的报错。- 如果不加
-d,class 文件会生成在和 java 文件相同的目录,一多就乱。
如果你只有新版 JDK,也要坚持编出 JDK 6 能识别的字节码,可以用-source 1.6 -target 1.6,但新版编译器会警告bootstrap class path not set。这个警告的意思是新版 javac 默认关联的是新版 rt.jar,编出来的 class 可能引用了 JDK 6 没有的 API。后半段我会专门讲用-bootclasspath解决,这里先记住:这个警告不能无视。
3.2 老项目的编码陷阱:GBK 与 UTF-8 在编译期的翻转
在 JDK 6 的年代,国内绝大多数的 Window 老项目源码都是 GBK 编码。而现在的 IDE 和命令行环境默认 UTF-8,两者一碰撞,编译期就开始报乱码。
最常见的报错是错误: 编码 GBK 的不可映射字符,或者编译成功后运行输出一堆问号。现象是同一个源文件里中文字符串变成了鍙傛暟这类完全看不懂的字节序列。
我处理这个问题的方法极其简单:编译时永远显式声明编码,不依赖系统默认值。老项目源码是 GBK 就用-encoding GBK,如果项目当年是 UTF-8 保存的,那就用-encoding UTF-8。判断方法是用文本编辑器看源码的中文注释是否正常显示,或者用类似 notepad++ 的状态栏编码提示。
cd /d D:\legacy_project D:\jdk1.6.0_06\bin\javac -encoding GBK -source 1.6 -target 1.6 -d classes -cp lib\* src\com\example\Main.java逻辑说明:-cp lib\*把依赖的 jar 包全部引入,多个 jar 不用逐个列出,星号通配符只对 jar 生效,对 class 目录不生效。
编码问题在运行时同样存在。如果代码里用new String(bytes)或者BufferedReader读取配置文件,读取时没有指定 charset,那么在 Windows 命令行窗口里看到的输出可能就是乱码。老项目的配置文件多为 GBK,建议在代码读取处统一用new InputStreamReader(stream, "GBK"),这属于源码层面的修改,但比绕过编码问题更治本。我在某次维护仓储系统时,就是因为没注意这个细节,导致了整整一个下午的排查,那是很典型的浪费时间。
3.3 java 运行期:-cp 与 -jar 的取舍,以及 classpath 的完整形态
编译完老项目,运行是下一步。初学者最容易掉进去的坑是把-cp和-jar混用。当你在命令行写成java -cp myapp.jar -jar Main.jar时,Java 会直接忽略-cp,以-jar为准运行 jar 包自身的 manifest 里指定的 Main-Class。如果你的 jar 包依赖外部 jar,必须在 manifest 的 Class-Path 里写清楚,否则运行时必然ClassNotFoundException。
我跑老项目更倾向于不用-jar,而是用完整 classpath 指定主类,这样依赖关系全在明面上:
cd /d D:\legacy_project D:\jdk1.6.0_06\bin\java -cp classes;lib\*;config com.example.Main逻辑说明:-cp后面三项是 class 目录、依赖 jar、配置文件目录。配置文件目录放在 classpath 里,代码才能用getResourceAsStream("config.properties")找到它,这是老项目最常见的资源访问方式。
参数说明:
classes是编译产物目录。lib\*是 lib 下所有 jar 的通配符,注意这是命令行层面的通配,不会递归到子目录。config目录里有 properties 或 xml,代码能读到说明 classpath 生效。- 主类必须写全限定名
com.example.Main,不能带 .class 后缀。
这里有一个容易忽略的细节:-cp的路径分隔符在 Windows 上是分号;,不是冒号。有人把 Linux 的习惯带过来,一运行就报找不到或无法加载主类,这类问题我能在一分钟内定位,因为几乎全是分隔符写错。
4. JVM 参数调优:PermGen、堆内存与老程序性能下限
4.1 PermGen 是什么?为什么 JDK 6 必须手动关注它
JDK 6 的 JVM 内存区域中有一个叫 PermGen(永久代)的地方,专门存放类的元数据、方法信息、静态变量和常量池。你每加载一个 class,JVM 就要往 PermGen 里塞一份类的元信息。JDK 6 之前的默认上限只有 64 MB,对一个大点的老 Web 应用来说非常容易爆。
JDK 8 之后这块被元空间取代,不再有PermGen Space这个报错。但你现在跑的是 JDK 6,就必须面对这个时代特性。老项目启动时报java.lang.OutOfMemoryError: PermGen space,十有八九是 PermGen 不够用,而不是堆内存不够。
为什么会爆?两个高发场景:一是 Web 容器加载了大量第三方 jar,每个 jar 里都有一票类;二是开发环境做了热部署,每热部署一次就重新加载一批 class,旧 class 的元信息无法完全回收,PermGen 越积越满。我接手的某次维护中,某旧版容器连着热部署五次之后必现 PermGen 溢出,无论业务代码多简单,原因就在这。
4.2 堆内存与 PermGen 的组合参数:给老程序一个合理下限
JDK 6 启动参数里,堆内存和 PermGen 要分开设置。堆内存用-Xms和-Xmx,PermGen 用-XX:PermSize和-XX:MaxPermSize。下面这组是我给一个老项目做常态运行时的推荐起点:
D:\jdk1.6.0_06\bin\java -Xms256m -Xmx1024m -XX:PermSize=128m -XX:MaxPermSize=256m -cp classes;lib\* com.example.Main逻辑说明:-Xms256m是启动时分配的初始堆大小,-Xmx1024m是堆上限。-XX:PermSize=128m是初始 PermGen,-XX:MaxPermSize=256m是 PermGen 上限。
参数说明:
- 堆内存和 PermGen 是两回事,千万别以为把
-Xmx加大就能解决 PermGen 溢出。 -XX:PermSize设大一点能减少 JVM 运行中动态扩容带来的性能损耗,老应用启动慢的,可以考虑初始值直接等于最大值。- 如果应用加载大量动态生成的类(比如 CGLIB 代理类),256m 还不够,可以再往上加到 512m,但要先看你机器的物理内存。
一个容易被忽略的细节:在 32 位 JDK 下,堆加 PermGen 加 JVM 自身开销不能超过 4 GB 的寻址范围,所以你设-Xmx2048m -XX:MaxPermSize=512m时,如果机器内存小,JVM 可能启动报错。遇到这种情况,优先考虑把这台老应用迁移到 64 位 JDK 上跑,而不是硬调参数。
4.3 打开 GC 日志:让 Full GC 不再是黑匣子
调 JVM 参数最忌讳凭感觉,我给老项目做评估时第一件事永远是打开 GC 日志。JDK 6 的 GC 日志参数不算多,但足够定位问题:
D:\jdk1.6.0_06\bin\java -Xloggc:D:\legacy_project\logs\gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -cp classes;lib\* com.example.Main逻辑说明:-Xloggc指定 GC 日志输出文件,-XX:+PrintGCDetails输出每次 GC 的详细数据,包括年轻代、老年代、回收前后占用,-XX:+PrintGCDateStamps给每条日志打上时间戳。
GC 日志打开后重点看两类信息:
- 日志里频繁出现
Full GC,说明老年代长期饱和,这通常意味着堆太小或代码里有内存泄漏。 - 出现
PermGen相关的 GC 记录,并且回收效果极差,说明 PermGen 设置不足或者有类加载器泄漏。
我一般会先让老项目带 GC 日志跑半天,再打开日志文件看 Full GC 的时间间隔。如果每几分钟就来一次,不管代码逻辑多简单,都要先把-Xmx调大再看。有一次我排查一个定时任务服务,GC 日志显示每次 Full GC 后老年代占用只降了几个百分点,最后定位到是一个静态集合不断存数据不清理,这是典型的隐性内存泄漏。这类问题不借助 GC 日志,光看代码要排查很久。
5. 避坑笔记:JDK 6 在 Windows 上常见的五个翻车现场
5.1 双击 java.exe 闪退,命令提示符里也说不是内部或外部命令
现象:从解压目录直接双击运行 java.exe,窗口一闪而过,什么都看不到;在 cmd 里敲 java -version,提示不是内部或外部命令。
原因:双击运行是独立进程,窗口关了输出就没了,这是正常现象;cmd 里报错是因为 PATH 没有指向解压包的 bin 目录。
解决:不要双击,在 cmd 里先执行cd /d C:\jdk1.6.0_06\bin,再执行java -version。如果这能输出版本,说明 JDK 本身没坏,只是环境变量没配。如果在这个目录下都报错,检查一下解压包是否完整,bin 目录下有没有 java.exe 和 javac.exe。
5.2 编译报“编码 GBK 的不可映射字符”或“编码 UTF-8 的不可映射字符”
现象:javac 编译老项目源码时,报错信息指向中文字符,有时显示成乱码,有时直接说不可映射。
原因:源文件的实际编码和 javac 使用的编码不一致。老项目源码是 GBK,而新版 javac 按平台默认 UTF-8 读取,或者反过来。
解决:编译命令里显式加-encoding GBK或-encoding UTF-8,以源文件实际保存编码为准。这个参数要放在 javac 所有参数的最前面,因为如果位置不对,javac 可能先按错误编码读取了源码,再来处理参数就没意义了。我踩过这个坑是某次把-encoding放在了-d后面,结果报错完全没变化,调换顺序后立刻正常。
5.3 运行报“Unsupported major.minor version 51.0”
现象:在 JDK 6 环境下运行程序,抛出UnsupportedClassVersionError,提示不支持 51.0。
原因:51.0 对应 JDK 7 的字节码版本。你手里的 class 是 JDK 7 及以上编译器编出来的,JVM 6 不认。这个报错说明编译和运行的 JDK 版本不一致,和代码写法没关系。
解决:用javap -verbose查看 class 文件的 major version,确认是 50 才对应 JDK 6;然后重新用 JDK 6 的 javac 编译,或者按第 3 章的方法加-source 1.6 -target 1.6。如果 class 是从 jar 里来的,先确认 jar 是否真的为 JDK 6 编译,别只看文件名带不带版本号。
5.4 安装目录带空格,启动脚本全部识别不到 Java
现象:我把 JDK 6 解压到C:\Program Files\Java\jdk1.6.0_06,写好的启动脚本运行时提示找不到 Java,或者提示找不到主类。
原因:老项目的启动脚本很多是当年顺手写的,没有给路径加双引号。遇到含空格的路径,系统把C:\Program当成程序名,后面的Files\Java\...全成了参数,脚本自然失效。
解决:重新解压到C:\jdk1.6.0_06这类无空格路径,这是最省事的,不用去改一堆历史脚本。如果项目脚本里硬编码了旧路径,那只能逐一把路径加双引号,但我会优先考虑移动 JDK 目录,因为历史遗留脚本里加引号很容易漏掉某些隐藏调用。
5.5 开发环境热部署几次后,必现 PermGen space 溢出
现象:某个老应用在开发环境里用容器热部署,改几次代码后重启模块,马上报java.lang.OutOfMemoryError: PermGen space,重启 JVM 又恢复正常。
原因:热部署每次都会用新的类加载器重新加载 class,旧的类加载器和它加载过的类元信息无法被完全回收,PermGen 被一点点耗尽。这是 JDK 6 时代的老毛病,在 JDK 8 里才通过元空间稍作缓解。
解决:开发期间把-XX:MaxPermSize调大,比如 256m 起步;更重要的是减少热部署次数,需要频繁验证时直接整机重启,而不是反复 reload 模块。如果想彻底治好,只能做迁移评估,把运行环境升级到 JDK 8,但迁移意味着要重新验证所有依赖库的兼容性,这个决策只能由项目负责人权衡,开发过程中先让 PermGen 上限宽裕一点顶住。
6. 验证与进阶:用 javap 确认字节码版本并做降级编译
6.1 一条命令验证解压版 JDK 6 是否可用
配好环境变量后,验证不要只看 java -version 就结束。我习惯连续跑三条命令确认状态:
C:\jdk1.6.0_06\bin\java -version C:\jdk1.6.0_06\bin\javac -version echo %JAVA_HOME%第一条确认运行版本是 1.6.0_06,第二条确认编译器版本一致,第三条确认 JAVA_HOME 指向解压根目录而不是 bin 目录。如果 java 和 javac 版本不一致,说明 PATH 里存在两个 JDK,这是环境变量配置最常见的隐患。
6.2 用 javap 反编译 class,确认字节码版本是否为 50
接手一个不明来历的旧 class 文件时,不要猜它是什么 JDK 编出来的,直接反编译看字节码版本:
C:\jdk1.6.0_06\bin\javap -verbose Main.class | findstr "major"输出中如果显示major version: 50,说明这个 class 是 JDK 6 编出来的;51 是 JDK 7,52 是 JDK 8。这个命令的价值在于:当你把某个 jar 包部署到 JDK 6 环境前,先扫一遍关键 class 的版本,能提前发现不兼容问题,避免部署后线上才报 UnsupportedClassVersionError。
6.3 进阶技巧:用新版 JDK 编出纯正 JDK 6 字节码
如果你手里的环境只剩新版 JDK,但目标机器必须跑 JDK 6,有一个技巧可以把新代码编成 JDK 6 能跑的 class。关键在-bootclasspath,它让 javac 对照 JDK 6 的核心库来检查 API 调用:
C:\jdk1.8.0_191\bin\javac -source 1.6 -target 1.6 -bootclasspath C:\jdk1.6.0_06\jre\lib\rt.jar -encoding GBK -d classes src\com\example\Main.java逻辑说明:-bootclasspath指定编译器使用的核心类库路径。这里指向 JDK 6 的 rt.jar,javac 就会以 JDK 6 的 API 集合为基准,一旦你的代码调用了 JDK 7 之后才有的方法,编译器会直接报错,而不是编译出在 JDK 6 上运行才爆炸的字节码。
参数说明:
-source 1.6和-target 1.6控制语法和字节码版本。-bootclasspath必须指向 JDK 6 的jre\lib\rt.jar,不要使用新版 JDK 自带的 rt.jar,否则等于没设。- 如果你用 JDK 8 编译且不指定 bootclasspath,虽然也能生成 major 50 的 class,但可能悄悄引用了新 API,编译期内无法识别,这类隐患的排查成本极高。
这个技巧的核心价值在于:它让你在不安装老 JDK 的前提下也能产出兼容老环境的构建物。但从那以后,我每次处理这类老项目,都强制自己先走一遍 javap -verbose 验证字节码版本,再用-bootclasspath指向对应 rt.jar 重新编译,确认编译期和运行期都不会埋雷,然后才敢把产物交给运维去部署。这套流程帮我少翻了好几次车,希望帮到你。
本文还有配套的精品资源,点击获取