面對客戶抱怨「頁面太慢」、流量暴增時系統崩潰,本文提供七個立竿見影的調優方向,涵蓋程式碼層面與容器配置層面。
Servlet 生命週期中 init() 僅執行一次,適合初始化昂貴資源:
public class ControllerServlet extends HttpServlet {
private DataSource dataSource;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
try {
Context ctx = new InitialContext();
dataSource = (DataSource) ctx.lookup("jdbc/testDS"); // JNDI 查詢只做一次
} catch (NamingException e) {
throw new ServletException("DataSource lookup failed", e);
}
}
public DataSource getDataSource() { return dataSource; }
}
避免每請求重複 JNDI lookup、建立連線池。
reloadable="true" 方便熱部署。| 誤用 | 後果 | 修正 |
|---|---|---|
| 預設每頁建立 Session | 大量無用 Session 物件、序列化開銷 | JSP 頁面指令 <%@ page session="false" %> |
| Session 存放大物件 (查詢結果集、集合) | 序列化/反序列化慢、記憶體暴增 | 只存 ID/Token,資料放快取 (Redis/Ehcache) |
| 用完不釋放 | Session 逾時前長期佔用記憶體 | 明確呼叫 session.invalidate() |
| 逾時設定過長 (預設 30 分鐘) | 併發用戶多時記憶體壓力大 | 依業務縮短 (如 10-15 分鐘) |
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String encoding = req.getHeader("Accept-Encoding");
OutputStream out;
if (encoding != null && encoding.contains("gzip")) {
resp.setHeader("Content-Encoding", "gzip");
out = new GZIPOutputStream(resp.getOutputStream());
} else if (encoding != null && encoding.contains("deflate")) {
resp.setHeader("Content-Encoding", "deflate");
out = new DeflaterOutputStream(resp.getOutputStream());
} else {
out = resp.getOutputStream();
}
// 後續寫入 out...
}
HTML/JSON/CSS/JS 通常壓縮率 70-90%,大幅降低傳輸時間。現代容器 (Tomcat 9+, Jetty, Undertow) 可直接在 server.xml 設定 Compression="on" 免寫代碼。
minThreads),忙碌時擴充至上限 (maxThreads),閒置回收。負載曲線平滑,吞吐量穩定。<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="200" minSpareThreads="25" /> 並讓 Connector 參考 executor="tomcatThreadPool"。<%@ include file="header.jsp" %> → 編譯期合併為單一 Servlet,效能最佳,適合不變片段 (頁首/頁尾/選單)。<jsp:include page="dynamic.jsp" flush="true" /> → 執行期獨立編譯/執行,支援參數傳遞、條件包含,但有額外請求轉發開銷。原則:能用靜態包含就不用動態包含。
<jsp:useBean id="user" scope="request" class="com.example.User" />
| scope | 生命週期 | 適用場景 |
|---|---|---|
| page | 單頁請求處理期間 (預設) | 頁面專用暫存物件 |
| request | 單次 HTTP 請求 (含 forward) | Controller 產生、View 消費的資料 |
| session | 用戶會話期間 | 登入用戶、購物車 — 慎用 |
| application | Web 應用存活期間 | 共享快取、配置 — 需同步 |
錯誤範例:請求級資料設為 session,導致請求結束後物件殘留,增加 GC 壓力與記憶體佔用。
StringBuilder (非同步) 而非 +。log4j2/slf4j 非同步 Appender。ServletOutputStream,文字輸出用 PrintWriter (內建編碼轉換),依內容類型決定。效能調優無銀彈,但上述七大方向覆蓋 80% 以上常見瓶頸。原則:減少物件建立、縮短生命週期、降低 I/O、平滑併發。調優前必測量 (JMeter/LoadRunner + APM),調優後必驗證。