티스토리 뷰

Java & Kotlin

[JVM] 네이티브 메서드와 블로킹 I/O로 대기중인 자바 스레드의 상태(Java Thread State on Blocking I/O with Native Method)

망나니개발자 2025. 7. 29. 10:00
반응형



1. 네이티브 메서드와 블로킹 I/O로 대기중인 자바 스레드의 상태
(Java Thread State on Blocking I/O with Native Method)


[ 문제에 대한 이해 ]

오늘날 대부분의 서비스들은 서버-클라이언트 모델을 기반으로 하고 있고, 스프링과 같은 애플리케이션의 내부 역시 다음과 같은 서버 소켓을 통한 연결, 그리고 통신이 기반을 이루게 된다.

그렇다면 다음과 같이 원시적인 형태의 ServerSocket 생성하고, 요청에 대한 수립을 기다리는 accept()를 호출중인 코드가 존재한다고 하자.

fun main(args: Array<String>) {
    val serverSocket = ServerSocket(13232)
    val socket = serverSocket.accept()

    socket.use { s ->
        s.getInputStream().use { inputStream ->
            val dis = DataInputStream(inputStream)
            while (true) {
                val input = dis.readUTF()
                println("message = $input")
            }
        }
    }
}

 

 

위의 코드를 실행중인 메인 스레드의 상태를 확인해보면 어떨까? 이전 포스팅에서 다음의 명령어로 스레드 덤프를 출력해서 확인할 수 있음을 살펴보았다.

jstack $(lsof -t -iTCP:13232 -sTCP:LISTEN) > socket.txt 

 

 

해당 내용을 출력하여 메인 스레드의 상태를 살펴보면 RUNNABLE임을 확인할 수 있다.

"main" #1 prio=5 os_prio=31 cpu=133.85ms elapsed=5.96s tid=0x000000014c80a800 nid=10499 runnable  [0x000000016dbc2000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.Net.accept(java.base@18.0.2/Native Method)
at sun.nio.ch.NioSocketImpl.accept(java.base@18.0.2/NioSocketImpl.java:752)
at java.net.ServerSocket.implAccept(java.base@18.0.2/ServerSocket.java:673)
at java.net.ServerSocket.platformImplAccept(java.base@18.0.2/ServerSocket.java:639)
at java.net.ServerSocket.implAccept(java.base@18.0.2/ServerSocket.java:615)
at java.net.ServerSocket.implAccept(java.base@18.0.2/ServerSocket.java:572)
at java.net.ServerSocket.accept(java.base@18.0.2/ServerSocket.java:530)
at com.mangkyu.ServerApplicationKt.main(ServerApplication.kt:8)

 

 

ServerSocket이 accept() 메서드를 통해 요청을 대기하는 블로킹 상황임이 분명한데, 스레드의 상태가 BLOCKED이 아닌 RUNNABLE인 이유는 무엇일까?

 

 

 

[ 문제 상황에 대한 해결 ]

먼저 ServerSocket의 accept() 메서드에 대한 자바 공식 문서 내용을 살펴보도록 하자. 다음은 자바 24의 공식 문서를 기반으로 작성된 내용이다. 확실히 소켓에 대한 연결이 이루어지기를 수신하고 연결을 수락하며, 연결이 만들어질 때까지 차단된다고 나와있다.

 

 

이어서 ServerSocket의 accept() 호출 스택 트레이스를 분석해보도록 하자. 최종적으로 호출하게 되는 메서드는 Net.accept()이다.

"main" #1 prio=5 os_prio=31 cpu=133.85ms elapsed=5.96s tid=0x000000014c80a800 nid=10499 runnable  [0x000000016dbc2000]
java.lang.Thread.State: RUNNABLE
at sun.nio.ch.Net.accept(java.base@18.0.2/Native Method)
at sun.nio.ch.NioSocketImpl.accept(java.base@18.0.2/NioSocketImpl.java:752)
at java.net.ServerSocket.implAccept(java.base@18.0.2/ServerSocket.java:673)
at java.net.ServerSocket.platformImplAccept(java.base@18.0.2/ServerSocket.java:639)
at java.net.ServerSocket.implAccept(java.base@18.0.2/ServerSocket.java:615)
at java.net.ServerSocket.implAccept(java.base@18.0.2/ServerSocket.java:572)
at java.net.ServerSocket.accept(java.base@18.0.2/ServerSocket.java:530)
at com.mangkyu.ServerApplicationKt.main(ServerApplication.kt:8)

 

 

그리고 Net.accept()를 보면, 해당 부분이 native 메서드로 구현되어 있음을 확인할 수 있다.

 

 

자바에서 native 키워드가 붙은 메서드는 자바로 구현되어 있지 않고, 플랫폼 의존적인 네이티브 언어(주로 C나 C++)로 구현된 메서드이다. JVM은 운영체제 시스템 콜 호출 등의 용도로 네이티브 메서드를 사용하는데, 이는 Java 만으로는 직접 호출할 수 없다. JVM 내부에는 JNI(Java Native Interface)가 존재하는데, 자바에서 native 메서드를 호출하면 JNI를 통해 C나 C++로 작성된 코드를 호출하게 되는 것이다. 참고로 네이티브 메서드의 호출은 우리에게 익숙한 Heap이 아닌 Native Method Stack이라는 영역에서 관리됨을 아래의 그림에서 확인할 수 있다.

 

 

네이티브 영역을 통한 OS 작업은 JVM의 세계보다 저수준이자 외부의 세계이기 때문에, JVM의 입장에서 ServerSocket.accept()처럼 native blocking I/O 호출 중인 스레드는 실제로는 OS에서 블로킹 중이지만, JVM은 이를 RUNNABLE로 볼 수 밖에 없는 것이다. JVM은 스레드가 JVM 코드 내부에서 명시적으로 wait/lock 중일 때만 WAITING, BLOCKED 상태로 구분할 수 있고, 네이티브 코드(C/C++) 내부에서 블로킹 되는지 여부는 감지할 수 없다. 즉, 실제 OS 커널 수준에서는 block 되어 있음에도 불구하고, JVM은 이를 모르기 때문에 RUNNABLE 상태로 남아있는 것이다. 이를 통해 다시 한번 확인할 수 있는 것은, JVM의 스레드 상태가 OS 스케줄링 상태와 1:1로 매핑되지 않는다는 것이다.

 

 

 

[ 결론 ]

블로킹 I/O를 수행하는 자바 스레드는 OS 수준에서는 블로킹 상태이다. 하지만 JVM의 스레드 상태가 OS 스케줄링 상태와 1:1로 매핑되지 않기 때문에, JVM에서는 RUNNABLE 상태로 표시될 수 있다. 이는 대게 JVM이 네이티브 메서드 호출을 통해 OS와 상호작용하기 때문에 발생하는 현상이다. 따라서, 스레드의 상태를 정확히 이해하기 위해서는 JVM과 OS의 차이를 인식하고, 네이티브 메서드 호출이 스레드 상태에 미치는 영향을 고려할 필요가 있다.

 

 

 

 

참고자료

 

 

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG more
«   2025/08   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함