JVM에서 JIT 컴파일 된 코드를 보는 방법은 무엇입니까?


84

JVM에서 JIT가 생성하는 네이티브 코드를 볼 수있는 방법이 있습니까?


JIT로 컴파일 된 (네이티브) 코드를 보시겠습니까, 아니면 바이트 코드 만 보시겠습니까? 여기서이 질문을하면 네이티브 코드를 정말로보고 싶은지 의문이 생기기 때문에 묻습니다. 그리고 죄송합니다. 그런 도구도 모르겠습니다.
— gimpf

3
Exaclty JIT 컴파일 된 네이티브 코드를보고 싶습니다. 물론 그것은 내가 일을 끝내는 데 필요한 것이 아니라 일종의 실험과 조사입니다.
— alsor.net

사소한 프레임 문제 : 최신 JVM에서 사용되는 동적 컴파일러 에는 컴파일 된 코드 버전이 하나만 있는 것이 아닙니다 . 해석을 시작한 다음 메서드 또는 그 일부를 컴파일 한 다음 클래스가로드 / 언로드되거나 사용 패턴이 이동하거나 성능 통계에 따라 잠재적으로 여러 번 다시 컴파일 할 수 있습니다. (저는 그것이 유익하다고 생각되면 컴파일 된 버전을 버리고 해석으로 돌아갈 수도 있다고 생각합니다.) 따라서 다른 시스템에서 다른 코드를 얻을 수있을뿐만 아니라 같은 시스템에서 다른 실행을 할 때도 마찬가지 일뿐만 아니라 같은 실행 에서 다른 시간에 .
— gidds

답변:


45

Sun Hotspot JVM (즉, Oracle이 java.com에서 제공 한 것)을 사용하고 있다고 가정하면 플래그를 추가 할 수 있습니다.

-XX : + PrintOptoAssembly

코드를 실행할 때. 이렇게하면 JIT 컴파일러에서 생성 된 최적화 된 코드가 출력되고 나머지는 제외됩니다.

최적화되지 않은 부분을 포함하여 전체 바이트 코드를 보려면 다음을 추가하십시오.

-XX : CompileThreshold = #

코드를 실행할 때.

이 명령과 JIT의 일반적인 기능에 대한 자세한 내용은 여기 에서 읽을 수 있습니다 .


이 옵션은 디버그 빌드 또는 기타에만 존재합니까? 내 JVM ( "Java (TM) SE 런타임 환경 (빌드 1.6.0_16-b01"))이 웹 소스에 Sun Java 6 및 OpenJDK에서이 기능을 사용할 수 있다고 표시하더라도 인식하지 못하기 때문입니다.
— Joachim Sauer

2
예, DEBUG 바이너리가 필요합니다. blogs.warwick.ac.uk/richardwarburton/entry/…
— alsor.net

3
적어도 요즘에는 -XX : + PrintAssembly 여야하지 않습니까? 내 컴퓨터에서 테스트했으며 여기에있는 내용과 일치합니다. wikis.sun.com/display/HotSpotInternals/PrintAssembly 이 옵션과 디스어셈블러 플러그인 앞에 -XX : + UnlockDiagnosticVMOptions가 필요합니다.
— Blaisorblade 2011

@Blaisorblade 내가 받고 있습니다 : 잘못 지정된 VM 옵션 'PrintAssembly'오류 : Java 가상 머신을 만들 수 없습니다. 오류 : 치명적인 예외가 발생했습니다. 프로그램이 종료됩니다.
— Koray Tugay

@KorayTugay 다른 답변보기 — 업데이트 된 링크는 stackoverflow.com/a/15146962/53974 또는 stackoverflow.com/a/4149878/53974에 제공된대로 wikis.oracle.com/display/HotSpotInternals/PrintAssembly 입니다. 다음 지침이 작동하지 않으면 적절한 장소에서 세부 사항을 묻습니다 (이 질문을 참조하여 귀하의 사례에 대해 다른 질문을해야하는지 확실하지 않음).
— Blaisorblade

