虚拟机
模拟某种计算机体系结构,执行特定指令集的软件。包括进程虚拟机和系统虚拟机(VMWare)
公有设计,私有实现
《Java虚拟机规范》(JVMS)定义了概念模型,不约束虚拟机的具体实现。
Java虚拟机运行时数据区
在《Java虚拟机规范》中定义了若干种程序运行期间会使用到的存储不同类型数据的区域。
有一些区域是全局共享的,随着虚拟机启动而创建,随着虚拟机退出而销毁。有一些区域是线程私有的,随着线程开始和结束而创建和销毁。
是所有Java虚拟机共同的内存区域概念模型。
既然虚拟机作为一个虚拟的计算机, 来执行我们的程序, 那么在执行的过程中, 必然要有地方存放我们的代码(class文件); 在执行的过程中, 总会创建很多对象, 必须有地方存放这些对象; 在执行的过程中, 还需要保存一些执行的状态, 比如, 将要执行哪个方法, 当前方法执行完成之后, 要返回到哪个方法等信息, 所以, 必须有一个地方来保持执行的状态。 上面的描述中, “地方”指的当然就是内存区域, 程序运行起来之后, 就是一个动态的过程, 必须合理的划分内存区域, 来存放各种数据。 所以, 在本文中, 将会详细介绍JVM的运行时数据区。
运行时数据区的划分
分为程序计数器、Java堆、Java虚拟机栈、本地方法栈、方法区
这是一块较小的内存空间,它的作用可以看作是当前线程所执行的字节码的行号指示器。
如果当前线程正在执行Java方法,指数器记录的是正在执行的虚拟机字节码指令的地址;如果是native方法,指数器为空。
这是唯一一个没有OOM规定的区域。
虚拟机栈描述的是Java方法执行的内存模型:每个方法(不包括native方法)在执行时都会创建一个栈帧,用于存储局部变量,操作数栈,动态链接,方法出口等信息。每一个方法从调用到执行结束的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。
在Java虚拟机规范中,对该区域内存规定了两种异常状况:
本地方法栈则为虚拟机使用到的Native方法提供内存空间。有些虚拟机的实现直接把本地方法栈和虚拟机栈合二为一,比如非常典型的Sun HotSpot虚拟机。
和虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。
栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区的虚拟机栈(Virtual Machine Stack)的栈元素。
它被用于存储数据和部分过程结果的数据结构,同时也被用来处理动态链接、方法返回值和异常分派。
在编译代码的时候,栈帧中需要多大的局部变量表,多深的操作数栈都已经完全确定了,并且写入到了方法表的Code属性中,因此一个栈帧需要分配多少内存,不会受到程序运行期变量数据的影响,而仅仅取决于具体虚拟机的实现。
对于执行引擎来讲,活动线程中,只有虚拟机栈顶的栈帧才是有效的,称为当前栈帧(Current Stack Frame),这个栈帧所关联的方法称为当前方法(Current Method)。执行引用所运行的所有字节码指令都只针对当前栈帧进行操作。
一个完整的栈帧包括:局部变量表、操作数栈、动态连接信息、方法正常完成和异常完成信息
举例
Java堆、栈的关联过程:两种方式
方式一的优点是访问速度更快,因为方式二需要两次指针定位。方式二的优点是,Java堆中的对象经常会变化(GC),此时只需要修改句柄池中的引用即可,比较方便。目前第一种方式比较流行。
1. 引用计数算法
给对象添加一个引用计数器,每当有一个地方引用它时,引用计数器的值加1;引用失效时,值减1;计数器为0的对象可以被回收。
缺点:循环引用
OC语言的解决方式:强引用和弱引用。弱引用不会增加引用计数。
2. 可达性分析算法
由于引用计数算法的缺陷,Java虚拟机通常使用这种判定方法。
通过一系列称为“GC Root”的对象作为起始点,从这些起点向下搜索,搜索路线称为“引用链”,如果一个对象到GC Roots没有任何引用链,则判定不可用。
Java中GC Roots:
1. 标记-清除算法
首先标记出所有需要回收的对象,标记完成后统一清除。
缺陷:产生大量不连续的内存碎片
2. 复制算法
将可用内存分为相等的两块,每次只使用其中一块,当一块内存用完了,就将还活着的对象复制到另一块内存中,再把这块内存中的所有对象清除掉。
缺陷:将内存缩小了一半、复制开销
改进:不一定需要分为大小相等的两块,可以适当提高可用内存比例。
3. 标记-整理算法
标记过程与“标记-清理算法”一样,然后让所有存活对象向一端移动,最后清理掉边界以外的对象。
缺陷:系统停顿时间更长(移动消耗)
4. 分代算法
当今商用Java虚拟机共同采用的算法。
根据对象存活周期的不同将内存划分为几块。一般把Java堆分为新生代和老年代,然后在不同内存区域采用不同的垃圾收集算法。比如新生代,会产生大量垃圾对象,适合采用复制算法(需要复制的对象较少),而老年代可以采用标记-清理或者标记-整理算法。
GC Roots枚举
安全点和安全区
Android系统内存分配和回收
APP内存限制机制
切换应用时后台APP清理机制
监控内存的方法
数据结构优化
对象复用
避免内存泄漏
内存泄漏会导致可用内存越来越少,频繁造成GC
当APP申请的内存空间大于系统为APP分配的空间时会出现OOM
参考文章