PS, 언어 공부/JAVA

[Java] 자바 가상스레드의 정의와 원리 파헤치기

Emil :) 2024. 6. 21. 23:57
728x90
반응형

 

서론


모 회사에서 면접 도중, 가상 스레드에 대한 질문이 들어왔었다. 프로젝트엔 아직 사용해보지 않았고, JDK 21이 현업에서 잘 사용되지 않는 점(정확히는 나온지 얼마 안되서 사용되지 않는다. 마이그레이션이 필요한 곳은 하는듯) 등을 이유로 따로 공부를 하진 않았기에, 간략하게나마 알고있는 내용으로 답변했었다.

JDK 17이 나왔을 때와 달리 지금은 필드에서도 17을 쓰는 회사가 꽤 많이 보인다. 이처럼 상위 버전이 출시되고 시간이 지나면 자연스럽게 현업에 녹아들게 되므로, 빠르게 체득해야 시장에서 경쟁력 있는 개발자로 성장할 수 있을 것 같다.

본 포스팅에선 이러한 배경을 토대로 자바 가상스레드와, 공부를 진행하며 내가 궁금했던 점들을 꼬리질문 형식으로 되물어보도록 하겠다.

참고


https://techblog.woowahan.com/15398/

 

Java의 미래, Virtual Thread | 우아한형제들 기술블로그

JDK21에 공식 feature로 추가된 Virtual Thread에 대해 알아보고, Thread, Reactive Programming, Kotlin coroutines와 비교해봅니다.

techblog.woowahan.com

 https://mangkyu.tistory.com/309

 

[Java] 기존 자바 스레드 모델의 한계와 자바 21의 가상 스레드(Virtual Thread)의 도입

1. 가상 스레드의 도입 배경 [ 기존 자바 스레드 모델의 문제와 한계 ] 자바 개발자들은 약 30년 동안 서버 애플리케이션의 동시성 처리를 위해 스레드를 사용해왔다. 대표적으로 스프링 프레임워

mangkyu.tistory.com

 https://d2.naver.com/helloworld/1203723 

본론


가상스레드의 정의와 등장 배경

JDK 21의 대표적인 신기능 중 하나이다. 우리가 컴퓨터를 만지면서 ssd나 hdd의 메모리를 ram으로 전환시키는 방법을 '가상 메모리' 라고 하는데, 이와 비슷한 맥락으로 스레드를 가상으로 만드는 것이다.

기존에 자바가 스레드를 사용하는 방법은 OS의 실제 메모리 스레드를 그대로 사용했다.

Java에서 유저 스레드를 만들면, JNI의 커널 영역 호출을 통해 OS가 커널의 스레드를 매핑하고 작업을 수행하는 형태였다.
그러나 이러한 형태가 성능에 영향을 끼쳐, JVM이 관리하는 경량 스레드를 따로 만든 것이 자바의 가상스레드이다.

그렇다면 왜 OS의 스레드 1:1 대응 형태가 성능이 좋지 못한 걸까?

이유는 여러가지가 있는데, 하나씩 따져보도록 하자.


1. OS의 스레드는 생성 & 소멸 비용 자체가 크다.

말 그대로 OS의 스레드는 생성과 소멸에 드는 비용이 크다. 이유가 뭘까?

첫째, 새로운 스레드를 할당할 때, OS는 각 스레드에 대해 일정 크기의 스택을 할당해준다. 이 스택 메모리를 초기화하고 확보하는 과정은 비용이 많이 든다.

둘째, OS에서 이 스레드들을 관리하기 위해 커널 구조체 라는걸 사용하는데, 이것도 새로 생성된다면 초기화하고 필요한 자원을 할당해줘야 한다.

셋째, 스레드의 생성과 소멸은 사용자의 시스템 콜에 의해 결정되는데, 여기서 또 사용자 모드 <-> 커널 모드 간 전환 비용이 수반된다. 여기서 또 비용이 상당히 소모된다.

그 외에도 TLS, 스케줄러 설정 등등.. 안그래도 할 거 많은 OS 친구가 진땀을 빼게된다.

2. 컨텍스트 스위칭(Context Switching) 비용이 크다.

1번과 마찬가지로 비용과 직결되는 이슈이다. 작업을 이어받는 행위를 컨텍스트 스위칭(Context Switching)이라고 하는데, A 스레드가 하던 작업을 B 스레드가 이어받으려면 커널 스레드를 점유해 작업을 이어서 진행한다. 그러나 이 또한 비용이 상당하다.

스레드의 컨텍스트 스위칭


가상 스레드의 구조와 동작 원리

이처럼  기존 자바가 가지고 있던 스레드 시스템은 비용과 밀접한 문제를 야기했다. 이를 개선하기 위해 자바 진영은 OS단이 아닌 JVM 단에서 관리하는 경량 스레드, 즉 가상 스레드를 만들게 된다.

가상 스레드는 플랫폼 스레드(Platfrom Thread)가상 스레드(Virtual Thread)로 나뉘는데, 플랫폼 스레드 내부에서 가상스레드가 번갈아가며 실행되는 방식이다.