76

일반적인 사용법

다른 답변에서 설명한 것처럼 다음 JVM 옵션으로 실행할 수 있습니다.

-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly

특정 방법으로 필터링

다음 구문을 사용하여 특정 메서드를 필터링 할 수도 있습니다.

-XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*MyClass.myMethod

메모:

  • OS 등에 따라 두 번째 인수를 따옴표 안에 넣어야 할 수도 있습니다.
  • 방법이 인라인되면 일부 최적화를 놓칠 수 있습니다.

방법 : Windows에 필요한 라이브러리 설치

Windows를 실행중인 경우이 페이지 에는 빌드 및 설치 방법 hsdis-amd64.dll과 hsdis-i386.dll작동하는 데 필요한 지침 이 있습니다. 참조를 위해 아래를 복사하고 해당 페이지 *의 내용을 확장합니다.


미리 빌드 된 바이너리를 얻을 수있는 위치

fcml 프로젝트 에서 미리 빌드 된 Windows 용 바이너리를 다운로드 할 수 있습니다.

어떻게 구축 hsdis-amd64.dll및 hsdis-i386.dllWindows에서

이 버전의 가이드는 64 비트 Cygwin을 사용하고 hsdis-amd64.dll을 생성하는 Windows 8.1 64 비트에서 준비되었습니다.

  1. Cygwin을 설치하십시오 . 상기 Select Packages화면 (확대하여 다음 패키지를 추가 Devel한 후, 카테고리를 한 번 클릭하면 Skip다음 각 패키지 이름 라벨) :

    • make
    • mingw64-x86_64-gcc-core(에만 필요 hsdis-amd64.dll)
    • mingw64-i686-gcc-core(에만 필요 hsdis-i386.dll)
    • diffutils( Utils카테고리)
  2. Cygwin 터미널을 실행하십시오. 이것은 설치 프로그램이 만든 바탕 화면 또는 시작 메뉴 아이콘을 사용하여 수행 할 수 있으며 Cygwin 홈 디렉토리 ( C:\cygwin\home\<username>\또는 C:\cygwin64\home\<username>\기본적으로)를 생성합니다.

  3. 최신 GNU binutils 소스 패키지를 다운로드하고 Cygwin 홈 디렉토리에 압축을 풉니 다. 작성 당시 최신 패키지는 binutils-2.25.tar.bz2. 그러면 binutils-2.25Cygwin 홈 디렉토리에 이름이 지정된 디렉토리 (또는 최신 버전이 무엇이든)가 생성됩니다.
  4. JDK 8 업데이트 저장소 로 이동하여 설치된 JRE 버전에 해당하는 태그를 선택한 다음 bz2를 클릭 하여 OpenJDK 소스를 다운로드합니다 . hsdis 디렉토리 (에서 찾을 수 있음 src\share\tools)를 Cygwin 홈 디렉토리로 추출하십시오 .
  5. Cygwin 터미널에을 입력 cd ~/hsdis합니다.
  6. 빌드하려면 다음을 hsdis-amd64.dll입력하십시오.

    make OS=Linux MINGW=x86_64-w64-mingw32 'AR=$(MINGW)-ar' BINUTILS=~/binutils-2.25

    빌드하려면 다음을 hsdis-i386.dll입력하십시오.

    make OS=Linux MINGW=i686-w64-mingw32 'AR=$(MINGW)-ar' BINUTILS=~/binutils-2.25

    두 경우 모두 2.25다운로드 한 binutils 버전으로 바꿉니다. OS=LinuxCygwin은 Linux와 유사한 환경이지만 hsdis makefile은이를 인식하지 못하기 때문에 필요합니다.

  7. 빌드가 실패하고 메시지 ./chew: No such file or directory및 gcc: command not found. <Cygwin home directory>\hsdis\build\Linux-amd64\bfd\MakefileWordpad 또는 Notepad ++와 같은 텍스트 편집기에서 편집 SUBDIRS = doc po하여 (binutils 2.25를 사용하는 경우 342 행) SUBDIRS = po. 이전 명령을 다시 실행하십시오.

