本文会根据学习和理解不断持续迭代更新
JVM
JVM是Java的核心和基础,是Java编译器和OS间的虚拟处理器,是一种用软件方法实现的抽象计算机,这个抽象计算机可以执行Java字节码程序(.class)
总的来说JVM是一种抽象定义,针对不同平台有不同平台对这个抽象的JVM实现(类似抽象类和实现类)
JVM体系结构下内存空间
JVM运行时的内存空间主要分为线程私有的数据区和共享数据区
其中线程私有的数据区包括程序计数器,虚拟机栈和本地方法区
共享数据区包括Java堆,方法区以及方法区包含的常量池
程序计数器: 记录正在执行的虚拟机字节码地址
虚拟机栈: 方法执行的内存区,每个方法执行的时候会在虚拟机栈创建栈帧
本地方法区:虚拟机Native方法执行的内存区域
Java堆: 对象分配的内存区域
方法区:存放类信息,常量,静态变量,编译器编译后的代码数据
常量池:存放编译器生成的各种字面量和符号引用
判断对象是否能被回收的方法引用计数法(Deprecated): 引用对象计数+1,引用失效计数-1,计数器为0表示对象可回收,缺点是很难解决对象相互引用的情况
可达性分析法: 通过”GC Root”对象作为起点向下搜索,搜索走过的路径称为”引用链”,当一个对象到GC Root不存在任何引用链,称为对象到GC Root不可达,表示该对象可回收,GC Root大体包括虚拟机栈中的引用对象,本地方法栈中Navtive方法引用对象,方法区静态属性的引用对象,常量池的引用对象等
GC算法标记清除法
标记需要回收的对象,回收时回收所有被标记的对象
缺点是标记和清除过程效率都不高,并且因为可能会产生大量内存碎片,在分配大对象的时候可能因为不存在连续内存而需要提前再触发一次GC
复制算法
把内存分为两部分,每次只使用其中一块,满了以后将存活对象全部复制到另外一边,余下的直接清除
缺点是可用内存空间降低为原来的一半
如果对象存活率高,可能会进行频繁的复制操作导致效率降低
标记整理法
过程跟标记清除法类似,但是标记后不是直接清除,而是把存货对象按照内存地址依次排列,然后把排列对象末端地址后的对象一次性回收,这样既不会产生碎片,也不用把内存分割
分代收集法
根据对象存活周期把堆分为新生区和老年区,根据各个年代的特点使用不同算法
新生代分为eden区和survior区,其中survior区分为两部分(survior from/survior to),默认比例是8:1:1,对象朝生夕死,使用复制算法
老年代存活率高,使用标记清除或者标记整理法
新生代老年代一般比例是1:2
新产生的对象会先到eden区,eden区满了以后再使用survior from,如果survior from也满了准备触发minorGC
此时会进行一次判断,判断老年区最大连续内存空间是否大于新生区所有对象总和,如果大于,直接触发minorGC
如果小于,判断是否打开HandlePromotionFailure,如果没有打开,触发FullGC
如果HandlePromotionFailure打开,判断老年区最大连续内存空间是否大于历代晋升老年区对象的平均大小,如果大于,尝试MinorGC,如果小于,执行FullGC.
如果老年代空间不足,比如创建大对象,Eden放不下,就会直接保存在老年代,如果老年代也放不下,直接FullGC
长期存活对象会晋升,存入老年代,JVM会为对象定义一个年龄计数器,如果对象在eden出生,每经历一次minorGC后依然存活计数器+1,年龄达到一定程度(默认15),就会晋升到老年代
如果晋升到老年代发现老年代没有足够内存存放晋升对象,触发FullGC
System.gc()也会触发FullGC
一图流:
Android虚拟机
Android虚拟机主要包括Dalvik(4.4默认及4.4以前)和ART(4.4非默认及4.4以后)
Android虚拟机不是JVM,因为它没有按照JVM规范来实现,而是针对移动平台计算能力和内存空间的特点做了特定的优化
和JVM的区别
架构的不同
JVM基于栈,需要去栈中读写数据,所需要指令更多,因此不太适合移动设备
DVM基于寄存器,执行速度更快,但是寄存器只有65535个,每个32位,总共只有256k
字节码不同
JVM中,Java Source->.class->.jar,JVM通过classloader加载class和jar并执行
DVM会将class转换为.dex,DVM通过.dex读取指令和数据,Java Source->.class->.dex
DVM允许在有限的内存中同时运行多个进程
每个Android应用运行在一个DVM实例中,每个DVM实例都运行在一个独立的进程空间,防止虚拟机崩溃导致所有程序关闭
DVM由Zygote进程创建和初始化
Android进程由Zygote创建,每次系统需要创建一个应用进程的时候,Zygote会fork自身,快速创建并初始化一个DVM实例,用于应用程序运行
DVM运行时堆内存(具体内容引用自这里)
DVM运行时堆内存主要由两个Space和多个辅助数据结构组成,两个Space分别是Zygote Space(Zygote Heap)和Allocation Space(Active Heap).Zygote Space用来管理Zygote进程启动过程预加载和创建的各种对象,Zygote Space不会触发GC,所有进程共享该区域,比如系统资源.
Allocation Space是Zygote进程fork第一个子进程之前创建的,是一个私有进程
除此以外,Zygote还包含以下数据结构:
Card Table: 用于DVM Concurrent GC,第一次进行垃圾标记后,记录垃圾信息
Heap Bitmap: 有两个Heap Bitmap,一个记录上次GC存活的对象,另一个记录本次GC存活的对象
Mark Stack: DVM运行时堆使用标记清除法进行GC,Mark Stack用于GC在标记阶段用于遍历存货对象
ART虚拟机
ART和Dalvik区别
DVM中应用每次运行都会通过JIT转换为机器码,因此启动比较慢,ART在安装过程会进行一次AOT(ahead of time),将字节码预编译成机器码并存储,运行效率大大提高(空间换时间),并且会节省一些能耗
ART运行时堆内存
ART的GC类型有很多种,主要是标记清除法和标记整理法,ART运行时堆内存根据不同GC类型也有不同划分,如果采用的是标记清除法,堆内存主要由4个Space(Zygote Space, Allocation Space, Image Space和Large Object Space)和多个辅助数据结构组成, 其中Zygote Space和Allocation Space和DVM一样,Image Space用来存放预加载类,Large Object Space用来存放大对象(默认12k).其中Zygote Space和Allocation Space是进程共享的.