DALVIK JIT 入口分析

Dalvik的JIT编译流程 & ART的dex2oat流程 一.Dalvik的JIT编译流程 Dalvik JIT是一个tracing JIT(也叫trace-based JIT),其中间表示分为两种,MIR与LIR。MIR与LIR节点各自形成链表,分别被组织在BasicBlock与CompilationUnit中。 编译流程是:  0、创建CompilationUnit对象(一个trace对应一个CompilationUnit)来存放一次编译中需要 阅读详情

本文主要讲解dalvik jit的框架,至于更深层次的内容,例如编译的方法、寄存器的分配等问题,将在以后讨论。请先阅读 dalvik VM的解释器分析


Dalvik JIT的简要介绍

Dalvik JustInTime技术,是在DVM解释器工作过程中,识别和分析出热点代码,然后将其编译为机器码,然后运行的过程。

jit的基本原理比较简单,我用下面的伪代码来解释其模型:对于一个特定的指令入口dalvik PC


 
  1.  
  2. if (dPC被执行次数 > 阀值 ) {
  3. jit_addr = GetJitAddr (dPC ) ;
  4. if (jit_addr != NULL ) {
  5. jit_addr ( ) ; //进入jit代码
  6. } else {
  7. 收集 dPC 所指代码到一组JitTraceRun中 ;
  8. AddCompileWorkQueue (jitTraceRuns ) ;
  9. }
  10. }

CompileWorkQueue有一个编译线程,负责编译,编译过程是


 
  1. while ( 1 ) {
  2. JitRuns jitRuns = GetFromWorkQueue ( ) ;
  3. addr = compile (jitRuns ) ;
  4. SetJitAddr (addr ) ;
  5. }

JIT总体可以分成3个重要的课题:

  1. JIT的入口、Trace和代码管理;内容包括1.1 如何从解释代码进入到JIT过程;1.2 DVM如何收集将要被JIT的代码;1.3 DVM是如何分配和保存代码数据的;

  2. JIT的编译过程,JIT如何将如何编译

  3. JIT与解释模式的相互调用。

因为内容比较多,因此,我将文章分为3篇,本篇文章主要介绍JIT的入口,JIT的主要运行框架;其余两篇文章,将介绍JIT的编译与JIT编译后的代码调用。

 Dalik代码中,哪些数据和重要的函数是和JIT有关的?通过Dalvik源代码,我们很容易看到,所有被WITH_JIT宏包围的代码,都是和JIT相关的。

Thread中的JIT数据结构


 
  1. struct Thread {
  2. ....
  3. #ifdef WITH_JIT
  4. struct JitToInterpEntries jitToInterpEntries ; //jit代码跳转到解释模式的桥接口,配合jit编译出代码使用的
  5. /*
  6.   * Whether the current top VM frame is in the interpreter or JIT cache:
  7.   * NULL : in the interpreter
  8.   * non-NULL: entry address of the JIT'ed code (the actual value doesn't
  9.   * matter)
  10.   */
  11. void * inJitCodeCache ; //当前jit的入口地址代码。用于解释到jit的跳转
  12. unsigned char * pJitProfTable ; //这是一个以davik PC为key的hash表,记录对应代码的执行次数
  13. int jitThreshold ;
  14. const void * jitResumeNPC ; // Translation return point 这些是用来jit暂停再重启要保存的数据
  15. const u4 * jitResumeNSP ; // Native SP at return point
  16. const u2 * jitResumeDPC ; // Dalvik inst following single-step
  17. JitState jitState ; //用于jit trace的状态信息保存
  18. int icRechainCount ;
  19. const void * pProfileCountdown ;
  20. const ClassObject * callsiteClass ; //invoke子对象时必须的数据
  21. const Method * methodToCall ;
  22. #endif
  23. ....
  24. }

比较重要的数据是pJitProfTable,它是一个hash表,以dalvik PC为key,通过dvmJitHash函数算出索引。索引所指是一个unsgind char数据,一开始,数据被写入255,然后直至递减至0,表示可以开始代码的Trace了。

