Unsafe 클래스 getBoolean 방법 분석

4351 단어 openjdk
Unsafe 류 의 다음 방법 을 예 로 들 면
public native boolean getBoolean(Object var1, long var2);

자바 로 컬 방법 은 jni 호출 로 이 루어 집 니 다.
jboolean, Unsafe_GetBoolean(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)

실제
jboolean, Unsafe_Get##Boolean(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)

매크로 에 정의 되 어 있 습 니 다.\# 연결 연산 자 를 사 용 했 습 니 다. 내용 은?
UNSAFE_ENTRY(jboolean, Unsafe_Get##Boolean(JNIEnv *env, jobject unsafe, jobject obj, jlong offset)) \
  UnsafeWrapper("Unsafe_Get"#Boolean); \
  GET_FIELD(obj, offset, jboolean, v); \
  return v; \
UNSAFE_END \

원칙 은 호출 이다
GET_FIELD(obj, offset, jboolean, v)

원본 코드 보기
#define GET_FIELD(obj, offset, type_name, v) \
  oop p = JNIHandles::resolve(obj); \
  type_name v = *(type_name*)index_oop_from_field_offset_long(p, offset)

다시 보 자 indexoop_from_field_offset_long 방법
inline void* index_oop_from_field_offset_long(oop p, jlong field_offset) {
  jlong byte_offset = field_offset_to_byte_offset(field_offset);
#ifdef ASSERT
  if (p != NULL) {
    assert(byte_offset >= 0 && byte_offset <= (jlong)MAX_OBJECT_SIZE, "sane offset");
    if (byte_offset == (jint)byte_offset) {
      void* ptr_plus_disp = (address)p + byte_offset;
      assert((void*)p->obj_field_addr((jint)byte_offset) == ptr_plus_disp,
             "raw [ptr+disp] must be consistent with oop::field_base");
    }
    jlong p_size = HeapWordSize * (jlong)(p->size());
    assert(byte_offset < p_size, err_msg("Unsafe access: offset " INT64_FORMAT " > object's size " INT64_FORMAT, byte_offset, p_size));
  }
#endif
  if (sizeof(char*) == sizeof(jint))    // (this constant folds!)
    return (address)p + (jint) byte_offset;
  else
    return (address)p +        byte_offset;
}


들 어 오 는 대상 의 주 소 를 알려 주 고 되 돌아 오 는 형식의 오프셋 을 알려 주 는 것 입 니 다. 그리고 c++ 포인터 와 오프셋 을 통 해 해당 하 는 주소 의 초기 위 치 를 되 돌려 주 고 유형 (포인터 유형 확인) 에 따라 대응 하 는 형식의 값 으로 되 돌려 줍 니 다. 주소 + 오프셋 = 대응 하 는 데이터 의 주소 입 니 다.
자바 가 들 어 오 는 fieldOffset 이 어떤 값 인지 확인 해 보 겠 습 니 다.
package com.jinrong.zaxiang.refect;

import java.lang.reflect.Field;

/**
 * @author wocan23
 * @create 2019/9/24   5:56
 */
public class RefectTest {

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        User user = new User();
        user.setAge(2);
        user.setName("zss");
        user.setFlagStudent(true);

        Class aClass = user.getClass();
        Field flagStudentField = aClass.getDeclaredField("flagStudent");
        flagStudentField.setAccessible(true);
        boolean aBoolean = flagStudentField.getBoolean(user);


        Field nameField = aClass.getDeclaredField("name");
        nameField.setAccessible(true);
        Object o = nameField.get(user);

        Field ageField = aClass.getDeclaredField("age");
        ageField.setAccessible(true);
        Object o1 = ageField.getInt(user);

    }
}
class User{
    private int age; // offset 12
    private String name; // offset 20
    private boolean flagStudent; // offset 16

    public boolean isFlagStudent() {
        return flagStudent;
    }

    public void setFlagStudent(boolean flagStudent) {
        this.flagStudent = flagStudent;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}


정지점 에서 offset 이 16 인 것 을 발 견 했 습 니 다. name 과 age 의 offset 이 얼마나 되 는 지 20 과 12 로 순 서 를 바 꾸 었 습 니 다. 오프셋 은 변 하지 않 았 습 니 다.
변 수 를 하나 더 추가 합 니 다.
class User{
    private String name; // offset 20
    private int age; // offset 12
    private boolean flagStudent; // offset 16
    private boolean flagMale; // offset 17

새로 추 가 된 것 만 바 뀌 었 을 뿐 실제 바이트 코드 의 순서 와 성명 순서 가 다르다 는 것 을 발견 했다.
또 멤버 들 은 12 부터 64 비트 자바 가상 머 신 에 압축 포인터 개념 (대응 가상 머 신 옵션 - XX: + UseCompressedOops, 기본 오픈) 을 도입 해 64 비트 의 자바 대상 지침 을 32 비트 로 압축 했다.이렇게 되면 대상 헤드 의 유형 포인터 도 32 비트 로 압축 되 어 대상 헤드 크기 를 16 바이트 에서 12 바이트 로 낮 출 수 있다.

좋은 웹페이지 즐겨찾기