멀티쓰레드에서 서블릿 인스턴스를 재사용할 때 발생할 수 있는 문제점?
JVM 메모리는 어떻게 관리되며, 인스턴스를 재사용할 때 어떤 문제가 발생할 수 있을까?
자바 프로그래밍에서 클래스의 인스턴스를 생성할 때 비용이 발생한다. 인스턴스를 생성하고 더 이상 사용하지 않을 경우 가비지 콜렉션 과정을 통해 메모리에서 해제하는 과정 또한 비용이 발생한다. 따라서 인스턴스를 매번 생성할 필요가 없는 경우 매번 인스턴스를 생성하지 않는 것이 성능 측면에서 더 유리하다.
이 단계에서 개발자가 갖추어야 할 역량 중의 하나가 클라이언트 요청마다 매번 인스턴스를 생성해야 하는지, 생성하지 않고 이미 생성된 인스턴스를 재사용할 것인지를 판단해야 한다. 이에 대한 기준은 인스턴스가 상태 값을 유지해야 하는지
에 따라 구분된다. 매 클라이언트마다 서로 다른 상태 값을 유지할 피요가 있는 경우에는 매 요청마다 인스턴스를 생성해야 한다. 하지만 Controller등은 매 요청마다 서로 다른 상태 값을 가지지 않기 때문에 매번 인스턴스를 생성하지 않고 인스턴스 하나를 생성한 후 재사용할 수 있다.
서블릿은 서블릿 컨테이너가 시작할 때 인스턴스 하나를 생성한 후 재사용한다. 이 같은 환경에서 소스코드 구현을 잘못하면 심각한 버그를 만들어 낼 수 있다. 이 버그는 매번 발생하는 버그가 아니라 여러명의 클라이언트가 동시에 같은 코드를 실행 하는 경우 발생할 수 있기 때문에 간헐적으로 발생한다. 따라서 버그의 원인을 찾기 힘들다. 하지만 버그에 따른 결과는 치명적일 수 있기 때문에 반드시 주의해야 한다.
JVM은 코드를 실행하기 위해 메모리를 스택과 힙 영역으로 나눠서 관리한다. 스택 영역은 각 메소드가 실행될 때 메소드의 인자. 로컬 변수 등을 관리하는 메모리 영역
으로 각 스레드마다 서로 다른 스택 영역
을 가진다. 힙 영역은 클래스의 인스턴스 상태 데이터를 관리하는 영역
이다. 힙 영역은 스레드가 서로 공유할 수 있는 영역
이다.
메모리의 구조는 대략 다음과 같다
JVM은 각 메소드 별로 스택 프레임을 생성한다. ShowController의 execute() 메소드를 실행하면 execute() 메소드에 대한 스택 프레임의 로컬 변수 영역의 첫 번째 위치에 자기 자신에 대한 메모리 위치를 가리킨다. ShowController에 대한 인스턴스는 힙에 생성되어 있으며, ShowController는 필드에 Question과 List
첫 번째 쓰레드가 접근했을 때는 별다른 특이사항이 없다. 하지만 첫번째 스레드가 완료되지 않은 상태에서 두 번째 쓰레드 요청에 의해 execute() 메소드가 실행될 경우
발생한다. 이 때의 메모리 상태는 아래와 같다.
원래 JVM Stack은 각 쓰레드에 맞게 두개를 그려야 하지만 공간 상 하나로 대체 하였습니다.
두 번째 쓰레드가 실행 되면서 ShowController가 가리키는 Question과 List
이를 해결하기 위해서 Question과 List
위와 같이 구현하면 ShowController가 Question과 List
참고 : 포비의 자바 웹 프로그래밍 Next Step