그림을 자세히 살펴보면, ForkJoin Pool 에 의해 OS의 스레드가 JVM의 플랫폼 스레드와 대응하는 것을 알 수 있는데, 이는 기존 자바 스레드 방식과 ForkJoin Pool을 제외하면 동일하다는 것을 알 수 있다. 즉, 플랫폼 스레드는 기존 자바 스레드 방식과 동일하다.

여기서 스케줄러는 플랫폼 스레드를 관리하고, 가상 스레드의 작업 분배 역할을 수행한다.
그렇다면 조금 더 파서 구조와 동작원리를 파악해보자.

먼저 구조는 다음과 같이 이루어져 있다.

  • scheduler : ForkJoinPool
  • carrierThread : 실제로 작업을 수행시키는 플랫폼 스레드
  • runContinuation : 작업 내용

또한, 사용하는 자원의 양은 다음과 같다.

  플랫폼 스레드 가상 스레드
메타 데이터 사이즈 약 2kb(OS별로 차이있음) 200~300 B
메모리 미리 할당된 Stack 사용 필요시 마다 Heap 사용
컨텍스트 스위칭 비용 1~10us (커널영역에서 발생하는 작업) ns (or 1us 미만)

이러한 내용을 토대로 동작원리를 살펴보자면,

  1. runContinuation을 carrierThread의 workQueue에 push한다.
  2. workQueue에 있는 runContinuation들은 work stealing 방식으로 carrier thread에 의해 처리된다.
    work stealing 방식은 스레드마다 개별 큐를 두고, 하나의 스레드 큐가 비게 되면 다른 스레드 큐에서 훔쳐와서 일을 하는 방식이다.
    자세한 내용은 여기를 참고하자.
  3. workQueue에서 진행되던 runContinuation들은 I/O, Interruption, 작업완료 등이 발생하면 pop되어 park(스레드 일시정지) 과정을 거쳐 다시 힙 메모리로 돌아간다.

이런 방식으로 가상 스레드가 동작한다. 그렇다면 기존 스레드와 가상 스레드 간의 벤치마킹이 또 필요한데, 이 부분은 우아한 형제들 기술블로그에 잘 나와있다. 사실 해당 포스팅 내용도 정리하며 궁금한 내용들을 작성한 나만의 메모장(?) 같은거라.. 겹치는 부분이 많다.
아무튼 약 51%가량 성능 개선이 이루어졌다고 하니, 획기적이지 않을 수 없다.

그렇다면 가상 스레드를 사용하면서 주의해야 할 점 몇가지를 또 알아보도록 하자.

가상 스레드 사용 시 주의사항

쓰레드 풀링 금지

쓰레드 풀링은 쓰레드 풀을 사용한다는 의미이다. 아무튼 왜 가상 스레드가 풀링을 하지 말라는건가?
일단 쓰레드 풀을 사용하는 이유는, 비용이 많이 드는 작업을 위한 것이다. 즉, 비용 절감을 위해 고안된 장치인데, 가상 스레드는 살아있는 동안 하나의 작업만을 위해 존재한다.

즉, 스레드 하나는 해당 작업을 완수하고 소멸되어야 한다. 그런데 작업을 공유하는 쓰레드 풀을 활용한다? 앞뒤가 맞지 않는다.
따라서 가상 스레드는 풀링 없이 매번 생성해주도록 하자.

스레드 로컬 사용 자제

스레드 로컬은 현재 스레드의 실행과 연관된 작업들에 대한 관리를 주로 이행한다. (참고로 이전에 작성한 Security 관련 포스트에서 스레드 로컬 이야기가 나왔다. 다시 보니 반갑구나.)
그러나 다음과 같은 이슈들이 몇가지 존재한다고 한다.

  • 명확한 생명주기가 없음(unbounded lifetime)
  • 변경 가능성에 대해서 제약이 없음(unconstrained mutability)
  • 메모리 사용에 대해서 제약이 없음(unconstrained memory usage)
  • 값비싼 상속 기능을 사용하는 InheritableThreadLocal의 성능 문제

가상 스레드는 스레드 로컬을 지원하지만, 이는 기존 코드들이 스레드 로컬을 사용하기 때문에 울며 겨자먹기로 지원하게 된것이지, JVM 개발자들은 이를 원하지 않았다고 한다. 이러한 스레드 로컬의 단점을 보완하여 개발중이라고 하니, 추후에 개선될 것으로 보인다. 

이 외에도 여러가지 사항이 있는데, 위에 두 가지가 가장 중요하다고 생각되어 메모한다. 추가적인 고려사항은 여기를 참고하자.

결론


가상 스레드에 대해 간단하게 알아봤다. 지금까지 일을 하거나 프로젝트에서 가상스레드를 도입해본 적이 없는데...
추후에 가상 스레드로 현업에서 개발을 진행해봤으면 하는 바람이다.. ^0^.. 

기존 스레드에 비해 월등한 성능을 보여준다고 하니.. 항상 성능 이슈로 볼멘소리가 나오는 자바 진영에도 힘이 되어주길 바란다.

구독 및 하트는 정보 포스팅 제작에 큰 힘이됩니다♡

 

728x90
반응형