본문 바로가기
Java & Kolin

M1 사용기 - JVM환경에서 ARM / Rosetta 번역 알아내기

by Limm_jk 2021. 10. 10.

개발을 하다보면 JVM만이 아닌 다른 환경에 종속적인 코드를 작성할 때가 있습니다. (ex. TestContainers - docker 컨테이너를 실행)

이때, JVM은 정상적으로 로제타로 번역이 되어서 잘 실행되지만, 이런 코드로 실행되는 것들은 M1에 맞지 않는 경우가 있습니다.

이런 경우를 대비하여 코드단에서 ARM / M1인지 알아보는 방법을 작성해보겠습니다.

 

* 해당 내용은 Kotlin으로 작성되었습니다.

 

1. System.getProperty

먼저 해볼 수 있는 것은 아키텍쳐 이름을 가져오는 것입니다.

이 정보는 시스템 클래스에 os.arch로 저장되어있습니다. 아래와 같은 코드로 확인이 가능합니다.

if (System.getProperty("os.arch").toLowerCase().contains("arm"))
	return true

다음과 같이 os.arch에 해당하는 값을 가져와서 arm을 포함하고 있는지 확인하고 있습니다.

 

raspberry pi와 같은 arm환경이라면 정상적으로 arm이라 뜰 것입니다.

하지만, 우리의 m1 + JVM에서는 그렇지 않습니다.

x86_64라고 뜨는 것을 볼 수 있습니다. 아마 로제타로 번역이 되어있기 때문이 아닐까 생각해봅니다.

 

2. sysctl sysctl.proc_translated

그렇다면 로제타로 번역된 환경을 위하여 번역되었음을 확인하는 코드 또한 작성해줘야겠습니다.

흐름은 os가 mac일 때, 번역되었는지 확인한다. 정도면 좋겠습니다.

하지만 번역되었는지를 어떻게 확인할 수 있을까요?

다음과 같이 애플 개발자 홈페이지에서 다음과 같이 번역되었는지를 확인하는 방법을 제공합니다.

 

해당 명령어는 다음과 같이 동작합니다.

 

1. 번역된 경우

$ sysctl sysctl.proc_translated
sysctl.proc_translated: 1

2. 번역되지 않은 경우

$ sysctl sysctl.proc_translated
sysctl.proc_translated: 0

3. ARM 구조가 아닌 MAC에서

$ sysctl sysctl.proc_translated
sysctl: unknown oid 'sysctl.proc_translated'

이를 기반으로 코드를 한번 작성해보겠습니다.

val os: String = SystemUtils.OS_NAME.toLowerCase()

// if JVM is translated on Rosetta
if (os.contains("mac")) {
  val process = Runtime.getRuntime().exec("sysctl sysctl.proc_translated")

  BufferedReader(InputStreamReader(process.inputStream)).use {
  val line = it.readLine()

  if (line != null && line.endsWith("1")) {
    	return true
    }
  }
}

os를 먼저 확인해서 os가 mac이라면 sysctl sysctl.proc_translated 명령어를 사용합니다.

그리고 실행하고 있는 JVM이 번역되었는지 확인하기 위하여 해당 명령어의 응답이 1로 종료되었는지 확인합니다.

 

이때, 1로 종료되었다면 번역된 arm환경이라는 의미이므로 arm에 맞는 환경으로 구동해주면 됩니다.

 

결론적으로 해당 ARM임을 알아내는 함수는 다음과 같이 작성할 수 있습니다.

private fun isARM(): Boolean {
  val os: String = SystemUtils.OS_NAME.toLowerCase()

  if (System.getProperty("os.arch").toLowerCase().contains("arm"))
  	return true

  // if JVM is translated on Rosetta
  if (os.contains("mac")) {
    val process = Runtime.getRuntime().exec("sysctl sysctl.proc_translated")

    BufferedReader(InputStreamReader(process.inputStream)).use {
      val line = it.readLine()
      
      if (line != null && line.endsWith("1")) {
      	return true
      }
    }
  }

  return false
}

M1만이 아닌 ARM환경을 위하여 1번에서 소개했던 arm아키텍쳐를 체크하는 로직도 함께 붙여주었습니다 :)

댓글