[Java] 스레드와 JVM

Nakjoo·2023년 1월 10일
0

[SEB_BE_43]

목록 보기
17/29
post-thumbnail

스레드

프로세스

  • 실행 중인 애플리케이션을 의미한다.
  • 데이터, 컴퓨터 자원, 스레드로 구성된다.

스레드

  • 데이터와 애플리케이션이 확보한 자원을 활용해 소스 코드를 실행한다.

메인 스레드(Main thread)

  • 자바에서 제일 먼저 실행되는 main 메서드를 메인 스레드가 실행시켜준다.
  • main 메서드의 코드를 처음부터 끝까지 순차적으로 실행시키며, 코드가 끝나거나 return문을 만나면 실행을 종료한다.

멀티 스레드(Muti-Thread)

  • 여러 개의 스레드를 이용하는 애플리케이션
  • 각각 스레드는 하나의 독립적인 프로세스처럼 작업 수행

작업 스레드 생성과 실행

메인 스레드 외에 스레드를 작업 스레드라고 한다. 이 작업 스레드를 생성하기 위해서는 두 가지 방법이 있다.

  • Runnable 인터페이스를 구현하여 생성
  • Thread 클래스를 상속받아서 생성

여기서 중요한 점은 둘 다 run() 메서드를 구현해야 한다는 것이다.

  • Runnable 인터페이스를 구현
class Test01 implements Runnable {
	public void run() {
    	...
    }
}
  • Thread 클래스 상속
class Test02 extends Thread {
	public void run() {
    	...
    }
}

스레드 5개를 생성하는 멀티 스레드 예제

Thread를 상속받아 1개 생성

Runnable 구현하여 1개 생성

외부 클래스로 스레드 2개, 내부 클래스로 스레드 2개, 익명 구현 객체로 1개 생성 -> 총 5개의 스레드를 실행하는 예제

실행 결과는 매번 다르기 때문에 단정 지을 수 없다. 대부분 1 2 3 4 5 가 나오겠지만 그렇지 않을 수도 있다. 이는 스레드를 실행하는 데 우선순위가 정해져 있지 않기 때문이다.

스레드의 이름

  • 메인스레드는 main이라는 이름을 가진다.
  • 그외 추가적으로 생성한 스레드는 기본적으로 Thread-n이라는 이름을 가진다.
  • getName() 메서드와 setName() 메서드로 스레드의 이름을 조회하고 설정할 수 있다.

스레드의 인스턴스 주소값 얻기

  • Thread 클래스의 정적 메서드인 currentThread()를 사용하면 스레드의 주소값을 얻을 수 있다.

임계 영역(Critical section)

  • 오로지 하나의 스레드만 코드를 실행할 수 있는 코드 영역을 의미한다.

락(Lock)

  • 임계 영역을 포함하고 있는 객체에 접근할 수 있는 권한을 의미한다.

즉, 임계 영역으로 설정된 객체가 다른 스레드에 의해 작업이 이루어지고 있지 않을 때, 임의의 스레드 A는 해당 객체에 대한 락을 획득하여 임계 영역 내의 코드를 실행할 수 있다.

스레드 A가 임계 영역 내의 코드를 실행 중일 때, 다른 스레드들은 락이 없으므로 임계 영역 내의 코드를 실행할 수 없게 된다.

스레드 A의 코드 실행이 끝나면 락을 반납하고 이때부터 다른 스레드들 중 하나가 락을 얻어서 임계 영역 내의 코드를 실행할 수 있게 된다.

특정 코드 구간을 임계 영역으로 설정하기 위해서는 synchronized 키워드를 사용한다.

synchronized 키워드를 사용하는 방법은 두 가지가 있다.

  • 메서드 전체를 임계 영역으로 지정
public synchronized void test() {
	...
}
  • 특정한 영역을 임계 영역으로 지정
    synchronized 옆 소괄호 안에는 해당 영역이 포함된 객체의 참조를 넣으면 된다.
