类加载子系统
:加载的类信息存放于方法区
当中,方法区
当中可能还包括运行时常量池信息,包括字符串字面量和数字常量。Java堆
:堆在虚拟机启动的时候创建,它是Java程序最主要的内存工做区域,几乎全部的Java对象实例都存放在Java堆当中,堆空间是全部线程共享的,这是一块与Java应用密切相关的内存空间。Java栈
:是每个线程所私有的内存空间,Java
栈是线程执行密切相关的,线程执行的基本行为是函数间的调用,每次函数调用的数据都是Java栈传递的。不管是JVM仍是计算机的操做系统,在函数调用时都须要用到栈,在Intellij idea的Debug界面中,咱们就能够看到栈信息:其中的一项,称为一个栈帧
,一个栈帧中,至少包含局部变量表、操做数栈、帧数据区几个部分。当一个函数返回时,栈帧会被从Java栈中弹出。(返回包括return和异常)html
本地方法栈
:本地指的是Native
,JVM容许Java直接调用本地方法。PC
:Program Counter,即程序计数器
,和CPU里面的PC表示的是一个意思,即Java代码当前走到的位置。一个Java线程是在执行一个方法,这个正在执行的方法称为当前方法,若是当前方法不是本地方法,那么就指向正在执行的指令
,若是执行的是本地方法,那么PC寄存器的值就是undefined。执行引擎
:是java虚拟机的最核心组件之一,它负责执行虚拟机的字节码,现代虚拟机为了提升执行效率,会使用即时编译技术将方法编译成机器码后再执行。其实,咱们能够发现,灰色的区域(Java栈、本地方法栈、程序计数器)是线程私有的,而其余的空间则是线程共用的。java
这三者都是线程私有的。算法
PC寄存器
用来存储指向下一条指令的地址,也即将要执行的指令代码,由执行引擎读取下一条的指令。它是一块很小的内存空间,几乎能够忽略不计,也是运行速度最快的存储区域。在JVM规范中每一个线程都有本身的程序计数器,是线程私有的,生命周期同线程周期保持一致。缓存
PC寄存器是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等等最基础的工做都要依赖它完成。markdown
它是惟一一个在Java虚拟机规范中,没有规定任何OOM状况的区域。也没有GC的区域。ide
每一次函数的调用,都会在调用栈上维护一个独立的栈帧,栈帧通常包括:函数
局部变量表:是一组变量值的存储空间,用来存放方法参数和局部变量,虚拟机经过索引定位的方式使用局部变量表。储存包括八种基本数据类型
、对象的引用地址
,参与方法的调用和返回。工具
操做数栈:存放方法调用时的实参,即操做数(在概念模型中各个栈帧都是相互独立的,可是实际实现时,都会使两个独立的栈帧一部分重叠,下面的部分操做数栈与上面的局部变量表重叠在一块区域,这样在方法调用时能够共用一部分的数据。)。oop
动态连接:(略,其实就是指向运行时常量池的方法引用)。性能
方法返回地址:方法的返回分红两个状况:
不管是异常仍是正常,退出方法的时候,都会退出到最开始的调用处。
- 若是是正常退出,那么调用者的PC 寄存器便可做为返回地址。
- 若是是异常退出,那么就参照异常处理表来肯定。
本地方法本质上时依赖于实现的,虚拟机实现的设计者们能够自由地决定使用怎样的机制来让Java程序调用本地方法。
一个JVM只存在一个堆内存
,堆是Java内存管理的核心区域。它在JVM虚拟机建立时即被建立,大小就已经肯定了,是JVM所管理的最大的一块内存空间。堆在物理上不要求其连续,可是在逻辑上要求是连续的。全部的线程共享Java堆,能够在堆中划分线程私有的缓冲区
。
区分为 : 新生代 + 老年代 + 永久代
区分为:新生代 + 老年代 + 元空间
1.内存划分和JVM的一项重要的功能:GC(垃圾回收)有很是大的关系,不一样的对象,不一样区的对象GC回收的频率不一样,主要参照GC算法。
2. 咱们建立的对象(至关多的一部分都是临时对象),存放在新生代,使用相对短的时间便可被回收的,若是没有被回收的就说明,这个对象的使用频率相对来讲高,屡次GC都不能回收。而永久代则用于存储class、运行时常量池、字段、方法、代码、JIT代码等。
3. 永久代和方法区是什么关系? 永久代是方法区的一种实现,咱们规定类加载后生成的Class对象放在方法区当中,这是JVM的规范的一部分,可是并无说:必须开辟一个内存,名称为方法区。实际上咱们选择在永久代中构建方法区,来存储这些对象。也不是全部虚拟机都有永久代的,咱们使用的HotSpot的JVM中的虚拟机只存在于JDK7以前,JDK8及以后被MetaSpace(元空间)所替代了。
4. GC算法和堆内存的划分是什么关系? GC算法主要是根据一些列的规则判断该对象是否还有存活的价值,及时释放无关对象能够节省空间。而堆内存的划分则和GC的频率有关系,各自划份内的GC算法也不相同。
- 新生代:GC的频率相对较高。
- 老年代:GC的频率相对较低。
- 永久代:主要是GC一些卸载的类和废弃的常量。
字节码读入JVM后,机器并不能读懂字节码,只有JVM自身能读懂它,因此JVM必须作一件事情,将JVM翻译成相关平台的机器码(01),具体的过程在这章不作展开。这里讲讲在Android中使用的JIT技术和AOT技术,前者是动态编译,然后者是静态编译。
早期的Android(Android 2.1及以前)应用程序执行Java代码其实是解释器将每一个Java指令 翻译成 等价的几条微处理器指令,而且,根据转译后的指令一条一条地按照次序进行执行。这样的执行方式须要在执行前进行翻译,运行是可想而知的低效。
Android2.2 以后 引入了JIT技术,该技术简单来讲就是:每遇到一个Class文件,JIT就会对这个类进行编译,生成至关精简的二进制码,花费少量的编译时间来换取后续执行的速率。JIT的提出对性能提高是比较大的,可是确实十分有限的。由于某些Java文件是极少执行的,编译他们的时间有可能远远长于翻译器翻译他们的时间。总体下来,花费的时间并无减小。
后来,基于JIT的经验,又提出了动态编译器,动态地预判那些须要编译,哪些须要翻译,因此动态编译器是既包含了编译器、又包含了解释器的。
Java文件进行编译后,都是字节码文件(ByteCode),不管是传统的JVM,或者是Dalvik、ART虚拟机,只是各类虚拟机下的ByteCode的组成方式有所差别。其中,JVM对应的运行文件是.Class文件,而Dalvik对应的则是.dex文件,DVM专门对移动操做系统的特性进行了优化,而且基于寄存器进行设计。指令集具备很大的不一样。(基于寄存器进行设计的内容可参考RISC:即精简指令系统计算机,指令精简且关键,更多地依赖寄存器来获取更快的速度和更低的功耗)
Ahead-Of-Time技术。
ART模式(Android runtime)做为一个可选功能在Android 4.4的开发者选项中首次出现,做为Davlik的备选项,ART在安装时就会进行编译动做,编译的文件也再也不是一个字节码,而是具体的可执行文件,能够运行在更为底层的硬件上,这样在运行时,就省下了预编译和翻译的时间。该可执行文件本质上是一个ELF文件(是一种用于二进制文件、可执行文件、目标代码、共享库、核心转储格式文件),在Android中,咱们没法找到显式存在的OAT文件,其实OAT文件仍然是以.odex做为后缀的,经过file命令或者UE打开能够看到ELF头部。
ART设计是考虑兼容性的,即便是早期编译的Android Project中的Dex文件也能够在运行ART模式的Android设备上使用。这是经过dex2oat作到的,dalvik下的dex、odex文件都可以经过这个工具转化为oat文件,而且odex文件将比dex文件编译的更快。
Odex文件即Optimize Dex,对dex文件的优化,最直观的好处:decodex在系统第一次开机时,须要提取全部APK中的Dex文件,而Odex优化是提早提取出来了,这样运行速度和开机速度都有提升。其次Odex优化后,APK中能够没有Dex文件,而为Odex在Apk包中有一份Dex文件,在/data/dalvik-cache下还有提取出来的一份,浪费存储空间。必定程度上保护了硬件厂商本身的APK,由于APK中只有资源文件,反汇编并无意义。具体的代码都在Odex之中了。
也正是由于AOT技术,在搭载Android 5.0系统的机器上,初始化后或者是初次开机启动很是的慢,由于系统会提取全部App的dex字节码,优化而且拷贝到/data/dalvik-cache缓存目录中,所以,第一次启动耗时会明显更高。
在Android 7.0 中,JIT被从新启用,采用AOT/JIT 混合编译的策略,特色是: