摘抄自<<Android 进阶解密>>一书
Java是通过实现不同平台上的虚拟机,然后即时翻译javac生成的中间代码来做到跨平台的。
跨平台的工作被虚拟机开发人员来解决了
分为两大部分:分别是编译时环境与运行时环境,当一个文件经过Java编译后会生成Class文件,这个文件交给虚拟机处理。Java虚拟机与Java语言没有必然联系,虚拟机只与二进制文件:Class文件有关,因此只要任何语言编译成Class文件,就可以被Java虚拟机识别并执行
生成的Class文件格式不会依赖于特定的硬件和操作系统,每一个Class文件都对应唯一的类与接口定义信息,但是类或者接口并不一定定义在文件中,比如类与接口可以通过类加载器来直接生成。
类生命周期分为加载、链接(验证、准备与解析)、初始化、使用与卸载
Class文件加载阶段(不是类加载)主要做了三件事:
java.lang.Class对象,作为方法区这个类的各种数据访问入口Java与Android类加载器都遵循双亲委托机制
Java虚拟机有两种类加载器:系统加载器与自定义加载器
系统加载器包括:
Bootstrap ClassLoader引导类加载器,Java虚拟机启动就是通过引导类加载器创建一个初始类来完成的,由于类加载器是使用平台相关底层C/C++语言实现,不能被Java代码访问到,但是我们可以查询某个类是否被引导类加载器加载过Extensions ClassLoader扩展类加载器:用于加载Java扩展类,提供除了系统类之外的额外功能
Application ClassLoader 应用程序类加载器又称为系统类加载器,因为这个;类加载器可以通过ClassLoader的getSystemClassLoader方法获取到。
ClassLoaer 的加载机制是一种特别聪明的方式,双亲委托机制,在这种机制下,一个Class只会被加载一次。
所谓双亲委托机制就是首先判断该类是否被加载过,如果没有加载则不是自身去查找而是委托给父类加载器进行查找,这样依次进行递归,直到委托给最顶层的Bootstrap ClassLoader,如果Bootstrap ClassLoader找到了该class就会直接返回,如果没有找到,则会继续向下查找,如果还没有找到最好交由自身去查找。Bootstrap ClassLoader是c++代码实现的加载器,Java中无法访问。
ClassLoader父子关系并不是使用继承来实现的,而是使用组合来实现代码的复用。
双亲委托机制优点:
ClassString类来代替系统的String类,会造成安全隐患,采用双亲委托机制会使系统的Sting在虚拟机启动时候就加载,无法自定义string类,除非修改类加载器搜索算法。还有一点,只有两个类名一致并且被同一个类加载器加载的类,Java虚拟机才会认为是同一个类,否则不是。BootClassLoader
Android系统启动时会使用该类加载器来加载常用类,与SDK的Bootstrap classLoader不同,它并不是C++代码实现,而是Java代码实现。它是ClassLoader内部类,继承自ClassLoader。是一个单例类,访问修饰符是默认的,只有在同一个包中才可以访问,因此应用程序中无法直接调用。BootClassLoader是在Zygote进程的Zygote入口方法中被创建,用于加载preloaded-classes文件中存有的预加载类。DexClassLoader
它可以加载dex文件以及包含dex的压缩文件(apk与jar文件),不管加载那个文件,最终都要加载dex文件。它继承自BaseDexClassLoader,方法都在BaseDexClassLoader中实现的PathClassLoader
Android系统使用PathClassLoader来加载系统类和应用程序的类。也继承自BaseDexClassLoader,都在BaseDexClassLoader中实现的,它无法定义解压dex文件的存储路径,因此PathClassLoader用来加载已经安装apk的dex文件。安装的apk的dex文件会存储在/data/dalvik-cache中。PathClassLoader是在SystemServer进程中采用工厂模式创建的。虚拟机接收到一条new指令时,首先会检查这个指定的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表类是否已被加载、链接与初始化过
Java堆内存是规整,即所有用过的内存放在一边,而空闲内存放在另一边。分配内存时将位于中间的指针指示器向空闲的内存移动一段与对象大小相等的距离,这样来完成分配内存操作Java堆内存不是规整的,则需要由虚拟机维护一个列表来记录那些内存时可用的,这样分配时,从列表查询足够大的内存分配给对象,并在分配后更新列表CAS算法并配上失败重试的方式保证更新操作的原子性Java堆中预先分配一小块内存,这块内存称为本地线程分配缓冲,线程需要分配内存时,就在对应的缓冲上分配内存将分配到的内存,除了对象头外都初始化为零值
将对象的所属类、对象的hashcode和对象的GC分代年龄等数据存储在对象的对象头中
init方法进行初始化初始化成员变量、调用类的构造方法,这样一个对象就被创建出来了
分为三个区域:
Mark Word与元数据指针,Mark Word用于存储对象运行时的数据,比如HashCode、锁状态标志、Gc分代年龄、线程持有的锁等,而元数据指针用于指向方法区中的目标类的元数据,通过元数据可以确定对象的具体类型。static成员进行初始化当对象被创建,并分配给变量赋值时,状态就切换到了应用阶段。这个阶段对象至少具有一种引用强,软,弱,虚引用
在程序中找不到对象的任何强引用,比如程序执行已经超出了对象的作用域。在不可见阶段,对象仍可能被特殊的强引用GC Roots持有着,比如对象被本地方法栈中的JNI引用或被运行中的线程引用等
在程序中找不到对象的任何强引用,并且垃圾收集器发现对象不可达
垃圾收集器已经发现对象不可达,并且垃圾收集器准备好对该对象的内存空间重新分配,如果对象重写finalize方法,就会调用该方法
在对象执行完finalize方法仍然处于不可达,或者对象没有重写finalize方法,则对象进入终结阶段,并等待垃圾收集器回收对象空间**
当垃圾收集器对对象的内存空间进行回收或者再分配时,这个对象就会彻底消失
被标记不可达的对象会被垃圾收集器立即回收
不会,被标记对象会进入收集阶段,如果该对象重写了finalize方法,就调用,否则进入终结阶段,这时才会被垃圾收集器回收。