public void test() {
	synchronized (...) {
    	...
    }
}

JVM

JVM이란?

  • 자바로 작성한 소스 코드를 해석해 실행하는 별도의 프로그램이다.

JVM의 구조

JVM의 동작 방식은

  1. 자바로 소스 코드를 작성하고 실행하면, 컴파일러가 실행되면서 .java를 바이트 코드 파일인 .class로 변환
  2. JVM이 운영 체제로부터 소스 코드 실행에 필요한 메모리 할당 = 런타임 데이터 영역(Runtime Data Area)
  3. 클래스 로더(Class Loader)가 바이트 코드 파일을 JVM 내부로 불러들여 런타임 데이터 영역에 적재
  4. 로드가 완료되면 실행 엔진이 런타임 데이터 영역에 적재된 바이트 코드 실행

이 때 실행 엔진은 두 가지 방식으로 바이트 코드를 실행시킨다.

  1. 인터프리터(Interpreter)를 통해 코드를 한 줄씩 기계어로 번역하고 실행
  2. JIT Compiler(Just-In-Time Compiler)를 통해 바이트 코드 전체를 기계어로 번역하고 실행

Stack 영역

  • JVM에서 Stack은 메서드가 호출되면 메서드를 위한 공간인 Method Frame을 만들 때 사용된다.
  • 메서드 내부에서 사용하는 참조변수, 매개변수, 지역변수, 리턴값 및 연산 시 일어나는 값들이 임시로 저장된다.

Heap 영역

  • JVM에는 단 하나의 Heap 영역이 존재한다.
  • JVM이 작동되면 자동 생성된다.

인스턴스를 생성하면 Heap 영역에 인스턴스가 생성되며, 인스턴스가 생성된 위치의 주소값을 참조 변수에 할당해주는데, 이 참조 변수는 Stack 영역에 선언된 변수이다.

Garbage Collection

  • 프로그램에서 더 이상 사용하지 않는 객체를 찾아 삭제하거나 제거하여 메모리를 확보하는 것을 의미한다.

가비지 컬렉션의 동작 방식을 이해하려면 Heap 영역에 대한 이해가 필요하다.

JVM의 Heap 영역의 객체는 대부분 일회성이며, 메모리에 남아 있는 기간이 대부분 짧다는 전제로 설계되어 있다.

그렇기 때문에 이 객체가 얼마나 살아있냐에 따라서 Heap 영역을 Young, Old 영역으로 나눈다.

Young 영역은 새롭게 생성된 객체가 할당되는 곳이고 이곳에서 많은 객체가 생성되었다 사라지는 것을 반복한다.

이 영역에서 활동하는 가비지 컬렉터를 Minor GC라고 한다.

Young 영역에서 살아남은 객체들이 Old 영역으로 복사된다. 보통 Young 영역보다 크게 할당되고 크기가 큰 만큼 가비지는 적게 발생한다.

이 영역에서 활동하는 가비지 컬렉터를 Major GC라고 한다.

가비지 컬렉션가 실행될 때는 두 가지 단계를 따른다.

  1. Stop The World
    Stop The World는 가비지 컬렉션을 실행시키기 위해 JVM이 애플리케이션의 실행을 멈추는 작업이다.

    가비지 컬렉션이 실행될 때 가비지 컬렉션을 실행하는 스레드를 제외한 모든 스레드들의 작업이 중단되고, 가비지 정리가 완료되면 재개된다.

  2. Mark and Sweep
    Mark는 사용되는 메모리와 사용하지 않는 메모리를 식별하는 작업을 의미한다. Sweep은 Mark 단계에서 사용되지 않음으로 식별된 메모리를 해제하는 작업을 의미한다.

즉, 1번을 통해 모든 작업이 중단되면, GC가 모든 변수와 객체를 탐색해 각각 어떤 객체를 참고하고 있는지 확인한다.

그리고, 사용되고 있는 메모리를 식별해 사용되지 않는 메모리는 제거하는 과정을 진행한다.

0개의 댓글