IT/Java

A fatal error has been detected by the Java Runtime Environment 해결

yeTi 2020. 5. 29. 16:14

안녕하세요. yeTi입니다.
오늘은 자바 어플리케이션을 도커로 배포시 발생하는 A fatal error has been detected by the Java Runtime Environment오류를 해결해보겠습니다.

현상

도커Springboot 어플리케이션을 배포하는 상황에서 베이스이미지로 openjdk:12-alpine을 사용했습니다.

쿠버네티스에 배포 후 어플리케이션이 로드하는 과정에서 다음과 같은 오류가 발생했습니다.

#
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGILL (0x4) at pc=0x00007fb82810e9bc, pid=1, tid=6
#
# JRE version: OpenJDK Runtime Environment (12.0+29) (build 12-ea+29)
# Java VM: OpenJDK 64-Bit Server VM (12-ea+29, mixed mode, sharing, tiered, compressed oops, serial gc, linux-amd64)
# Problematic frame:
# v ~StubRoutines::updateBytesCRC32
#
# Core dump will be written. Default location: Core dumps may be processed with "/usr/share/apport/apport %p %s %c %d %P %E" (or dumping to //core.1)
#
# An error report file with more information is saved as:
# //hs_err_pid1.log
Could not load hsdis-amd64.so; library not loadable; PrintAssembly is disabled
#
# If you would like to submit a bug report, please visit:
# http://bugreport.java.com/bugreport/crash.jsp
#

현상 파악

로그 분석

로그를 확인해보면 JRE에서 SIGILL오류가 발생했고 PrintAssembly가 비활성화되어 hsdis-amd64.so를 로드할 수 없다는 내용입니다.

이것만 가지고 정확히 어떤 부분에서 오류가 났는지는 알수가 없습니다.

검색

Could not load hsdis-amd64.so - Github에 따르면 Elasticsearch프로젝트에서 해당 이슈 발생시 java 옵션에 -XX:UseSSE=2를 추가하면 해결된다고 합니다.

Openjdk tests failed with msg 'Could not load hsdis-amd64.so; library not loadable; PrintAssembly is disabled' - Github에 따르면 java 옵션에 -XX:UseAVX=0을 추가하면 해결된다고 합니다. 해당 이슈는 2019년 2월에 생성되어 2020년 4월에 close됐습니다.

비슷한 이슈로 Set build JVM args - Github에 따르면 Kubernetes 환경에서 빌드 오류를 예방하려면 -XX:UseAVX=0옵션을 추가해서 사용하라는 내용이 있습니다.

AVX enabled by default for Skylake even when unsupported - Github에 따르면 Skylake server CPUs를 위해 UseAVX 옵션의 기본값이 2로 설정됐습니다.

분석

위에서 언급한 단어들을 확인해보겠습니다.

SSE(Streaming SIMD Extensions, SSE)AVX(Advanced Vector Extensions)x86 명령어 집합의 확장으로 SIMD명령어 집합중의 하나입니다.

이는 CPU에 따라서 지원하는 범위가 달라 CPU에 맞게 설정해줘야합니다.

그래서 현재 사용하고 있는 CPU를 확인해봤습니다.

Intel(R) Xeon(R) Silver 4214 CPU @ 2.20GHz x86_64 - Cascade Lake를 사용하고 있고 지원하는 명령어 집합은 avx2, avx512f, avx512dq, avx512cd, avx512bw, avx512vl, avx512, sse, sse2, ssse3, sse4_1, sse4_2를 지원합니다.

이와 같이 Cascadelake 아키텍처를 사용하고 있고, AVX와, SSE를 폭넓게 지원하고 있습니다.

참고적으로 Intel CPU의 아키텍쳐 변천사를 보면 Nehalem, Sandybridge, Ivybridge, Haswell, Broadwell, Skylake, Cascadelake 순으로 변화했고 Cascadelake는 2019년 4월에 출시한 아키텍처입니다.

이제부터 JDK12가 지원하는 CPU 및 AVX, SSE의 범위를 확인해보겠습니다.

enum  Extended_Family {
  // AMD
  CPU_FAMILY_AMD_11H = 0x11,
  // ZX
  CPU_FAMILY_ZX_CORE_F6 = 6,
  CPU_FAMILY_ZX_CORE_F7 = 7,
  // Intel
  CPU_FAMILY_INTEL_CORE = 6,
  CPU_MODEL_NEHALEM = 0x1e,
  CPU_MODEL_NEHALEM_EP = 0x1a,
  CPU_MODEL_NEHALEM_EX = 0x2e,
  CPU_MODEL_WESTMERE = 0x25,
  CPU_MODEL_WESTMERE_EP = 0x2c,
  CPU_MODEL_WESTMERE_EX = 0x2f,
  CPU_MODEL_SANDYBRIDGE = 0x2a,
  CPU_MODEL_SANDYBRIDGE_EP = 0x2d,
  CPU_MODEL_IVYBRIDGE_EP = 0x3a,
  CPU_MODEL_HASWELL_E3 = 0x3c,
  CPU_MODEL_HASWELL_E7 = 0x3f,
  CPU_MODEL_BROADWELL = 0x3d,
  CPU_MODEL_SKYLAKE = CPU_MODEL_HASWELL_E3
};

위와 같이 OpenJDK 12에서는 내부적으로 CPU를 확인해서 AVX의 사용수준을 정의하는데, 현재 Skylake까지 지원하고 있습니다. 따라서 Cascadelake에 대한 AVX, SSE 설정이 맞게 되지 않을것이라고 추측할 수 있습니다.

OpenJDK13, OpenJDK14를 확인해본 결과도 Cascadelake를 지원하지 않았습니다.

이어서 자바 옵션에 따른 명령어 확장 옵션의 설정을 확인해보겠습니다.

if (UseSSE < 4) {
  _features &= ~CPU_SSE4_1;
  _features &= ~CPU_SSE4_2;
}

if (UseSSE < 3) {
  _features &= ~CPU_SSE3;
  _features &= ~CPU_SSSE3;
  _features &= ~CPU_SSE4A;
}

if (UseSSE < 2)
  _features &= ~CPU_SSE2;

if (UseSSE < 1)
  _features &= ~CPU_SSE;
if (UseAVX < 3) {
  _features &= ~CPU_AVX512F;
  _features &= ~CPU_AVX512DQ;
  _features &= ~CPU_AVX512CD;
  _features &= ~CPU_AVX512BW;
  _features &= ~CPU_AVX512VL;
  _features &= ~CPU_AVX512_VPOPCNTDQ;
  _features &= ~CPU_VPCLMULQDQ;
  _features &= ~CPU_VAES;
}

if (UseAVX < 2)
  _features &= ~CPU_AVX2;

if (UseAVX < 1) {
  _features &= ~CPU_AVX;
  _features &= ~CPU_VZEROUPPER;
}

따라서 Intel(R) Xeon(R) Silver 4214 CPU @ 2.20GHz x86_64가 지원하는 명령어 집합인 UseSSE=3, UseAVX=2로 설정하면 CPU가 지원하는 명령어 집합을 올바르게 사용할 수 있습니다.

해결

java 옵션에 -XX:UseSSE=3, -XX:UseAVX=2을 추가하여 지원하는 명령어 집합을 맞게 사용하도록 설정합니다.