## 七月头的总结
一、关于cli小工具的开发
一直想着要试试自己的独立工程能力,之前跟着大佬或者是其他的什么课程项目作业混,虽然说我感觉学到了不少的东西,但是似乎一直没有机会自己搞。~~~~ ~哎,对于我这种内心深处要强的人来说,实在是太憋屈了。
reinhardt-Stadler/wds-mod-manager: 一个用于简化wds系列兵棋游戏图包替换工作的组件
连接就在上面了,反正如果哪位点进来的读者也刚好玩WDS兵棋的可以去看看,顺便留下点改进建议。
至于说开发新的体验~ ~ ~ 后面在说吧。主要是vibe coding出来的,有几个部分我还要在研究一下 哎,我太懒了。得闲在说
二、关于安卓系统文件的一点点学习
阴差阳错的,也是前两个月我才偶然的发现(做项目想用皇室战争的音乐恶搞一下,结果翻了大半个网站,没找着心仪的,索性动了个破天荒的歪心——自己搞破解,反正也是迟早要了解的。)不看不知道,原来所谓apk格式,就是zip换个名字。最后音乐还是没找到想要的,我也不知道官方把他的awv文件放哪了。
后面又看了clash的内核架构。感觉还蛮有意思的,原来我每天搞了搞去的应用里面长这样。什么是 Clash? | Clash
然后想起之前拿到的一个移动应用题目,因为是apk格式,当时被困了很久。真是柳暗花明。结果哪个题没去继续研究,反而看了篇关于安卓内核相关的一些东西:Android逆向笔记 —— DEX 文件格式解析 - 知乎 怎么说呢~ 还是感觉发现了块新大陆,感觉自己在朝着理解程序底层原理的道路上又进了一步。(其实人类在封装各种底层上,走的距离比我想象中更远)对于java的应用也并非原先学习时了解的那么简单
因为学过java,索性变抛开科特林的语言。一个java的原码,在保存后由IDE生成一个.class格式文件,按照以前的知识,这里是直接交给我们cpu上面一层的JVM来间接的解析我们的字节码,这不同于c的直接二进制机器码,还是相当于架构在上面跑的。也就相当于JVM可以理解成一个专门用于解析.class类型字节码的软件。当然了,这是在标准的学习状态下我们了解到的知识。
然而,在安卓的环境条件下java的编译原理发生了变化。
|–> 安卓的历史简要
安卓的解释器,或者说是虚拟机是经历了两个阶段
第一个阶段是采用的Dalvik虚拟机,这是是Google等厂商合作开发的Android移动设备平台的核心组成部分之一。第二个阶段是ART虚拟机,亦即Android Runtime缩写。是一种在Android操作系统上的运行环境,在Android 5.0及后续Android版本中作为正式的运行时库取代了以往的Dalvik虚拟机。
JVM与Dalvik
实际上,能产生.class文件的并不只有java语言, JVM 系的语言:Java、Kotlin、Scala、Groovy、Clojure 等以及JPython都可以生成class。毕竟,.class的本质还是字节码,也就是说,在真正跑机器码之前,上述语言都是需要生成字节码的语言,可以视为产生了.class文件。

在一般的计算机中,字节码即.class就是程序送到cpu运行前最后的形态了,后面JIT那里一般人碰不到。不过,由于java的JVM有点类似于在cpu的上层自己定义了一套命令——为了java一次编译,处处调用的哲学。所以在JVM的栈结构中,产生了大量的“栈”用来存储运行过程中跑出来的各种数据,然后又需要反复的遍历栈来判断哪些栈中的数据已经过期需要清理,这一来二去的就使得java每次运行都需要占用大量的内存。(这里涉及到java和c的具体命令设计的区别,简单讲就是java因为有栈,他的字节码都是无局部变量的——结果、过程、参数、方法等都存在栈中,而c是允许在命令中携带参数,所以可以用更小的内存来实现同样的指令)~ ~ emm其实就是关于变量到底如何参与我的运算这里,两种语言的逻辑不一样
无论如何,上述的java编译特性都是我们的手机不可接受的,高内存占用,高频率扫描。手机上的每一个小的应用程序,本质上讲就是一个程序/进程。这对于我们手机上动辄数个聊天软件,各种信息平台的环境而言,如果还是采用原来的JVM/JIT模式,后果将是不可想象的。根本性的来讲,问题无非就是内存限制,电量限制
因之,必须寻找更好的解决方案。
理解Java字节码与机器码的差异及其工作原理_java jvm 字节码 机器码-CSDN博客

