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.dllhsdis-i386.dll작동하는 데 필요한 지침 있습니다. 참조를 위해 아래를 복사하고 해당 페이지 *의 내용을 확장합니다.


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

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

어떻게 구축 hsdis-amd64.dllhsdis-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 directorygcc: 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.