gDvmJit以及dvmJitGetTraceAddr

gDvmJit是一个维护Jit信息的全局对象。其中最重要的是维护jit代码入口的hash表。gDvmJit.pJitEntryTable是JitEntry的hash表。这是一个桶hash对象。通过dvmJitGetTraceAddr和dvmJitGetTraceAddrThread等函数获取到dalvik pc对应的代码地址。

JIT的入口

我们知道,DVM解释器的入口是dvmMterpStdRun ,在这个函数入口处,有一个common_updateProfile的标签(汇编函数),这个标签下的代码,就是解释模式进入到JIT世界的入口。

dalvik大部分都运行在arm设备上,因此对应的源码是vm/mterp/out/InterpAsm-armv7-a-neon.S.

common_updateProfile代码分析

为了方便大家阅读,我将对应的汇编码翻译成对等的C++代码


 
  1.  
  2. common_updateProfile :
  3. /* 对等C++代码*/
  4. int idx = dvmJitHash (rPC ) ;
  5. if ( --thread - >pJitProfTable [idx ] ! = 0 ) {
  6. goto_op_code (ip ) ; //这是跳转到对应的解释器入口处。
  7. }
  8.  
  9. thread - >pJitProfTable [idx ] = thread - >jitThreshold ;
  10.  
  11. EXPORT_PC ( ) //save dPC到StackArea中,这是解释器要保存的事情
  12. if ( (thread - >inJitCodeCache = dvmJitGetTraceAddrThread (dPC, thread ) ) ! = NULL ) {
  13. thread - >inJitCodeCache ( ) ; //调用jit入口
  14. }
  15. //其他情况,调用common_selectTrace,后续在详细讨论

其中common_selectTrace是负责开始trace代码的。

这里面比较难以理解的部分,是还要reset counter,似乎看起来是延长了进入jit的次数。

DVM进入common_updateProfile的点

DVM有很多地方都会进入到common_updateProfile中,这些包括:

  • dvmMterpStdRun入口处;

  • invoke函数,准备好参数后

  • IF指令处

  • GOTO指令

  • SWITCH指令

  • JIT指令转移到解释模式后,也有机会再次进入到JIT代码。这种情况下,jit会考虑将两者连在一起,在jit中直接跳转。

这些说明,JIT是以一段连续的没有跳转的代码为单位进行编译的。

Trace代码

Trace代码的功能,是收集将要被编译的代码,然后将结果放入到编译队列中。

Davik完成这个工作,是一边继续执行解释代码,一边收集要被编译的代码的。这个过程是通过一个巧妙的方法实现的。

为了完成这个工作,Dalvik用一个简单的状态机来实现过程控制。其中Thread.jitState是用来保存状态的。分别有5个状态:

  1. kJitNone : 初始状态,此时没有开始jit

  2. kJitTSelectRequest/kJitTSelectRequestHot:请求代码收集。是在common_updateProfile被调用后发现对应code还没有

  3. kJitTSelect : 开始收集代码的阶段,当遇到分支跳转代码、return代码、throw代码后,就会转入Select

  4. kJitTSelectEnd : 完成收集代码的阶段,会调用dvmCompilerWorkEnqueue,将字节码加入队列

  5. kJitDone : 完成编译,并调用dvmJitSetCodeAddr,将编译后代码的地址保存起来。

除了jitState之外,Thread还有一种subMode,来表示当前解释器运行在那种状态下。subMode由枚举值ExecutionSubModes定义。ExecutionSubModes有很多,但是,我只列出重要的几个:

  1. kSubModeNormal : 正常状态,没有活动的子模式

  2. kSubModeJitTraceBuild : Trace状态,现在以Trace状态运行。

kSubModeJitTraceBuild是我们关注的子模式。那么,kSubModeJitTraceBuild这种子模式究竟有什么作用呢?与Trace代码和jitState的状态变化有什么关系?

下面我们回答这个问题。

SubMode的秘密