JVM执行的是.class文件、Dalvik和ART执行的.dex文件。
class文件有很多冗余信息,dex文件会做去冗余信息的优化。
JVM是基于堆栈,Dalvik虚拟机是基于寄存器。——这实际上回到了c的编译模式下
JVM是基于栈的指令,一个指令字占用一个字节,会很紧凑,比如一个方法体的执行,需要经过一连串的指令来完成。JVM 是基于操作数栈的计算模型——算术运算的操作数必须先从局部变量表 load 到操作数栈,计算完再 store 回去;而 Dalvik/寄存器模型可以直接在指令中指定源/目标寄存器,省去了 load/store 的往返。
Dalvik是基于虚拟寄存器的指令集(比起JVM在指令结构上更贴近真实的CPU指令集,经JIT/AOT处理后成为arm指令),需要指定源地址和目标地址(理解为变量声明),因此需要更多的指令空间。Dalvik的某些指令需要占用两个字节。
基于栈和基于寄存器的指令集,各有优势,一般来说,执行同样的功能,基于栈需要更多的指令(主要是load和store),而基于寄存器需要更多的指令空间。
这里借用其他up主的图来说明:
JVM指令集

Dalvik指令集

虚拟机之Jvm、dalvik、art联系和区别_dalvik和jvm-CSDN博客
Dalvik充分的利用了Linux进程的管理的特性,Android手机上,每启动一个应用就独立对应一个虚拟机,多个应用同时运行,就有多个虚拟机,都是独立的进程互不影响。
安卓下的Dalvik与ART
.dex格式是专为Dalvik设计的一种优化格式,适合内存和处理器速度有限的系统。
JIT(just in time)编译器,dalvik虚拟机使用JIT编译,每次apk应用运行时实时将一部分dex编译成机器码,然后被执行。
Dalvik采用的JIT编译技术,ART采用的AOT编译技术,AOT(Ahead of time),ART同时也改善了性能、垃圾回收、应用程序出错以及性能分析。
简单理解一下,AOT就是提前做好的预制菜,cpu饿了就直接端上去跑。JIT是临时点菜,只提前准备一小点基础字节码。无非就是JIT的CPU因为要反复调用存储里的数据比较累,而AOT更占用空间一点,~~这其实跟pycache的缓存机制是一个思路
在apk程序启动过程中:
若Dalvik虚拟机,JIT通过连续不断的性能分析来优化程序代码的执行,在程序运行的过程中,Dalvik虚拟机需要不断将dex字节码编译成机器码。
若ART虚拟机,ART引入了AOT预编译技术,在应用程序安装的过程中,AOT就将所有的dex字节码编译成了机器码,应用程序运行过程中,不需要实时的做编译工作,直接调用即可。
因此,ART极大的提升了应用程序的运行效率,同时也减少了手机的电量消耗,提供了移动设备的续航能力,在垃圾回收机制上,也有很大的提升。
为了保证向下兼容,ART使用了相同的Dalvik字节码文件(dex),即在应用程序目录下保留了dex文件供旧程序调用,然而.odex文件则替换成了可执行与可链接格式(ELF)可执行文件。一旦一个程序被ART的dex2oat命令编译,那么这个程序将会只通过ELF可执行文件来运行。因此,相对于Dalvik虚拟机模式,ART模式下Android应用程序的安装需要消耗更多的时间,同时也会占用更大的内部储存空间,用于储存编译后的代码,但节省了很多Dalvik虚拟机用于实时编译的时间,即运行的时候,效率会更高。
ART这种预编译模式,会造成安装耗时,在Android N实现了一个使用AOT、解释、JIT混合模式的运行环境。
关于dex文件的一点思考
其实也没有想到中间写了这么多关于java底层原理的差异,本来只是想着自己复述一遍dex的来历与cpu编译的底层,结果发现真正到自己输出的时候其实讲的很粗糙,甚至不能清晰的讲明白应该事情,一补习发现自己之前对于JVM的理解还是太狭隘了
三、末了
本来这篇博客前两天就应该发的,但是写到dex的收获那里,想着在复述一下自己在dex和java的class类间的理解,结果输出到一半感觉自己都没乱清楚java在搞什么,然后又从头补习了一遍java的编译原理,加上白天忙着打游戏,也就耽误了。