Android 에서 사용자 정의 ClassLoader 시간 소모 문제 에 대한 추적

머리말
Android 의 클래스 로 더 에는 BootClassLoader,URLClassLoader 가 있 습 니 다.
PathClassLoader,DexClassLoader,BaseDexClassLoader 등 은 자바.lang.ClassLoader 에서 최종 계승 되 었 습 니 다.
최근 수박 동 영상 클 라 이언 트 의 콜 드 시작 속 도 를 최적화 할 때 플러그 인 ClassLoader 주입 을 닫 은 상태 에서 시작 속도 가 300 ms 정도 올 라 간 것 을 발 견 했 습 니 다.그러나 수박 은 시작 단계 에서 플러그 인 을 사용 하지 않 았 습 니 다.이렇게 큰 시간 은 어떻게 되 었 습 니까?다음은 더 이상 할 말 이 없 으 니 상세 한 소 개 를 해 봅 시다.
원인 을 알아맞히다
우선 수박 이 현재 사용 하고 있 는 플러그 인 ClassLoader 가 어떻게 주입 되 었 는 지 살 펴 보 겠 습 니 다.대체적으로 코드 는 다음 과 같 습 니 다.

코드 는 크게 PathClassLoader 와 BootClassLoader 사이 에 Delegate ClassLoader 를 삽입 하고 Delegate ClassLoader 의 findClass 방법 에서 플러그 인 Class 의 로 딩 을 실행 한 다 는 뜻 입 니 다.
검증 을 편리 하 게 하기 위해 간단 한 테스트 데 모 를 쓰 고 클래스 를 불 러 오 는 데 걸 리 는 시간 을 테스트 합 니 다.

샤 오미 맥 스 2,안 드 로 이 드 7.1.1 기종 을 예 로 들 면 Delegate ClassLoader 에 주입 하지 않 고 주입 하지 않 는 데 걸 리 는 시간 을 테스트 합 니 다.
주입 하지 않 음:60μs
주입 후:472μs
차이 가 많 지 않 아 8 배 느 렸 고 몇 가지 핸드폰 의 기본 데 이 터 를 테스트 했 지만 4.x 핸드폰 에 있 는 이 두 가지 상황 에서 시간 차 이 는 매우 적다.
DelegateClassLoader.findClass 는 시간 이 걸 립 니까?
부모 위탁 메커니즘 때문에 숙주 에 있 는 모든 종류의 로 딩 은 Delegate ClassLoader.findClass 에 갑 니 다.그러나 Delegate ClassLoader 에 숙주 류 가 존재 하지 않 기 때문에 찾 을 수 없 기 때문에 숙주 류 의 로 딩 은 쓸모없는 findClass 방법 을 한 번 더 호출 합 니 다.한 번 의 findClass 호출 은 이렇게 큰 시간 을 가 져 올 수 있 습 니까?그래서 Delegate ClassLoader 코드 를 다음 과 같이 간소화 합 니 다.

이렇게 해서 Delegate ClassLoader 에 서 는 플러그 인 류 로 딩 논 리 를 하지 않 고 부모 ClassLoader 로 이동 하 는 loadClass 작업 만 했 습 니 다.
결 과 는 여전히 8 배 안팎 의 시간 차 였 다.
자바 방법 호출 시간 이 걸 립 니까?
위의 방안 은 사용자 정의 ClassLoader 를 주입 하지 않 는 것 보다 Delegate ClassLoader.loadclass 방법 을 한 번 더 호출 했 을 뿐 이론 적 으로 이렇게 큰 시간 이 걸 릴 수 없습니다.자바 방법 을 한 번 더 호출 하면 Delegate ClassLoader.loadClass 가 8 배의 시간 차 이 를 보인다 면 두 번 더 호출 하면 16 배의 차이 가 아 닐 까?
그래서 Delegate ClassLoader 두 개 를 주입 하려 고 합 니 다.다음 과 같 습 니 다.

