웹 서버 로 하여 금 outofmemory 를 피하 게 하 다

Jetty + Jersey 를 RESTful server 로 사용 하기 때문에 사용자 가 REST API 를 통 해 백 엔 드 서 비 스 를 방문 할 때 한 사용자 가 한 번 에 제출 하거나 얻 은 데이터 의 최대 치 는 50m 이 며, jvm 의 heap size 최대 치 를 1G 로 가정 하면 동시 다발 사용자 수가 너무 많 으 면 백 엔 드 에 outofmemory error 가 나타 날 수 있 습 니 다.이런 상황 이 발생 하지 않도록 초보적인 방안 을 생각해 냈 다.
 
1. 하나의 filter 를 설정 하고 모든 사용자 의 요청 은 filter 의 filter 방법 으로 처 리 됩 니 다.
2 매번 filter 방법 이 호출 될 때마다 jvm 의 힙 메모리 사용 상황 을 검사 합 니 다. 힙 메모리 사용률 이 80% 이상 이 되면 System. gc () 를 호출 하고 접근 을 거부 하 며 503 오 류 를 되 돌려 클 라 이언 트 에 알 립 니 다.
 
코드 는 다음 과 같 습 니 다:
private void checkServerMemoryUsage() {
		MemoryMXBean aMemoryMXBean=ManagementFactory.getMemoryMXBean();
		MemoryUsage heapMemoryUsage=aMemoryMXBean.getHeapMemoryUsage();
		long usedHeapMemory = heapMemoryUsage.getUsed();
		long maxHeapMemory = heapMemoryUsage.getMax();
		double MemUsedRate = (usedHeapMemory*1.0)/maxHeapMemory;

		if(MemUsedRate >= 0.8) {
			System.gc();
			logger.error("Heap memory will be expired. Used Memory is: "+maxHeapMemory+" bytes. The max heap memory is: "+maxHeapMemory+" bytes.");
			throw new RestException(503, "Server is too busy!");
		}
	}

 
원래 이렇게 하 는 것 이 문제 가 없다 고 생각 했 지만 나중에 디 버 깅 과정 에서 이런 현상 을 발견 했다.
10 개의 클 라 이언 트 를 시 뮬 레이 션 하고 server 에 요청 합 니 다. 모든 클 라 이언 트 가 제출 한 데 이 터 는 5M 입 니 다. 모든 클 라 이언 트 는 5000 번 연속 으로 보 냅 니 다. 실행 과정 에서 - verbosegc 옵션 을 열 었 습 니 다. 시작 할 때 상황 이 정상 적 이 고 gc 동작 이 자주 진행 되 었 습 니 다. (이것 은 신세대 Eden 구역 이 자주 꽉 차 서 minor GC 를 촉발 한 것 입 니 다)백 스테이지 에서 사용자 가 제출 한 데 이 터 를 책임 지고 처리 하기 때문에 새로운 대상 이 많이 생 길 수 있 기 때문에 이미 사용 한 구역 이 점점 커지 지만 사용률 이 80% 에 이 르 렀 을 때 System. gc () 를 명시 적 으로 호출 했 습 니 다. 이 럴 때 시스템 은 full gc 를 실행 하고 클 라 이언 트 도 503 오 류 를 받 을 수 있 습 니 다.모든 것 이 비교적 완벽 해 보인다.
 
내 가 모든 클 라 이언 트 스 레 드 를 kill 로 떨 어 뜨 렸 을 때 문제 가 발생 했 습 니 다. 힙 의 점용 율 은 아직도 천천히 증가 하고 있 습 니 다 (앞에서 제출 한 데이터 가 아직 처리 되 고 있 을 수 있 기 때 문 입 니 다). 그러나 minor gc 는 촉발 되 지 않 았 습 니 다 (Eden 구역 이 채 워 지지 않 으 면 촉발 되 지 않 습 니 다). 이 를 통 해 대량의 대상 이 늙 은 세대 (Old generation) 에 있 고 늙 은 세대 가 가득 쓰 이지 않 으 며 full gc 를 촉발 하지 않 습 니 다.(기본 설정 은 1 시간 에 한 번 씩 full gc 를 실행 합 니 다) 이 때 힙 의 사용률 은 장기 적 으로 높 은 위치 로 유 지 됩 니 다.
 
그래서 개선 방안 이 나 타 났 습 니 다.
쓰다
static class MemoryInfo{
long usedHeapMemory;
long maxHeapMemory;
double getUsageRate(){
return (usedHeapMemory*1.0)/maxHeapMemory;
}
}
private void checkServerMemoryUsage() {
MemoryInfo info = getHeapUssageRate();
if(info.getUsageRate() >= 0.8) {
System.gc();
info = getHeapUssageRate();
if(info.getUsageRate() >= 0.8) {
logger.error("Heap memory will be expired. Used Memory is: "+info.usedHeapMemory+" bytes. The max heap memory is: "+info.maxHeapMemory+" bytes.");
throw new RestException(503, "Server is too busy!");
}
}
}
private MemoryInfo getHeapUssageRate() {
MemoryInfo info = new MemoryInfo();
MemoryMXBean aMemoryMXBean=ManagementFactory.getMemoryMXBean();
MemoryUsage heapMemoryUsage=aMemoryMXBean.getHeapMemoryUsage();
info.usedHeapMemory = heapMemoryUsage.getUsed();
info.maxHeapMemory = heapMemoryUsage.getMax();
return info;
}
 
요청 이 올 때마다 hep 사용 상황 을 확인 하고 사용률 이 80% 에 이 르 면 즉시 Sytem. gc () 를 호출 하여 full gc 를 실행 합 니 다. 그리고 사용률 이 80% 를 초과 하 는 지 다시 판단 합 니 다. 초과 하면 서 비 스 를 거부 하고 503 오 류 를 되 돌려 줍 니 다.
 
추가 개선:
 
full gc 는 시간 이 오래 걸 리 기 때문에 서버 의 성능 에 영향 을 줄 수 있 습 니 다.
 

좋은 웹페이지 즐겨찾기