메모리 모델과 관리
Last updated
Last updated
자바 멀티 스레드 환경에서 공유되는 메모리 할당과 동작 방식을 정의하는 모델
데이터에 대한 가시성, 접근성 등에 대한 명세를 포함한다.
최신 플랫폼에서는 최적화 등을 이유로 명령이 순서대로 실행되는 것을 보장하지 않는다.
멀티 프로세서는 메인 메모리와 별개인 캐시 메모리를 소유할 수 있다.
여러 스레드가 완벽히 동기화된 상태를 유지하는 것은 비용이 많이 든다.
공유 멀티프로세서 아키텍처
CPU 코어마다 L1, L2 캐시가 존재한다.
CPU 캐시에 존재하는 내용과 RAM에 존재하는 내용이 동기화되지 않는 상태가 존재할 수 있다.
서로 다른 스레드가 하나의 데이터에 접근할 때 일관적인 상태를 보장하는 속성
여러 스레드가 동일 데이터를 읽을 때 일관적이지 않은 값을 읽는 경우 에러 발생
멀티스레드 환경에서 한 스레드에서 변경한 값을 다른 스레드에서 언제 보게 될지를 정의한 것
어떤 스레드에 의해 값이 변경되었을 때 다른 스레드가 가장 최신 값을 읽을 수 있게 하는 속성
final 필드는 별도의 동기화 처리가 필요 없기 때문에 세이프한 불변 객체 구현을 지원하며 JIT 컴파일러는 레지스터에 최종 캐시 값을 유지할 수 있다.
스레드 간 레이스 컨디션일 때에도 불변이다.
모든 스레드에서 CPU 레지스터가 아닌 메인 메모리로부터 값을 읽고 쓰도록 강제하는 키워드이다.
멀티스레드에서 원자성을 보장해주지 않으므로 synchronized, concurrent 패키지의 AtomicXX 등을 사용해야 한다.
현대 멀티프로세서 아키텍처는 쓰기 작업을 바로 갱신하지 않고 레지스터에 담아두었다가 한번에 처리한다.
필요하지 않은 메모리의 참조를 해제하지 못해 발생하는 누수를 의미한다.
메모리에 할당되어 있지만 실행 코드에서 접근할 수 없는 경우에도 발생한다.
시스템 성능을 저하시키고 치명적 에러가 발생할 확률이 높다.
GC를 사용해 메모리가 관리는 되지만 완벽하지 않다.
참조, 비참조 유형의 객체가 존재한다.
참조중인 객체이지만 사용되지 않는다면 메모리 누수가 발생한 것이다.
static field 객체가 사용되지 않는 경우 누수 발생 가능
사용한 리소스를 해제하지 않아 발생할 수 있으므로, try-with-resourcse 혹은 finally 블록을 통해 리소스 해제하도록 한다.
equals, hashcode 메서드를 재정의하지 않으면 불필요한 참조가 발생할 수 있다.
아우터 클래스를 참조하는 이너 클래스가 있을 때 이너 클래스를 사용하려면 항상 아우터클래스를 참조해야 한다. 이때 아우터 클래스를 사용하지 않는다면 누수 발생 가능
아우터 클래스를 참조하지 않는 경우 이너클래스를 static클래스로 구현하면 바깥 클래스에 대한 참조를 가지지 않아 메모리 누수가 발생하지 않는다.
ThreadLocal 클래스를 사용해 데이터를 스레드별로 사용할 때 명시적으로 값 제거해주지 않으면 참조가 유지되어 누수 발생 가능
ThreadLocal.remove() 메서드 호출해 명시적으로 참조 제거 필요
VisualVM, YourKit 등과 같은 프로파일러를 활용한 모니터링 또는 `-verbose:gc` 옵션 사용으로 GC 모니터링을 통해 예방할 수 있다.
참조 객체(java.lang.ref 패키지)를 통해 메모리 누수를 방지할 수 있다.
벤치마크를 통해 성능 모니터링을 미리 진행해 예방할 수 있다.
코드리뷰도 중요하다.
컬렉션 사용 시 초기 사이즈 지정해 초기화 하거나, CRUD에 대한 시간 복잡도를 확인해 적절한 컬렉션을 사용
HashMap 사용시 리사이징이 일어나면 해시값을 다시 조정하기 때문에 이러한 일이 발생하지 않도록 주의
불변 객체나 캐싱 형태의 구현을 활용해 볼 것
박싱/언박싱을 가급적 피할 것
Stream API 사용시 메모리를 더 사용하거나 성능이 더 느릴 수 있어 주의할 것
스레드 상태 정보를 확인할 수 있다.
자바 프로세스의 모든 스레드에 대한 스냅샷을 떠 문제 진단 시 유용하게 사용될 수 있다.
일반 텍스트로 작성되어 내용을 파일에 저장한다.
jstack, JMC, jvisualvm, jcmd, jconsole 등이 있다.
특정 순간에 JVM 메모리 상에 있는 모든 객체에 대한 스냅샷을 떠 확인할 수 있다.
앱 실행 시 힙 메모리 사용량 분석 가능
메모리 누수, GC 이슈 등을 해결하는 데 활용
jmap, VisualVM을 사용하거나 java -XX:+HeapDumpOnOutOfMemoryError <ClassName>
설정으로 OutOfMemoryError 발생 시 자동으로 힙 덤프를 수행할 수 있다.
APM
앱의 성능, 가용성 등을 모니터링 또는 관리하는 것
프로파일링보다 한단계 높은 어플리케이션 수준에서 성능을 모니터링, 분석
프로파일링
메모리 사용량, 시간복잡도, 특정 명령의 호출 빈도 등을 측정해 분석하는 것
런타임 동안에 어떤 일이 발생하는 지에 대한 정보를 제공
APM과 달리 개발자의 코드 레벨에서 성능을 최적화하는 데에 유용
Newrelic, VisualVM 등의 툴이 있다