JVM의 StackMapTable의 전생
9900 단어 Jacoco
4
스택 구조는 Code 속성(Classfile의 Code 속성을 가리킨다)의 속성표(attributes table) 구조에 있습니다.바이트 코드의 코드 속성에는 StackMapTable 속성이 최대 한 개까지 포함됩니다.Java 7 버전 이후 스택맵을 바이트 코드 파일의 강제 부분으로 사용합니다.원래 프로그래머는 JVM 중의 JIT 컴파일러의 세부 사항에 관심을 가질 필요가 없고 컴파일 원리나 데이터 흐름, 제어 흐름의 세부 사항도 알 필요가 없다.그러나 창고 맵은bytecode를 생성하려면 바이트 코드 명령에 대응하는 국부 변수와 조작 창고의 유형을 정확하게 알아야 합니다.자바 7이 컴파일하는 동안 검증을 하는 동안 해야 할 일, 즉 유형 검사, 즉 창고도에 포함된 내용을 했기 때문이다.
그러나 자바의 검증은 클래스를 불러올 때 한 번만 실행되며 대부분의 시간을 차지하는 작업은 IO의 소모이지 검증 과정이 아니다.현재 창고가 있어도 검증 과정은 실행되고 창고의 존재는 일부 검증 시간을 절약할 뿐이다.또한 JVM의 디자이너는 창고가 없는 검증의 실현을 호환해야 한다. 왜냐하면 Java7 이전 버전은 창고라는 개념을 강제하지 않았기 때문이다. 그러나 Java8은 창고의 바이트 구조를 이어갔다.
mally, you do not need to know much about the stackmap Table when using ASM. ASM can automatically generate this table for you unless you initialize the classwriter with
COMPUTE_FRAME
flags.For detailed table definition, you can consult the JVM specification (After Java7? ). The only place I know for the stackmap table is the bytecode verification when a class loader is about to load a bytecode, or you want to see the legal of bytecode you generated. In this case, verifier takes the entries in the table to make the correctness of bytecodes (e.g., type consistence, jump targets and so on). Check this link http://stackoverflow.com/questions/25109942/is-there-a-better-explanation-of-stack-map-frames if you want to detail explanation. 다음은 하나의 예를 결합하여 창고도의 구조를 살펴보자.
Java 코드는 다음과 같습니다.
package bytecode;
public class Coffee {
int bean;
public void getBean(int var) {
if (var > 0) {
this.bean = var;
} else {
throw new IllegalArgumentException();
}
}
}
Verbose를 사용하여Class 파일 구조를 보십시오. 다음과 같습니다. StackMapTable를 중점적으로 보십시오. 창고 그림에는 두 개의 entry가 포함되어 있습니다.
public class com.lijingyao.bytecode.Coffee
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#18 // java/lang/Object."":()V
#2 = Fieldref #5.#19 // com/lijingyao/bytecode/Coffee.bean:I
#3 = Class #20 // java/lang/IllegalArgumentException
#4 = Methodref #3.#18 // java/lang/IllegalArgumentException."":()V
#5 = Class #21 // com/lijingyao/bytecode/Coffee
#6 = Class #22 // java/lang/Object
#7 = Utf8 bean
#8 = Utf8 I
#9 = Utf8
#10 = Utf8 ()V
#11 = Utf8 Code
#12 = Utf8 LineNumberTable
#13 = Utf8 getBean
#14 = Utf8 (I)V
#15 = Utf8 StackMapTable
#16 = Utf8 SourceFile
#17 = Utf8 Coffee.java
#18 = NameAndType #9:#10 // "":()V
#19 = NameAndType #7:#8 // bean:I
#20 = Utf8 java/lang/IllegalArgumentException
#21 = Utf8 com/lijingyao/bytecode/Coffee
#22 = Utf8 java/lang/Object
{
int bean;
descriptor: I
flags:
public com.lijingyao.bytecode.Coffee();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
LineNumberTable:
line 6: 0
public void getBean(int);
descriptor: (I)V
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: iload_1
1: ifle 12
4: aload_0
5: iload_1
6: putfield #2 // Field bean:I
9: goto 20
12: new #3 // class java/lang/IllegalArgumentException
15: dup
16: invokespecial #4 // Method java/lang/IllegalArgumentException."":()V
19: athrow
20: return
LineNumberTable:
line 10: 0
line 11: 4
line 13: 12
line 15: 20
StackMapTable: number_of_entries = 2
frame_type = 12 /* same */
frame_type = 7 /* same */
}
Classfile의 상수 테이블 구조에서 #15의utf8 구조 속성을 볼 수 있습니다. StackMapTable 구조를 표시합니다.마지막 몇 줄에서 getBean (int) 방법의 구체적인 StackMapTable 구조를 볼 수 있는데 이것이 바로 창고도입니다.위에서 알 수 있듯이 StackMapTable에는 attribute 가 포함되어 있습니다.name_index,attribute_length,number_of_entries 및 entries 구조.여기서 numberof_entries는 Stack map frame의 개수, 즉 entries 개수를 나타냅니다. 이 예에서 두 개의'frame type'즉 =2를 볼 수 있습니다.entries 중 두 가지는 각각 프레임type = 12/* same/및 frametype = 7/same/.각 entry 요소는 메서드의 StackMapFrame을 나타냅니다.특정 바이트 코드의 편이량(프레임에 대응하는 바이트 코드 위치를 표시함)과 이 편이량에 있는 국부 변수표(local variables), 조작 창고(operand stack entry)에 필요한 검증 유형(ps: 국부 변수표와 조작 창고에 대해 참고할 수 있습니다http://blog.csdn.net/lijingyao8206/article/details/46562933를 참조하십시오.모든 방법의 첫 번째 StackMapFrame은 은식(entries[0])이며 유형 검사기의 방법 설명을 통해 계산된다.여기 우리가 본 프레임...type = 12/same */사실 메소드의 두 번째 StackMapFrame입니다. 표시된 StackMapFrame에 불과합니다.entries 테이블의 모든 Stack map 프레임은 이전 요소에 의존하고, 항목마다 편이량의 증가량을 사용하여 표시합니다.그래서 엔트리의 순서가 중요해요.
여기에 먼저 바이트 코드 명령과 파라미터 개념을 보충한다. 바이트 코드의 명령은 한 바이트 길이의 부호가 표시하는 조작 코드(Opcode)와 그 다음에 조작해야 할 몇 가지 파라미터로 구성된다.어떤 지령은 반드시 매개 변수가 필요한 것은 아니다.그러나 여기서 하나의 개념을 혼동하지 않도록 주의해야 한다. 이곳의 매개 변수와 조작수(oprends)는 같은 개념이 아니다.여기의arguments(파라미터)는 정적 값으로 컴파일된 바이트 코드에 저장되며 Oprends(조작수)의 값은 1절에서 소개한 조작수 창고의 운행기에서 값의 데이터 구조를 알 수 있습니다.분명히 말했는지 모르겠지만, 많은 번역문과 문장들이 지령집의 '매개 변수' 와 조작 창고의 '조작 수' 를 혼동하는 것을 발견했다.사실 매개 변수는 한 바이트 단위의 기호 정형으로 목표 주소를 가리키는 데 사용되며, 한 바이트가 넘으면 두 개의 매개 변수로 저장하고, 두 개의 매개 변수는 높은 위치에 따라 앞에 저장한다.예: 대상 명령 주소 = goto 명령 주소 + (매개변수 1 <<< 8 | 매개변수 2).
창고의 Stack map 프레임 구조의 entries는 편이량의 증가량으로 표시되기 때문에offsetdelta+1 공식은 모든 디스플레이 프레임에 따라 다음 디스플레이 프레임의 편이량을 계산합니다.예를 들어 getBean의 편이량을 이렇게 계산해야 한다. 이 예에서 첫 번째로 보이는 entries 항목:frametype=12, 여기 12는 이 프레임의 바이트 코드 편이량(offset delta)입니다.다음 원소의 편이량은 이전 원소의offsetdelta+1 + 현재 프레임의 편이도입니다.그래서 저희가 봤어요.
1: ifle 12 이 줄에서 ifle 바이트 코드 명령의 매개 변수는 12이므로 entries에서 첫 번째 StackMap Frame 요소의 바이트 코드 편이량은offsetdelta=12, 동일
9:goto 20 이 줄의 goto 바이트 명령의 매개 변수는 20이고 사실은 goto 12+1+7이다. 즉, goto 명령의 바이트 코드 편이량은 20이다.따라서 StackMapTable는 편향량을 기록함으로써 바이트 순서를 확보하고 기록을 중복하지 않습니다.StackMapTable는 JVM 형식 검사의 검증 단계에 바이트 명령 편이량에 대한 정보를 추가한 것에 불과하다는 것을 알 수 있다. 증량의 계산 방식을 통해 방법의 모든 바이트 편이량에 대한 검사를 간소화했다.
이 예제에서 StackMapFrame의 frametype/* same */항목은 현재 프레임과 이전 프레임이 같은 부분 변수를 가지고 있으며 현재 작업 스택이 비어 있음을 나타냅니다.이 문서는 주로 JVM 8 사양과 결합되어 있으며 오류가 있으면 바로잡아 주십시오.