DLL은 지금부터 복사하여 설치할 수 있습니다 hsdis\build\Linux-amd64또는 hsdis\build\Linux-i586귀하의 JRE의에 bin\server또는 bin\client디렉토리. 를 검색하여 시스템에서 이러한 모든 디렉토리를 찾을 수 있습니다 java.dll.

보너스 팁 : AT & T보다 Intel ASM 구문을 선호하는 경우 사용 -XX:PrintAssemblyOptions=intel하는 다른 PrintAssembly 옵션과 함께 지정 하십시오.

* 페이지 라이센스는 크리에이티브 커먼즈입니다.


1
다른 플랫폼에 대한 사전 구축 된 바이너리 - kenai.com/projects/base-hsdis/downloads
— 애쉬 윈 Jayaprakash

@AshwinJayaprakash Mac OS에서이 파일을 어디에 두어야합니까?
— Koray Tugay

@KorayTugay 넣어 그들을/usr/lib/
— Jean-François Savard

링크 된 페이지의 최신 버전에서 복사하여 답변을 업데이트했지만 이는 일반적으로 외부 리소스를 그대로 복사하는 대신 외부 리소스에 연결하는 이유를 강조합니다.
— Aleksandr Dubinsky

@AleksandrDubinsky 업데이트 해 주셔서 감사합니다. 일부러 복사했습니다 : 해당 사이트가 다운되면 내 대답은 여전히 ​​자체 포함됩니다 ...
— assylias

29

를 사용하려면 hsdis 플러그인이 필요합니다 PrintAssembly. 편리한 선택은 FCML 라이브러리를 기반으로하는 hsdis 플러그인입니다.

UNIX 계열 시스템 용으로 컴파일 할 수 있으며 Windows 에서는 Sourceforge 의 FCML 다운로드 섹션에 있는 미리 빌드 된 라이브러리를 사용할 수 있습니다 .

Windows에 설치하려면 :

  • dll을 추출합니다 (hsdis-1.1.2-win32-i386.zip 및 hsdis-1.1.2-win32-amd64.zip에서 찾을 수 있음).
  • dll을 존재하는 곳에 복사합니다 java.dll(Windows 검색 사용). 내 시스템에서 두 위치에서 찾았습니다.
    • C:\Program Files\Java\jre1.8.0_45\bin\server
    • C:\Program Files\Java\jdk1.8.0_45\jre\bin\server

Linux에 설치하려면 :

  • 소스 코드 다운로드, 추출
  • cd <source code dir>
  • ./configure && make && sudo make install
  • cd example/hsdis && make && sudo make install
  • sudo ln -s /usr/local/lib/libhsdis.so <JDK PATH>/lib/amd64/hsdis-amd64.so
  • sudo ln -s /usr/local/lib/libhsdis.so <JDK PATH>/jre/lib/amd64/hsdis-amd64.so
  • 내 시스템에서 JDK는 /usr/lib/jvm/java-8-oracle

실행 방법 :

java -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly 
-XX:+LogCompilation -XX:PrintAssemblyOptions=intel,mpad=10,cpad=10,code 
-jar fcml-test.jar

추가 구성 매개 변수 :

코드 니모닉 앞에 기계어 코드를 인쇄합니다.
intel Intel 구문을 사용합니다.
gas AT & T 어셈블러 구문을 사용합니다 (GNU 어셈블러 호환 가능).
dec IMM 및 변위를 10 진수 값으로 인쇄합니다.
mpad = XX 명령어의 니모닉 부분에 대한 패딩입니다.
cpad = XX 기계 코드의 패딩입니다.
seg 기본 세그먼트 레지스터를 표시합니다.
0 HEX 리터럴의 경우 선행 0을 표시합니다.