그러나 결 과 는 8 배 안팎 의 시간 차 이 였 고 16 배가 아니 었 다.이 는 방법 호출 에 따 른 성능 손실 이 아니 었 다.
사용자 정의 ClassLoader 시간 이 걸 립 니까?
그래서 시스템 이 PathClassLoader 에 어떤 최적화 가 있 을 것 같 습 니까?그리고 빈 PathClassLoader 를 만들어 PathClassLoader 와 BootClassLoader 사이 에 주입 합 니 다.다음 과 같 습 니 다.

신기 한 8 배 소모 시간 차이 없어!그래서 정말 시스템 이 PathClassLoader 에 최 적 화 된 거 예요?
이 의문 을 가지 고 ClassLoader 의 소스 코드 를 살 펴 보 겠 습 니 다.Android 7.1.1 소스 코드 를 예 로 들 면.
ClassLoader#loadClass
먼저 소스 를 살 펴 보 겠 습 니 다.ClassLoader 의 loadClass 소스 코드 는 다음 과 같 습 니 다.

대체로 findLoadedClass 를 호출 하여 불 러 온 class 에서 찾 은 다음 부모 ClassLoader 의 loadClass 를 호출 하여 찾 습 니 다.찾 지 못 하면 마지막 으로 자신의 findClass 를 불 러 옵 니 다.
JVM 에서 클래스 를 처음 불 러 올 때 이전에 불 러 오지 않 았 을 것 입 니 다.따라서 findLoadedClass 는 null 로 돌아 가 야 합 니 다.BootClassLoader 에는 시스템 클래스 만 있 기 때문에 숙주 클래스 의 로 딩 은 PathClassLoader\#findClass 로 불 러 온 것 입 니 다.
PathClassLoader#findClass
그러면 PathClassLoader\#findClass 의 소스 코드 를 살 펴 보 겠 습 니 다.호출 체인 은 대체적으로 다음 과 같 습 니 다.

