ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Java] Native Resource와 native 예약어
    Java 2024. 10. 30. 22:44

    이 글을 읽기 전 먼저 읽으면 도움이 됩니다.
    https://jngsngjn.tistory.com/41

    [Java] JVM의 메모리 영역 (JVM Memory Structure)

    JVM(Java Virtual Machine)의 메모리 영역은 Java 애플리케이션이 실행될 때 사용하는 메모리 공간을 여러 영역으로 나눈 것이다. 크게 5가지 영역으로 구분되며, 각 영역은 고유한 역할을 가지고 있다.

    jngsngjn.tistory.com


    GC(Garbage Collector)는 JVM에서 더 이상 참조되지 않는 객체(불필요한 객체)를 자동으로 탐지하고 메모리에서 제거한다. 이렇듯 GC의 역할은 자바 프로그램이 실행되는 동안 힙 메모리에 할당된 객체들 중에서 필요 없는 객체를 찾아 메모리를 회수하는 것이다. 이렇게 자바는 메모리 관리를 자동화하여, 개발자가 수동으로 메모리를 해제하지 않게 해 준다.
     
    여기서 나는 한 가지 궁금증이 생겼다. DB Connection이나 FileStream 등을 사용하고 나면 항상 finally 블록에서 반드시 명시적으로 close()를 호출하여 자원을 해제해 주어야 한다고 배웠다. GC가 이런 녀석들은 관리해 주지 않는 것인가?! 이에 대해 알아보기로 하자.


    네이티브 리소스 (Native Resource)

    (1) 정의

    운영체제에 의해 직접 관리되는 자원으로, 자바의 JVM 메모리 관리 범위를 벗어난 시스템 자원을 의미한다. 파일 핸들, 네트워크 소켓, 데이터베이스 커넥션 등과 같이 운영체제 레벨에서 할당되고 해제되는 자원 등이 이에 해당한다.
     

    (2) 종류

    파일 핸들, 네트워크 소켓, 데이터베이스 커넥션, 그래픽 리소스 등
     

    (3) 특징

    • 운영체제에 의존적 : 네이티브 리소스는 자바의 플랫폼 독립성과는 달리, 운영체제마다 할당 방식과 관리 방식이 다르다. 예를 들어, 파일을 열거나 데이터베이스와 연결할 때 각 운영체제의 API나 커널 기능이 사용된다.
    • 힙 메모리 외부에 존재 : 네이티브 리소스는 JVM 힙 메모리 내부가 아니라 운영체제에서 관리하는 네이티브 메모리에 존재한다. 자바 객체가 네이티브 리소스를 참조하지만, 네이티브 리소스 자체는 힙 메모리 외부에 존재하는 것이다.
    • 명시적인 해제 필요 : 자바의 GC는 힙 메모리의 객체를 관리할 뿐, 네이티브 리소스는 자동으로 해제하지 않는다. 따라서 close() 메서드 호출을 통해 명시적으로 해제해야 한다. 이를 통해 네이티브 리소스를 운영체제에 반환할 수 있다. close()를 호출하지 않으면 네이티브 리소스는 운영체제에 계속 남아 있게 되어 시스템 리소스가 낭비될 수 있다. Java 7 이상에서는 try-with-resources 구문을 사용하여 close()가 자동으로 호출되도록 할 수 있다. 이를 통해 종종 close() 호출을 빼먹는 실수를 줄일 수 있다.
    try (FileInputStream fis = new FileInputStream("file.txt")) {
        // 파일 작업 수행
    } // try 블록을 벗어날 때 fis.close()가 자동으로 호출

    다음과 같은 순서로 이해하면 쉽다!

    • GC는 힙 메모리에 할당된 객체를 관리한다.
    • DB Connection, FileOutputStream 등은 결국 자바 객체이기 때문에 힙 메모리에 저장된다.
    • 이와 동시에 이것들은 JVM 외부의 네이티브 리소스와 연결된 객체이다.
    • 네이티브 리소스는 운영체제에 의해 관리되기 때문에 JVM의 직접적인 관리 범위를 벗어나 있다.
    • 따라서 네이티브 리소스를 사용하는 객체가 메모리에서 해제되더라도, GC는 네이티브 리소스를 자동으로 해제할 수 없다.
    • 결국 개발자가 close()를 호출해 주어야 한다.
    • 실수하지 않도록 try-with-resources 구문을 사용하자.

    native 예약어

    자바에는 많은 예약어가 있다. 혹시 그중에서 native 예약어를 본 적 있는가? 이것은 앞서 살펴본 내용과 밀접한 관련이 있다. native 예약어는 자바로는 직접 구현할 수 없는 기능을 네이티브 코드(C/C++)로 구현할 때 사용한다. 위에서 다룬 네이티브 리소스를 다루기 위해서도 자바는 종종 native 예약어를 통해 운영체제 레벨의 시스템 자원에 접근한다.
     
    native 예약어는 메서드 선언에 사용되어, 해당 메서드가 네이티브 코드로 구현됨을 나타낸다. 자바 표준 라이브러리에도 native 메서드로 구현된 부분이 많이 존재한다. 예를 들어 FileOutputStream 클래스에는 다음과 같은 메서드가 있다.

    private native void open0(String name, boolean append) throws FileNotFoundException;

     

    native 메서드의 특징

    (1) 메서드 선언만 존재 : native 메서드는 구현부가 존재하지 않고 선언만 존재한다. 해당 메서드의 구현이 JVM 외부에 존재하는 네이티브 코드로 구현되어 있기 때문이다.
     
    (2) 네이티브 라이브러리 로드 : native 메서드를 사용하려면 JVM이 네이티브 라이브러리를 미리 로드해야 한다. System.loadLibrary("libraryName")를 통해 로드할 수 있다. FileOutputStream과 같은 자바 표준 라이브러리의 native 메서드는 기본적으로 JDK와 함께 제공되는 네이티브 라이브러리에 의해 구현되어 있다.


    native 메서드와 네이티브 코드를 이어주는 다리 역할을 하는 JNI라는 것이 있다. 이에 대해서는 짧게 보고 마무리하자.
     

    JNI (Java Native Interface)

    JNI는 자바가 운영체제 레벨에서 C 또는 C++로 작성된 네이티브 코드를 호출할 수 있도록 해주는 API이다. native 메서드가 호출되면 JNI라는 인터페이스를 통해 네이티브 코드가 실행되는 것이다.

    Java Native Interface