자바 성능 최적화: 코드를 느리게 만드는 8가지 안티패턴

안티 패턴 수정으로 인한 성능 개선
Jonathan Vogel이 구축한 Java 주문 처리 애플리케이션은 초기 경과 시간 1,198ms, 초당 85,000건 처리, 1GB 이상 힙 사용, 19회 GC 일시 정지 성능을 보였습니다. 아키텍처 변경이나 JDK 업데이트 없이 8가지 안티 패턴을 수정한 후, 성능이 경과 시간 239ms, 초당 419,000건 처리, 139MB 힙 사용, 4회 GC 일시 정지로 개선되었습니다. 이는 처리량 5배 증가, 힙 사용량 87% 감소, GC 일시 정지 79% 감소를 의미합니다.
수정해야 할 8가지 Java 성능 안티 패턴
- 루프 내 문자열 연결 - 불변성으로 인한 O(n²) 복사
- 루프 내 O(n²) 스트림 반복 - 요소마다 전체 목록 스트리밍
- 핫 경로에서의 String.format() 사용 - 가장 느린 문자열 빌더, 매 호출마다 형식 파싱
- 핫 경로에서의 오토박싱 - 수백만 개의 일회용 래퍼 객체 생성
- 제어 흐름을 위한 예외 사용 - fillInStackTrace()가 전체 호출 스택을 순회
- 너무 광범위한 동기화 - 하나의 락이 병목 현상이 됨
- 재사용 가능한 객체 재생성 - 호출마다 ObjectMapper, DateTimeFormatter, Gson 생성
- 가상 스레드 고정 (JDK 21-23) - synchronized + 블로킹 I/O가 캐리어를 고정
상세 예시 및 해결 방법
1. 루프 내 문자열 연결
문제 코드:
String report = "";
for (String line : logLines) {
report = report + line + "\n";
}문자열 불변성으로 인해 O(n²) 복사가 발생합니다. BellSoft JMH 벤치마크에 따르면 n이 4배 증가할 때 루프 연결 속도는 7배 이상 느려집니다.
해결 방법:
StringBuilder sb = new StringBuilder();
for (String line : logLines) {
sb.append(line).append("\n");
}
String report = sb.toString();참고: JDK 9부터 컴파일러는 "Order: " + id + " total: " + amount와 같은 단일 라인 연결을 최적화하지만, 이 최적화는 루프 내부로 이어지지 않습니다.
2. 루프 내 스트림으로 인한 의도치 않은 O(n²)
문제 코드:
for (Order order : orders) {
int hour = order.timestamp().atZone(ZoneId.systemDefault()).getHour();
long countForHour = orders.stream()
.filter(o -> o.timestamp().atZone(ZoneId.systemDefault()).getHour() == hour)
.count();
ordersByHour.put(hour, countForHour);
}이 패턴은 JFR 기록에서 CPU 스택 샘플의 거의 71%를 차지했습니다. 10,000건의 주문으로 단일 패스 대신 1억 건의 비교를 수행합니다.
해결 방법:
for (Order order : orders) {
int hour = order.timestamp().atZone(ZoneId.systemDefault()).getHour();
ordersByHour.merge(hour, 1L, Long::sum);
}이는 단일 패스로 O(n) 성능을 제공합니다. 단일 스트림 파이프라인에서 Collectors.groupingBy(... Collectors.counting())을 사용할 수도 있습니다.
이 글은 3부작 Java 성능 최적화 시리즈의 1부이며, 2부와 3부는 곧 출시될 예정입니다. 2부에서는 화염 그래프와 실제로 핫했던 메서드를 포함하여 이러한 수치 뒤의 프로파일링 데이터를 살펴볼 것입니다.
📖 전체 Source 읽기: HN AI Agents
👀 See Also

AI 모델 선택 그만 묻기: 작업을 Haiku, Sonnet, Opus 계층으로 라우팅하세요
작업 유형별로 최소 세 가지 모델을 사용하세요: 읽기/요약에는 Haiku 등급, 코드 작성에는 Sonnet 등급, 다중 파일 리팩터와 디버깅에만 Opus 등급을 사용하세요. 한 사용자의 설정은 40%는 저렴한 모델, 35%는 중간, 25%는 최고 성능 모델에 할당하여 월 약 $30-40의 비용이 듭니다.

GitHub 저장소 소유자: AI 봇 스팸을 차단하기 위해 Git의 --author 플래그 사용
Archestra는 GitHub의 'prior contributors' 설정과 Git의 --author 플래그를 활용하여 캡차 기반 온보딩 절차를 통해 실제 인간을 화이트리스트에 등록함으로써 AI 댓글/PR 스팸과 싸웠습니다.

클로드를 최대한 활용하기: Cowork와 Claude Code를 사용한 데이터 분석가의 워크플로
코딩 배경지식이 없는 데이터 분석가가 Google Places API를 사용한 리드 생성 도구, 사기 대시보드, 자동화된 소셜 미디어 게시 도구를 구축하면서 Cowork로 엔드투엔드 자동화를 하고 Claude Code로 무거운 작업을 처리하는 방법을 공유합니다.

코딩 에이전트의 구성 요소: 도구, 메모리, 컨텍스트가 LLM을 확장하는 방법
세바스찬 라슈카는 Claude Code와 Codex CLI와 같은 코딩 에이전트의 여섯 가지 구성 요소를 분석하며, 에이전트 하네스가 모델을 도구, 메모리, 저장소 컨텍스트와 결합하여 LLM을 소프트웨어 작업에 더 효과적으로 만드는 방법을 설명합니다.