시스템 이 ClassLoader 에 최적화 되 어 있다 면 호출 체인 에서 ClassLoader 에 유용 한 곳 에 중점 을 두 면 됩 니 다.
전체 findClass 프로 세 스에 서 ClassLoader 에 사용 되 는 곳 은 많 지 않 습 니 다.ClassLinker::RegisterDex File 과 ClassLinker::SetupClass 에서 만 사용 되 었 습 니 다.
ClassLinker::RegisterDexFile 에서 ClassLoader 에 대한 classtable 의 간단 한 조작;
  • ClassLinker:SetupClass 에 서 는 불 러 온 class 에 ClassLoader 를 설정 합 니 다.두 가지 방법 은 ClassLoader 의 조작 에 최적화 되 지 않 고 이론 적 으로 성능 손실 을 초래 하지 않 습 니 다.여 기 는 코드 를 붙 이지 않 습 니 다
  • findClass 에 최적화 되 어 있 지 않 으 면 ClassLoader\#findLoadedClass 에 있 습 니까?
    ClassLoader#findLoadedClass
    ClassLoader\#findLoadedClass 의 소스 코드 를 살 펴 보 겠 습 니 다.호출 체인 은 대체적으로 다음 과 같 습 니 다.

    우선 c 층 호출 의 첫 번 째 방법 을 살 펴 보 겠 습 니 다.VMClassLoaderfindLoadedClass :

    여 기 는 주로 두 개의 지점 이 있 습 니 다.첫 번 째 지점,12 번 째 줄 은 ClassLinker\#Lookupclass 를 호출 합 니 다.

    대체로 ClassLoader 에서 ClassTable 을 찾 은 다음 ClassTable\#Lookup 을 호출 한 다 는 뜻 입 니 다.이 ClassTable 에는 불 러 온 클래스 와 시작 할 때 app image 에서 불 러 온 클래스 가 저 장 됩 니 다.(app image 는 컴 파일 된'핫 코드'를 기록 하고 시작 할 때 캐 시 에 한꺼번에 불 러 옵 니 다.Tinker 블 로 그 를 참고 하 십시오.)클래스 가 처음으로 불 러 오고 app image 에 없 으 면 null 로 돌아 갑 니 다.
    이렇게 하면 두 번 째 지점(25 번 째 줄)ClassLinker::FindClassInPath ClassLoader 로 갑 니 다.

    여 기 는 주로 두 부분 으로 나 뉜 다.
  • 첫 번 째 부분:37 줄 부터 자바 층 의 PathClassLoader 에서 DexPathList 를 반사 한 다음 에 DexPathList 에서 dexElements 를 얻 은 다음 에 dexElements 를 옮 겨 다 니 며 모든 Element 에서 dexFile 을 얻 은 다음 에 DexFile 에서 mCookie 를 얻 은 다음 에 mCookie 를 통 해 c 층 의 DexFile 을 얻 습 니 다.마지막 으로 c 층 Dex File\#FindClassDef 를 호출 하여 실제 실행 클래스 의 로 딩 을 합 니 다.전체 절 차 는 c 층 에서 자바 층 의 PathClassLoader\#findClass 논 리 를 한 번 걸 었 습 니 다
  • 두 번 째 부분:재 귀적 인 방식 으로 BootClassLoader 부터 PathClassLoader 까지 차례대로 FindClassInPathClassLoader 를 호출 합 니 다.class 를 찾 을 때 까지 자바 층 ClassLoader 의 부모 가 class 를 불 러 오 는 체 제 를 c 층 에서 한 번 하 는 것 과 같 습 니 다.이것 은 ART 에서 class 로 딩 에 대한 최적화 입 니 다.그러나 Dalvik 에 서 는 이 논리 가 없습니다./dalvik/native/javalangVMClassLoader.cpp 를 참고 하 십시오
  • 포인트 가 왔 습 니 다!위 에서 반사 기 를 사용 하여 PathClassLoader 의 필드 를 만 들 었 기 때문에 이 메커니즘 에 문제 가 없 도록 이 안에 검증 을 추가 하 였 습 니 다.

    ClassLoader 체인 에 모 르 는 ClassLoader 가 존재 한다 면,ClassLoader 의 클래스 가 BootClassLoader 와 PathClassLoader 가 아니라면,로 딩 클래스 가 실패 했다 고 생각 합 니 다.물론 여기 서 불 러 오 는 데 실패 하면 최종 클래스 로 딩 결과 에 영향 을 주지 않 습 니 다.자바 층 find Loaded Class 가 실패 하면 find Class 에 도착 하기 때 문 입 니 다.
    결론.
    Android ART 에서 기본 적 인 ClassLoader 메커니즘 은 ClassLoader\#findLoadedClass 에서 JVM 의 findLoadedClass 와 findClass 두 가지 일 을 모두 했 습 니 다.그러나 classloader 체인 에 사용자 정의 ClassLoader 가 존재 한다 면 이 메커니즘 은 효력 을 잃 고 JVM 의 기본 ClassLoader 메커니즘 으로 되 돌아 갑 니 다.
    위의 문제 로 돌아 갑 니 다.저 희 는 ClassLoader 를 사용자 정의 하여 Art 의 ClassLoader 체 제 는 JVM 의 기본 클래스 로 딩 체제 로 되 돌 아 왔 습 니 다.JVM 의 기본 클래스 로 딩 체 제 는 여러 번 JNI 호출 이 존재 합 니 다.JNI 호출 자체 의 성능 은 직접 방법 으로 호출 하 는 것 보다 몇 배 높 습 니 다.여 기 는 더 이상 상세 하 게 전개 되 지 않 습 니 다.그래서 앞에서 말 한 몇 배의 시간 차 이 를 설명 할 수 있다.
    레 퍼 런 스
    Android N 혼합 컴 파일 및 열 패 치 영향 분석
    총결산
    이상 은 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 참고 학습 가치 가 있 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.

    좋은 웹페이지 즐겨찾기