JSP 在 Ubuntu 上出问题时,最常见的难点不是“不会调试”,而是不知道该先看哪里:是语法报错、线程阻塞、内存压力,还是远程环境配置有问题。下面这篇内容按排查顺序重组了一遍,从日志入口、命令行工具到 IDE 和远程调试逐步展开,方便你根据现象快速选择合适的方法。
先从日志入手,确认问题落点
排查 JSP 运行错误,第一步通常不是直接下断点,而是先看日志。Ubuntu 系统层面的日志一般位于 /var/log/syslog 或 /var/log/messages;如果应用跑在 Tomcat 上,更重要的是查看 Tomcat 自身日志目录,例如 /var/log/tomcat9/,或者对应版本下的 logs 目录。
其中,catalina.out 往往是最先要看的文件。JSP 编译失败、运行时报错、类找不到、异常堆栈等信息,通常都会先出现在这里。对于大多数“页面打不开”或“请求直接报 500”这类问题,先确认 catalina.out 中的报错类型,往往就能判断后续该走语法排查、配置修复还是 JVM 分析路线。
命令行工具怎么配合使用
如果日志只能告诉你“出错了”,但还不足以定位根因,就可以进一步借助命令行工具缩小范围。
用 System.out.println() 快速看变量和流程
这是最直接、也最容易马上见效的方法。在可疑位置加入输出语句:
System.out.println("变量值:"+variable);
然后实时查看 Tomcat 日志:
tail -f /var/log/tomcat9/catalina.out
这种方式虽然朴素,但对判断变量值是否异常、流程是否进入某个分支非常有效,特别适合处理 JSP 页面中的简单逻辑问题。
用 jstack 排查线程阻塞和死锁
当页面响应很慢、请求卡住或者怀疑线程死锁时,可以用 jstack 查看线程堆栈。先找到 Tomcat 进程 ID:
ps -ef | grep tomcat
然后执行:
jstack -l <Tomcat进程ID>
输出结果里可以重点关注 RUNNABLE 状态的线程,以及是否存在多个线程互相等待资源的情况。
用 jmap 和 jstat 观察内存与 GC 压力
如果问题表现为 Tomcat 越跑越慢、频繁 Full GC 或怀疑内存泄漏,可以先看堆内存和 GC 指标。
查看堆内存分布:
jmap -heap <Tomcat进程ID>
它会显示 Eden、Survivor、Old Gen 等区域的使用占比,适合初步判断内存压力是否集中在老年代。
持续观察 GC 指标:
jstat -gcutil <Tomcat进程ID> 1000
这里的 1000 表示每秒刷新一次。排查时通常可以重点看 O(Old 区使用率)、YGC(Young GC 次数)等指标,判断 JVM 是否处于高压状态。
把错误信息放大:日志与错误页配置
当现有日志信息还不够用时,可以通过 JVM 和 JSP 配置,把更多细节主动暴露出来。

启用 GC 日志
在 Tomcat 的 bin/catalina.sh 中加入 JVM 参数:
JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/var/log/tomcat9/gc.log"
重启 Tomcat 后,会生成 gc.log 文件。这个日志适合用来分析垃圾回收频率、停顿情况,以及是否需要进一步调整内存参数。
配置 JSP 错误页输出完整异常
如果你希望 JSP 出错时直接拿到更完整的异常上下文,可以在页面顶部添加:
<%@ page isErrorPage="true" %>
再将错误跳转到专门的错误页,例如 error.jsp,并通过:
exception.printStackTrace()
输出详细堆栈。对于语法错误、空指针、业务逻辑异常,这种方式比只看浏览器里的报错页更直观。
打开 JSP 调试标记
另一个常见做法,是在 JSP 页面顶部加入:
<%@ page debug="true" %>
这样在 JSP 引擎处理页面报错时,服务器通常会输出更详细的信息,比如编译错误所在的具体行号和原因。这一招对定位 JSP 语法问题尤其省时间。
IDE 图形化调试适合处理什么问题
当问题已经不是“有没有报错”,而是“代码到底走到了哪一步”,IDE 的图形化调试就比纯日志更高效。Eclipse 和 IntelliJ IDEA 都能直接配合 Tomcat 做 JSP 调试。
常见做法包括:
- 在 JSP 的脚本段
<% ... %>或表达式<%= ... %>中设置断点; - 通过 IDE 的
Debug菜单启动 Tomcat; - 访问 JSP 页面后,在断点处暂停执行;
- 在调试窗口查看当前作用域中的变量值、对象属性和调用栈;
- 使用 Step Over、Step Into、Resume 等操作逐步验证执行路径。
这类方式特别适合排查条件分支判断错误、对象属性异常、方法链调用结果不符合预期等问题。
需要长期排查时,用日志框架替代临时输出
如果调试需求不只是一次性排错,而是希望后续也能稳定追踪 JSP 或相关后端逻辑,单靠 System.out.println() 就不够了。这时更适合接入日志框架,例如文中提到的 Log4j2。
添加依赖
如果项目使用 Maven,可以在 pom.xml 中加入 log4j-api 和 log4j-core 依赖,版本保持为 2.x.x。
配置输出级别与目标
在 src/main/resources 下创建 log4j2.xml,设置日志级别,例如 DEBUG,并定义输出格式与输出位置。常见配置会包含时间、线程、类名,以及输出到控制台或文件。
在代码中记录关键过程
通过 LogManager.getLogger() 获取 Logger 实例后,可以用下面的方式记录调试信息和异常:
logger.debug("调试信息")
logger.error("错误信息:"+e.getMessage())
日志可以输出到指定文件,例如 jsp-logs.log。相比临时打印,这种方式更适合保留现场、区分级别,也方便线上问题回溯。
远程服务器上的 JSP 怎么调试
如果问题只会在远程 Ubuntu 服务器上出现,本地复现不了,就需要用远程调试把 IDE 接到服务器上的 Tomcat。

先为 Tomcat 开启远程调试端口
在 catalina.sh 中加入 JVM 参数:
JAVA_OPTS="$JAVA_OPTS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
重启 Tomcat 后,调试端口 5005 会被打开。
再由本地 IDE 发起连接
接着在 IntelliJ IDEA 或 Eclipse 中创建 Remote Debug 配置,填入远程服务器 IP 和端口 5005。连接建立后,再访问目标 JSP 页面,就可以像本地调试一样命中断点、查看变量和调用栈。
这种方式最适合排查“只在线上发生”的环境差异问题,例如服务器配置不同、依赖版本差异,或者某些请求数据在测试环境根本出现不了。
一套更实用的排查顺序
如果你不想在每次报错时都从头试一遍,可以按下面这个顺序处理:
- 先看
catalina.out和系统日志,确认是编译错误、运行异常还是资源问题; - 用
System.out.println()和tail -f快速验证变量和执行路径; - 若出现卡顿、阻塞或无响应,再用
jstack看线程; - 若怀疑内存泄漏或 GC 异常,再用
jmap、jstat和 GC 日志继续分析; - 需要精确跟踪代码执行时,切到 IDE 断点调试;
- 问题只出现在远程服务器时,再启用远程调试。
这样处理的好处是,先用成本最低的手段排掉大部分常见问题,再逐步进入更重的调试工具,效率通常更高。









