목적
회사에서 사용하는 메인 언어인 자바의 최신 버전에서 어떤 기능이 추가되었는지 알아보고 팀원들과 공유한다.
ZGC, Virtual
자바 21 버전
https://openjdk.org/projects/jdk/21/
이번 23년 9월 19일에 출시되었으며, LTS 버전으로 최소 8년간 지원이 이루어지게 되고 430 ~ 453까지 많은 기능이 추가된 것을 볼 수 있다.
내가 이번에 주로 관심있게 본 변경 사항은 2가지 이다.
1. Generational ZGC
2. Virtual Thread
위 2가지가 내 흥미를 자극했다. 이번 포스팅에서는 Virtual Thread에 대해서 설명하고, 다음 편에 Generational ZGC를 알아보자.
Virtual Thread ( 가상 스레드 )
golang의 goroutine만큼의 경량 스레드는 아니지만, 전통적인 Java 스레드와 달리 새롭게 추가된 경량 스레드이다.
기존에는 os 스레드를 그대로 사용했지만, 이번에 새롭게 개발된 스레드일 경우 JVM에서 OS의 스레드에 자체적으로 내부 스케줄링을 통하여 사용할 수 있다.
그럼 가상 스레드는 왜 나오게 되었을까?
전통적인 JVM의 스레드( 플랫폼 스레드 )는 실제 OS 스레드에 1:1 매핑되며 이용되는 방식으로 동작을 했었다.
- 여기서 말하는 플랫폼 스레드는 어플리케이션 레벨의 스레드와는 다른 스레드이다.
플랫폼 스레드는 Thread per request이며, 하나의 요청을 처리하기 위해서는 하나의 스레드를 사용한다는 의미이다. 애플리케이션에서 처리량을 늘리고 싶으면 스레드를 늘려하는게 이치에 맞지만, 이는 물리적 한계로 불가능하다. 따라서 어플리케이션에서의 처리량은 플랫폼 스레드에서 감당할 수 있는 처리량을 넘어설 수 없다.
또한 플랫폼 스레드일 경우 요청을 처리할 때 해당 스레드는 blocking되므로 작업을 마칠때 까지 다른 스레드에서 해당 플랫폼 스레드에 접근이 불가능하다.
위와 같은 이유로 나온게 Reactive 프로그래밍, 즉 비동기 프로그래밍이다. 스플링에서 Webflux가 여기에 속하며 구현하는데 상당한 기술력이 필요하다.
그래서 가상 스레드는 뭐가 좋길래 위와 같은 문제점을 해결할 수 있다는 것일까?
자바는 스레드 중심의 프로그래밍이 이루어 진다. 즉, JVM의 스레드가 자체적으로 OS의 스레드에 스케줄링을 해준다면, webflux와 같은 복잡도가 높은 프로그래밍을 하지 않아도 된다.
구조
아래 사진은 가상 스레드와 플랫폼 스레드의 구조이다. 한번 보고 넘어가자.
가상 스레드와 플랫폼 스레드가 어떤 차이점인지 딱 봐도 알 수 있다. 바로, 스레드 스케줄링을 지원한다는 것이다.
하지만 어떻게 보면 스레드를 사용하기 위한 레이어가 하나 더 생겨서 느리지 않을까?? 라는 생각이 나는게 당연할 것이다.
복잡한 작업, 수행 시간이 짧은 작업을 수행하는 경우 blocking이 일어나는 빈도가 높은 경우가 많다. 플랫폼 스레드일 경우에는 작업이 끝날 때 까지 기다리고, 다음 작업을 이어서 하지만 가상 스레드일 경우에는 스레드 스케줄링을 통해 다른 가상 스레드의 작업을 할당한다. 이렇게 되므로 Reactive 프로그래밍에서 누리는 장점들을 그대로 사용할 수 있다.
구조2
가상 스레드의 구조를 좀 더 자세히 들여다 보자.
기존에 플랫폼 스레드가 있던 자리에 케리어 스레드가 생기고 스케줄러가 생긴 것을 볼 수 있다.
JDK 스케줄러는 작업 할당을 추상화하여 사용자가 따로 비동기 코드를 작성하지 않더라도 작업을 스케줄링하여 효율적으로 OS의 스레드를 사용할 수 있도록 도와준다. 추상화로 인해 사용자는 100%는 아니더라도 코드 변경 없이 가상 스레드로의 플랫폼 이전이 가능하다.
여기서 케리어 스레드는 작업 응답을 기다리는 블록킹 타임에 케리어 스레드를 양보하여 비동기적으로 작업이 실행이 되게 된다. 이로 인해 어플리케이션에서 처리량이 증가하게 된다.
JDK 스케줄러의 스케줄링은 FIFO로 이루어지게 되며, 먼저 들어온 작업을 케리어 스레드에게 할당한다.
[ 자바의 가상 스레드(Virtual Thread) 요약 ]
- 기존 자바의 스레드는 OS 스레드와 일대일 대응되도록 구현됨
- 이로 인해 스레드의 개수가 제한되며, I/O 작업 시에 블로킹되어 하드웨어를 최적으로 활용하지 못함
- 다른 언어들은 제약으로 인해 비동기 API, 코루틴 등으로 문제를 해결했지만 자바는 제약이 없음
- JVM 수준에서 이를 처리하고 기존 코드와의 호환성을 유지하는 방식으로 문제를 해결함
- 가상 스레드는 비용이 저렴한 스레드이므로 풀링하지 말아야 함
- 또한 수백만 개의 가상 스레드가 리소스를 공유할 수 있으므로 ThreadLocal은 주의해서 사용해야 함
그렇다면 모든 경우에서 가상 스레드를 사용하는 경우에 성능이 향상될까?
--- 작성중
참고 자료
https://theboreddev.com/understanding-java-virtual-threads/%EF%BB%BF
https://findstar.pe.kr/2023/04/17/java-virtual-threads-1/
https://mangkyu.tistory.com/309