Intel 구문은 Windows의 경우 기본 구문이고 AT & T 구문은 GNU / Linux의 기본 구문입니다.

자세한 내용은 FCML 라이브러리 참조 매뉴얼을 참조 하십시오.


lib를 수정 해 주셔서 감사합니다. Linux에서도 잘 작동합니다. 깔끔하게 정리하기 위해 이전 댓글을 삭제하고 있습니다.
— Aleksandr Dubinsky

Linux에서 libhsdis.so를 설치하고 hsdis-amd64.so에 대한 소프트 링크를 만든 후 java 명령을 실행하면 prmpts가 hsdis-amd64.so를 찾을 수 없습니다. 재부팅 한 다음 자바를 다시 실행하면 괜찮습니다. 소프트 링크가 즉시 작동하도록 재부팅을 피하는 방법은 무엇입니까? 로그 아웃?
— gfan

2
추가 사항 : 일부 Linux 배포판에서는 Ubuntu apt-get install libhsdis0-fcml( askubuntu.com/a/991166/489909 ) 와 같이 패키지 만 설치할 수 있습니다 . 이것을 직접 구축 할 필요는 없습니다.
— David Georg Reichelt


5

Windows 시스템에서 실행하는 경우 WinDbg가 도움이 될 것이라고 생각합니다. 나는 단지 하나의 병을 실행했습니다.

  • 그런 다음 Windbg를 통해 Java 프로세스에 연결했습니다.
  • ~ 명령으로 스레드를 검사했습니다 . 11 개의 스레드가 있었고 0 개의 스레드가 주 작업자 스레드였습니다.
  • 0 스레드로 전환- ~ 0s
  • kb에 의해 관리되지 않은 호출 스택을 살펴보면 다음과 같습니다.

    0008fba8 7c90e9c0의 ntdll! KiFastSystemCallRet
    0008fbac 7c8025cb의 ntdll! ZwWaitForSingleObject +에서 0xc
    0008fc10 7c802532 KERNEL32! WaitForSingleObjectEx + 0xa8
    0008fc24 00403a13 KERNEL32!의 WaitForSingleObject + 0x12를
    0008fc40 00402f68 자바 + 0x3a13
    0008fee4 004087b8 자바 + 0x2f68
    0008ffc0 7c816fd7 자바 + 0x87b8

    0008fff0 00000000 KERNEL32! BaseProcessStart + 인 0x23

강조 표시된 줄은 JVM에서 직접 실행되는 JIT 코드입니다.

  • 그런 다음 메소드 주소를 찾을 수 있습니다.
    java + 0x2f68은 00402f68입니다.

  • WinDBG :
    보기-> 디스 어셈블리를 클릭하십시오.
    편집-> 주소로 이동을 누르십시오.
    넣어 00402f68를 이
    얻은

    00402f68 55 push ebp
    00402f69 8bec mov ebp, esp
    00402f6b 81ec80020000 sub esp, 280h
    00402f71 53 push ebx
    00402f72 56 push esi
    00402f73 57 push edi
    ... 등

여기에 추가 정보는 프로세스 탐색기 및 WinDbg를 사용하여 메모리 덤프에서 JIT 코드를 추적하는 방법 의 예 입니다.


4

기계 코드와 일부 성능 데이터를 보는 또 다른 방법은 Java 코드를 기계 코드로 실행하는 것을 시각화하는 Java 플러그인이있는 AMD의 CodeAnalyst 또는 OProfile을 사용하는 것입니다.


0

JMH의 perfasm 프로파일 러 ( LinuxPerfAsmProfiler또는 WinPerfAsmProfiler)로 핫스팟 어셈블리를 인쇄합니다 . JMH은 필요하지 hsdis가에 의존하기 때문에 라이브러리를 PrintAssembly.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.