개발을 하면서 대용량 데이터를 인메모리에 저장해서 DB 의 부하를 최소화 하고 속도를 개선하는 작업을 한적이 있다
인메모리에 저장하는 데이터는 대용량 데이터로 주기적으로 스케줄링을 통해서 업데이트 되었다.
여기서 레디스를 사용하여 2가지의 개선점을 두었는데
1. 블루그린 배포시에 인메모리에 데이터를 채우는데 시간이 소모되므로 레디스 캐시를 이용하여 데이터를 불러온다.
2. 스케줄링을 돌릴때 모든 aws 인스턴스에서 ( java project 가 인스턴스당 1개 ) 모두 돌아가면 n 만큼 db에 부하가 걸린다. 따라서 레디스에 시간차 키를 두어서 선별적인 스케줄링을 실행하게 한다.
여기서 문제가 발생하였다.
인메모리에 데이터를 최신화 하는 과정에서 레디스를 사용했는데 레디스 스트림 객체가 heap 메모리를 많이 차지하는 일이 생겼다. ( oom )
뉴렐릭 로그를 살펴보니 heap 메모리가 지속적으로 증가했고 거의 90% 가 되었을 시점에 garbage collector cpu 가 거의 풀차지 되는걸 목격했다.
그렇다면 garbage collector 는 언제 작동하는 것인가 ?
가비지 컬렉터는 object 에 finalize() 메소드가 실행시킨다.
가비지 컬렉터는 메모리를 전부 소멸시키는 것이 아니라 메모리의 상태를 보고 일부만 소멸시킨다.
즉, 가비지 컬렉터는 메모리가 부족할 때 그리고 cpu 가 한가할 때에 JVM 에 의해서 자동 실행된다. 그렇기 때문에 finalize() 메소드가 호출되는 시점은 명확하지 않다.
따라서 프로그램이 종료될 때 즉시 자원을 해제해야 하는 경우에는 일반 메소드에서 작성하고 프로그램이 종료될 때 명
시적으로 메소드를 호출하는 것이 좋다????
https://d2.naver.com/helloworld/1329
Java는 프로그램 코드에서 메모리를 명시적으로 지정하여 해제하지 않는다. 가끔 명시적으로 해제하려고 해당 객체를 null로 지정하거나 System.gc() 메서드를 호출하는 개발자가 있다. null로 지정하는 것은 큰 문제가 안 되지만, System.gc() 메서드를 호출하는 것은 시스템의 성능에 매우 큰 영향을 끼치므로 System.gc() 메서드는 절대로 사용하면 안 된다(다행히도 NHN에서 System.gc() 메서드를 호출하는 개발자를 보진 못했다).
큰일날뻔;;
추가
가비지 컬렉터를 처음부터 줄이도록 하는 것 보다. 애플리케이션에서 최대한 공간 복잡도를 고려한 코딩을 해야한다. 그래도 안되면 그때 가비지 컬렉션 튜닝을 들어가야 한다.
https://d2.naver.com/helloworld/37111
"티끌 모아 태산"이라는 말이 있듯이, String대신 StringBuilder나 StringBuffer를 사용하는 것을 생활화하는 것부터가 시작이라고 보면 된다. 그리고, 로그를 최대한 적게 쌓도록 하는 것이 좋다.
우리가 흔히 알고있는 String str = "ABC" -> str+"DEF" 를 하게 되면 새로운 객체를 생성하고 결과값을 거기에 대입하게 된다.
하지만 어쩔 수 없는 현실도 있다. 경험상 XML과 JSON 파싱은 메모리를 가장 많이 사용한다. 아무리 String을 최대한 사용 안 하고 Log 처리를 잘 하더라도, 10~100 MB짜리 XML이나 JSON를 파싱하면 엄청난 임시 메모리를 사용한다. 그렇다고 XML과 JSON을 사용하지 않기는 어렵다. 그냥 현실이 그렇다는 것만 알아주기 바란다.
'개발 > java' 카테고리의 다른 글
JPA 기초 프록시란 무엇인가 (0) | 2020.04.12 |
---|---|
Java Collection 프레임워크 (0) | 2020.03.22 |
spring 에서 pageable custom 구현 (0) | 2019.12.19 |
java 복잡한 Comparator 예제 (0) | 2019.12.19 |
템플릿 메소드 패턴 (2) | 2019.07.04 |
댓글