进入kSubModeJitTraceBuild子模式

我们知道,当调用完common_updateProfile后,如果尚未获取jit代码,就会调用common_selectTrace。那么,common_selectTrace要做什么呢?为了简化代码,我还是将其翻译成对应的C++代码:对等的C++代码:


 
  1. if (thread - >subMode & kSubModeJitTraceBuild ! = 0 ) {
  2. thread - >jitState = new_jitState ; //(即kJitTSelectRequest);
  3. EXPORT_PC ( ) ;
  4. SAVE_PC_FP_TO_SELF ( ) ; //这两行是保存PC和FP信息的,与trace关系不大,忽略
  5. dvmJitCheckTraceRequest (self ) ; //(1)
  6. }
  7. FETCH_INST ( ) ; //取下个指令
  8. int opcode = GetInstOpcode (inst ) ;
  9. goto thread - >curHandlerTable + opcode * 64 ; //(2)

(1) dvmJitCheckTraceRequest指令,就是要将jitState由kJitNone转到kJitTSelectRequest状态的函数。另外,该函数将调用dvmEnableSubMode函数,完成解释器模式的转变;

(2) goto 语句是伪代码,表示现在跳转到thread→curHandlerTable + opcode * 64的指令处执行。

实际上,这是dalvik一个非常精巧的一个手段。

首先,curHandlerTable的值,是可以通过dvmEnableSubMode改变的,也就是说,当我们需要开始收集要编译指令时,需要进入kSubModeJitTraceBuild子模式,而模式的切换,是通过改变curHandlerTable的值实现的;

其次,curHandlerTable是什么呢? 从代码中,我们可以看到:


 
  1. newValue. ctl. curHandlerTable = (newValue. ctl. breakFlags )
  2. ? thread - >altHandlerTable : thread - >mainHandlerTable ;

当进入kSubModeJitTranceBuild时,newValue.ctl.BreakFlags标记就会被设置上,因此,这种情况下,curHandlerTable的值是altHandlerTable。而平常情况下,则是mainHandlerTable。

mainHandlerTable的巧妙设计

mainHandlerTable其实就是dvmAsmInstructionStart。dvmAsmInstructionStart这个符号,是定义在InterpAsm-armv7-a-neon.S中,是这样定义的:


 
  1. . global dvmAsmInstructionStart
  2. . type dvmAsmInstructionStart, %function
  3. dvmAsmInstructionStart = . L_OP_NOP
  4. . text
  5. ....

从这里,我们可以看出两个信息:

  1. 所有的dalvik字节码指令,都有对应的一段汇编码实现,而且每段汇编码的命名是.L_OP_XXX,XXX表示opcode的名字;

  2. dvmAsmInstructionStart是这组汇编码的首地址。也就是说,curHandlerTable其实是一组与dalvik字节码对应的,用于解释字节码的汇编码的数组。因此,curHandlerTable + opcode *64就是找到对应的.L_OP_opcode 代码地址,然后调用这段汇编码。

那么,为什么要乘以64呢? 因为每个.L_OP_XXX标签前面,都有一个 .align 64的伪汇编指令。他告诉汇编器,这些标签必须以64字节为对齐。所以,所有的.L_OP_XXX代码,全部被安排在以dvmAsmInstructionStart为开始的连续空间内,并以64字节对齐。

换句话说,每个OPCODE,它的编码必须是连续的,且每个汇编码的实现,不能超出64字节。如果超出了怎么办?那就用一个branch指令,跳转到一个不受限制的空间内执行。

那么,altHandlerTable又是谁呢?

altHandlerTable的秘密

altHandlerTable,对应的代码入口是dvmAsmAltInstructionStart,他是一组.L_ALT_OP_XXX入口的标签。与mainHandlerTable一样,它也是一组数组。

那么,.L_ALT_OP_XXX与.L_OP_XXX有什么不同呢?

我们以.L_ALT_OP_MOVE为例。

首先,我们看看其源代码是如何编写的:


 
  1. /* ------------------------------ */
  2. . balign 64
  3. . L_ALT_OP_MOVE : /* 0x01 */
  4. /* File: armv5te/alt_stub.S */
  5. /*
  6.  * Inter-instruction transfer stub. Call out to dvmCheckBefore to handle
  7.  * any interesting requests and then jump to the real instruction
  8.  * handler. Note that the call to dvmCheckBefore is done as a tail call.
  9.  * rIBASE updates won't be seen until a refresh, and we can tell we have a
  10.  * stale rIBASE if breakFlags==0. Always refresh rIBASE here, and then
  11.  * bail to the real handler if breakFlags==0.
  12.  */
  13. ldrb r3, [rSELF, #offThread_breakFlags]
  14. adrl lr, dvmAsmInstructionStart + ( 1 * 64 ) //(1)
  15. ldr rIBASE, [rSELF, #offThread_curHandlerTable]
  16. cmp r3, #0
  17. bxeq lr //(2) @ nothing to do - jump to real handler
  18. EXPORT_PC ( )
  19. mov r0, rPC @ arg0
  20. mov r1, rFP @ arg1
  21. mov r2, rSELF @ arg2
  22. b dvmCheckBefore //(3) @ (dPC,dFP,self) tail call

这是一个很精巧的设计。首先我把它翻译成对等的C++代码:


 
  1. . L_ALT_OP_MOVE :
  2. real_code_addr = dvmAsmInstrunctionstart + ( 1 * 64 ) ; //其实就是.L_OP_MOVE的地址,.L_OP_MOVE是第二条指令的实现
  3. if (thread - >breakFlags ! = 0 ) {
  4. dvmCheckBefore (dPC, dFP, self ) ;
  5. }
  6. goto real_code_addr ; //即goto .L_OP_MOVE

这里的关键是使用了lr寄存器保存.L_OP_MOVE的地址。

当代码执行(2)时,如果breakFlags == 0,就直接跳转到.L_OP_MOVE了。如果不等于0,就会调用dvmCheckBefore函数。

但是,一般情况下,我们都使用bl (branch link)来调用函数,而不是B。 bl的作用是,当调用函数时,将bl下条指令的地址,存放在lr寄存器内。

当函数返回时,使用 bx lr这样的指令,或者直接将lr的值赋值给pc,就可以返回了。

但是,这里(3) 使用了b,而没有使用bl,这样,lr的值就一直是 .L_OP_MOVE的地址。当dvmCheckBefore函数返回后,就会直接调用.L_OP_MOVE了。

由此可以看出,altHandlerTable其实就是在调用每条指令前,先调用dvmCheckBefore函数。

如何获取Trace代码

dvmCheckBefore函数会完成很多任务,其中也包括TraceBuild。因为我们只关心Trace部分,因此我们只找和Trace相关的代码。Trace代码的任务,主要由dvmCheckJit完成。

JitTraceRun

JitTraceRun是保存代码结构的。所有要编译的代码信息,就保存在这里。首先看该结构的定义:


 
  1. /*
  2.  * Element of a Jit trace description. If the isCode bit is set, it describes
  3.  * a contiguous sequence of Dalvik byte codes.
  4.  */
  5. struct JitCodeDesc {
  6. unsigned numInsts : 8 ; // Number of Byte codes in run
  7. unsigned runEnd : 1 ; // Run ends with last byte code
  8. JitHint hint : 7 ; // Hint to apply to final code of run
  9. u2 startOffset ; // Starting offset for trace run
  10. } ;
  11.  
  12. struct JitTraceRun {
  13. union {
  14. JitCodeDesc frag ;
  15. void * meta ;
  16. } info ;
  17. u4 isCode : 1 ;
  18. u4 unused : 31 ;
  19. } ;

JitTraceRun不是单独存在,必须以一个数组的方式存在。JitTraceRun.isCode表示是否是代码。当是代码时,JitCodeDesc frag结构被使用。

如果遇到 isCode==1 && info.frag.runEnd == 1就表示结束。

当isCode为1时,frag变量的每个成员含义是:

  • numInsts: 当前被trace的指令个数

  • startOffset: 当前指令开始偏移。这个偏移是相对于指令所在method的偏移。偏移从0开始,表示第一条指令。

  • hint : 忽略,本阶段不考虑

  • runEnd: 必须为0,表示有效的地址。

当isCode == 0时,meta数据被使用,这个定义很宽泛,可以是任意值。但是这里,它们被用作标记一个函数调用(invoke指令)。

要标记一个函数调用,必须用4个连续的JitTranceRun,其中前3个是meta数据,最后一个是isCode。为了方便,我们用表格列出如下

索引 isCode meta/frag 数据 说明
0 False meta = thisClass callee 函数的this指针
1 False meta = thisClass→classLoader thisClass的loader
2 False meta = calleeMethod 被调用函数对象
3 True frag.startOffset = resultPC 函数返回后,要继续运行的dalvik PC偏移

当Trace完成后,我们就会得到这样一组JitTraceRun的数组,从而让我们能够完成对代码的进一步处理。

dvmJitCheck

dvmJitCheck是在每次指令完成后调用的。他根据jitState作出不同的动作。JitTraceRun数组是放在Thread.trace对象中的。为了逻辑清晰,我用伪代码给出,不再引用他的源码了。有兴趣的同学可以自己查看。

当jitState为kJitTSelect状态时,表示正在进行收集指令工作


 
  1. //这段伪代码将字节码执行的逻辑也放进来,让大家能够清楚宏观上的结构
  2. u2 * lastPC == 0 ;
  3. currentTraceRun = 0 ;
  4. while ( 1 ) { //解释器的最外层循环
  5. inst = GetCurrentInst (curPC ) ; //获取当前指令
  6. if (lastPC == 0 ) {
  7. lastPC = curPC ; //第一次收集
  8. thread - >trace [currentTraceRun ]. info. frag. numInsts = 1 ;
  9. thread - >trace [currentTraceRun ]. info. frag. startOffset = lastPC - method - >insns ; //取得地址偏移
  10. thread - >trace [currentTraceRun ]. isCode = true ;
  11. } else {
  12. if (inst is Invoke ) //函数调用
  13. {
  14. //把 callee的this, classloader和method对象放入
  15. insertClassMethodInfo ( ) ;
  16. currentTraceRun + = 3 ;
  17. //把返回地址放入
  18. insertMoveResult ( ) ;
  19. currentTraceRun ++ ;
  20. } else if (inst is Branch or Goto or Throw or return ) {
  21. insertLastPC (lastPC ) ; //把最后的pc放入到trace中
  22. jitState = kJitTSelectEnd ; //结束
  23. break ;
  24. } else {
  25. thread - >trace [currentTraceRun ]. info. frag. numInsts ++ ; //增加指令个数
  26. }
  27. }
  28. lastPC = curPC ;
  29. /* 解释执行inst */
  30. ....
  31. }

通过这个简单的模型,我们可以看出,当遇到分支(if/else, switch/case) goto指令,throw和return指令后,trace就会结束。

另外,dalvik会尽量的将连续的代码放在一个run内,保证内存使用精简。

几乎所有用到的数据都要保存在Thread对象中。因为Thread对象是这个线程内运行的全局变量。

当进入kJitTSelectEnd后,就要调用dvmCompilerWorkEnqueue函数了。jit将所有的run对象拷贝一份到对象JitTraceDescription中,然后放入队列中,然编译线程继续运行。

编译及code代码存储

编译线程

在dalvik中,执行编译任务的是线程compilerThreadStart。这个线程由函数dvmCompilerStartup来创建。真正执行编译任务的是 dvmCompilerDoWork→dvmCompileTrace。

dvmCompileTrace是真正执行编译任务的函数。

编译完成后,得到的新地址会被保存到gDvmJit中。通过dvmJitSetCodeAddr调用实现。

详细的编译过程将另开辟一个文章来说明。因为编译过程实在是太复杂了。

编译出的代码内存如何分配

我曾经以为jit代码会在整个系统内共享,但是实际上,每个进程有自己的内存空间。函数dvmCompilerSetupCodeCache完成内存开辟的任务。内存首先会开辟一个gDvmJit.codeCacheSize大小(在arm下,是1500 * 1024大小)。并命名为dalvik-jit-code-cache.我们可以通过查看/proc//maps中找对应的段,就知道了。

当需要jit代码后,每次就从这个缓冲区获取一块。因为代码总是只分配不回收,所以分配方法非常简单。


Android JIT和AOT JIT 与 AOT 的共存:自 Android 7.0 起,混合模式结合两者优势,JIT 负责运行时优化,AOT 负责持久化编译,兼顾效率与存储。编译优化核心开发阶段:通过 Gradle 配置、缓存机制和工具链优化缩短构建时间。运行时:依赖 ART 的混合编译策略和编译器优化提升执行性能。 阅读详情

相关推荐

Shell数学计算(算术运算,加减乘除运算)

声明: 本篇博客的学习途径主要为以下网站和课堂讲解,发博客目的仅为学习使用,在该博客的基础上做了一定程序的简略和修改。 参考博客 : 原文链接:http://c.biancheng.net/shell/ Shell数学计算Shell数学计算数学计算命令对整数进行数学运算: (()) / let / $[] /exprShell (())Shell let命令bc命令:一款数学计算器在 Shell 中使用 bc 计算器 Shell数学计算 默认情况下,Shell 不会直接进行算术运算,而是把+两边的数据(数

AKUANer的博客 2万+

WPF编译时提示"xxx不包含适合于入口点的静态 Main方法xxx"

WPF编译时提示"xxx不包含适合于入口点的静态 Main方法xxx" 生成的时候一直报"xxx不包含适合于入口点的静态 Main 方法xxx"

编程之家 3786

factory机制-重载(一)

这里写自定义目录标题1.SystemVerilog对重载的支持2.对约束的重载3.factory 机制式的重载4.重载的方式和种类5.factory机制的调试 1.SystemVerilog对重载的支持 SystemVerilog是一种面向对象的语言。面向对象语言都有一大特征:重载。当在父类中定义一个函数/任务时,如果将其设置为virtual类型,那么就可以在子类中重载这个函数或任务。 重载的最大优势是使得一个子类的指针以父类的类型传递时,其表现的行为依然是子类的行为。 2.对约束的重载 在测试一个DUT时

sinat_41774721的博客 2960

无法在 DLL“kernel32”中找到名为“XXX”的入口点

1,出现这个问题,完全属于自己的误操作 2,因需要将Private改为Public的时候,默认的是整个项目 3,改了几处替换,取消掉后,之前   [DllImport("kernel32")]内读取配置文件的的函数名由XXPrivateXXX变成了XXPublicXXX 4,因此始终提示无法在 DLL“kernel32”中找到名为“XXX”的入口点 5,另外还要注意大小写不能错

dsyzxty的专栏 1万+

WPF窗口定位技巧:从基础到高级的三种实现方法

本文详细解析WPF窗口定位的三种核心方法:屏幕居中(CenterScreen)、父窗口居中(CenterOwner)和手动定位(Manual)。通过对比其适用场景、实现代码与注意事项,帮助开发者根据实际需求选择最佳方案,从而提升WPF应用程序的用户体验与专业度。

weixin_29224309的博客 272

手把手教你用GNU Radio和PlutoSDR搭建FM收音机(附完整流程图)

本文提供了一份详细的实践指南,教你如何使用GNU Radio和PlutoSDR搭建一个功能完整的FM收音机。从软硬件环境准备、核心流程图构建,到运行调试与效果优化,手把手带你完成从射频信号接收、下变频、FM解调到音频输出的全过程,是入门软件定义无线电(SDR)的绝佳实践项目。

weixin_29189377的博客 749

解决xxx.exe-无法找到入口,无法定位程序输入点......于动态链接库

解决xxx.exe-无法找到入口,无法定位程序输入点......于动态链接库。

weixin_43564060的博客 1万+

抖音视频数据抓取

最近经常有人问我抓取抖音视频的数据,问我一些很小白的问题,算法给他也不会用。 还是先抓包,获取需要的接口和参数,相关的算法在其它文章里面有描述。 搜索个视频看看 查看抓的包信息: 可以看到,这里是post请求...

2501_91048859的博客 3097

文件分析——分析/linuxrc文件(即分析busybox的源码)

以下内容源于朱有鹏嵌入式课程的学习与整理,如有侵权请告知删除。

主要记录嵌入式学习与开发过程。 6865

EMC诊断技术及电磁兼容理论设计

本文深入探讨了EMC诊断技术,重点在于滤波篇,详细讲解了EMI滤波器的设计、选型、测试及应用注意事项。内容涵盖噪声限值法规、滤波器的法规依据、插入损耗测试以及滤波电容的高频特性。同时,还阐述了噪声源的近场与远场特性,如何快速定位和抑制噪声。通过实例分析,提供了滤波器件和辐射噪声抑制器件的选型策略。

让你爱上电路设计 2162

win10锁屏或登录时会自动弹出触摸按键的问题解决办法

本篇文章主要讲解win10锁屏或登录时会自动弹出触摸按键的问题解决方式。日期:2023年5月17日作者:任聪聪。

任聪聪的博客 1万+

Android里的Dalvik、ART、JIT、AOT

Android里的Dalvik、ART、JIT、AOT有什么关系? JIT,Just-in-time,即时编译,边运行边编译; AOT,Ahead Of Time,提前编译,指运行前编译。 区别:这两种编译方式的主要区别在于是否在“运行时”进行编译 优劣 JIT优点:   可以根据当前硬件情况实时编译生成最优机器指令(ps. AOT也可以做到,在用户使用时使用字节码根据机器情况在做一次编译)   可以根据当前程序的运行情况生成最优的机器指令序列   当程序需要支持动态链接时,只能使用JIT .

qq_25883735的博客 446

一次携程酒店 phantom-token 逆向实战复盘(附python纯算)

摘要 本文介绍了通过分析携程酒店列表页面的数据接口,使用Python实现抓取的方法。主要内容包括: 目标站点为携程酒店列表页,通过开发者工具发现关键接口fetchHotelList 将浏览器请求转换为Python代码,包含请求头(headers)、Cookies和POST数据 提供完整的请求示例代码,包含日期参数、城市ID等必要信息 特别声明仅供学习交流使用,禁止商业用途 展示了如何获取酒店列表数据,包括排序筛选等参数设置 该方法可用于研究酒店数据采集,但需要注意遵守网站使用条款。

自成背后的博客 3022

Dalvik jit分析

iltgcl的专栏 992

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

【Android】一篇文章带你了解 Android的 JIT 、AOT、Dalvik、ART ,不再傻傻分不清

为了解释以上概念,我们得从Android 编译过程说起。 我们使用Java开发android,在编译打包APK文件时,会经过以下流程: Java编译器将应用中所有Java文件编译为class文件,dx工具将应用编译输出的类文件转换为Dalvik字节码,即dex文件之后经过签名、对齐等操作变为APK文件。 科普了概念之后,要开始聊一聊编译打包的前世今生。 在apk运行...

左耳的专栏 6418

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

浅析dalvik虚拟机JIT技术的实现

Android 2.2的虚拟机dalvik使用了JIT技术,使其运行速度快了5倍。dalvik解释并执行程序,JIT技术主要是对多次运行的代码进行编译,当再次调用时使用编译之后的机器码,而不是每次都解释,以节约时间。5倍是测试程序测出的值,并不是说程序运行速度也能达到5倍,这是因为测试程序有很多的重复调用和循环,而一般程序主要是顺序执行的,而且它是一边运行,一边编译,一开始的时候提速不多,所以真正运行程序速度提高不是特别明显。

谢彦的技术博客 4897

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

DAVLIK JIT编译实现 (上)

为什么要讲dalvik jit的编译实现呢?首先,为什么是dalvik?虽然dalvik已经在L版本后不再出现在Android系统中,但是ART是从dalvik继承而来,ART中有很多Dalvik的痕迹。在很多方面,ART都继承了dalvik的思想,如果不深入了解dalvik,对ART的了解也是不全面的。另外,dalvik相对与ART,逻辑结构更加简单和清晰,很多方面都可以看作ART的雏形和蓝本,

doon的专栏 2426

[FAQ10640]如何调试在dalvik-jit中抛出的SIGSEGV类型的NE

FAQs 73 of 126 如何抓取ramdump 如何在fastboot 里面添加新的命令 内容 [DESCRIPTION] 理论上来说,dalvik-JIT相关的代码逻辑是比较健壮的,我们很少有遇到dalvik-JIT相关的代码逻辑异常;但是在某些测试条件下,还是可能遇到dalvik-j

兰宝的专栏 467

Android里的Dalvik、ART、JIT、AOT有什么关系?

JIT,Just-in-time,即时编译,边运行边编译; AOT,Ahead Of Time,提前编译,指运行前编译。 区别  这两种编译方式的主要区别在于是否在“运行时”进行编译 优劣JIT优点:  可以根据当前硬件情况实时编译生成最优机器指令(ps. AOT也可以做到,在用户使用时使用字节码根据机器情况在做一次编译)  可以根据当前程序的运行情况生成最优的机器指令序列  当程...

weixin_30700977的博客 129

android odex版本调试_android 基础-Dalvik ,ART,JIT,AOT,Dex,Odex

Dalvik 和 ARTDalvik:Dalvik 虚拟机,android 5.0 以前所使用的虚拟机,可执行文件为 dex 格式,基于寄存器的虚拟机(jvm 基于堆栈)。通过 dx 工具将 .class 文件转换为 .dex 格式。ART:android rutime,android 5.0 以后使用的虚拟机。关于这个虚拟机的点,希望了解的关注点在于虚拟机这块。ios 和 android对比,a...

weixin_39929254的博客 743

卷二 Dalvik与Android源码分析 第五章 Interpreter与JIT 5.5 Interpreter的切换 图书版试读--请勿转发

作者 crosskernel@gmail.com 本节分析dalvik虚拟机是通过何种方式选择解释器的。  查找系统属性里解释器执行模式 int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) {    … //取出系统属性“dalvik.vm.execution-mode”     pr

crosskernel的专栏 2100

Dalvik和ART运行时环境的区别

Dalvik和ART运行时环境的区别在此,我并没有打算深入的学习Dalvik和ART两种方式的实现原理,只是想知道他俩的区别。之前,也是零零散散的看过,并没有总结成文字。在此,总结下。Dalvik以下内容来自wikipedia Dalvik虚拟机,是Google等厂商合作开发的Android移动设备平台的核心组成部分之一。它可以支持已转换为.dex(即“Dalvik Executable”)格式

ZhangGeng's Blog 2万+

AOT和JIT区别

AOT,JIT是什么? JIT,即Just-in-time,动态(即时)编译,边运行边编译;AOT,Ahead Of Time,指运行前编译,是两种程序的编译方式 区别 这两种编译方式的主要区别在于是否在“运行时”进行编译 优劣 JIT优点: 可以根据当前硬件情况实时编译生成最优机器指令(ps. AOT也可以做到,在用户使用是使用字节码根据机器情况在做一次编译) 可以根据当前程序的运行情况生成最优的机器指令序列 当程序需要支持动态链接时,只能使用JIT 可以根据进程中内存的实际情况调整代码,使内存能够更充分

Er_fengV的博客 2888
上一篇: android N编译遇到的问题
下一篇: DAVLIK JIT编译实现 (上)
漂流的代码
博客等级 码龄23年 188粉丝 99原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值