知识点干货--内存优化【2】之JVM

“神农尝百草”的故事不知道大家有没有听过,据说有很多个版本,说的挺有意思的,有兴趣的可以查一下。
神农氏本是三皇(伏羲,神农,黄帝)之一。在女娲补天之后,不知过了多长时间,在烈山的一个石洞里,出生了一个小孩。说来奇怪,在他刚出世,石洞周围自然涌现了九眼井,这九眼井里的水彼此相连,若取其中一眼之水,其它八眼皆会波动起来。这个孩子天生异相,身体是透明的,五脏六腑清晰可见,头上长有两只角,牛头人身。看到的人们都说这是天神下凡,九眼井是他带来的。于是在他长大后,大家推举他为部落首领,因为他们居住在炎热的南方,就自称炎族,称他为炎帝。有一次炎帝看见一只红色的鸟衔着一串像种子的东西,然后鸟儿把它吐了出来,炎帝走过去拾了起来,鸟儿围住他飞了三圈,又唧唧啾啾地叫了一阵飞走了。炎帝认为这是天帝派红鸟送来的食物种子,便把种子埋在土里。又用木头制成耒耜,教人们松泥土,并掘井灌溉禾苗。这年秋天,一大片禾苗成熟了。人们非常的高兴,于是大家感念炎帝的功德,都称炎帝为神农。这样周边的部落又称炎帝部落为神农部落,而称他为神农氏,即农业部落的首领。
神农氏看到人们经常得病,于是就到都广之野登建木上天帝花园取瑶草,却偶遇天帝,天帝赠给他一个神鞭,名为赭鞭,神农拿着这根神鞭从都广之野走了一路鞭了一路,然后回到了烈山。
相传神农氏为辨别各类草药,就亲自尝试,最后试到一种含有剧毒的草药(传说为断肠草),无法可解,最终牺牲了生命。人们为了纪念他的恩德和功绩,奉他为药王神,并建药王庙四时祭祀。在我国的川、鄂、陕交界传说是神农尝百草的地方,称为神农架山区。

上一篇讲了内存简介,因为本系列主要介绍的是和Android相关的内存优化,所以有必要先熟悉一下Java虚拟机–JVM,本篇就来简单讲一下JVM的构造和垃圾回收机制。

1、JVM结构图

下面这张图是JVM的结构图,主要由四个部分组成:

(1)、Class Loader子系统。

用来加载类文件,Java源代码文件(.java后缀)被Java编译器编译为字节码文件(.class后缀),然后由JVM中的类加载器加载各个类的字节码文件,加载完毕之后,交由JVM执行引擎执行。

(2)、运行时数据区(RUNTIME DATA AREA)。

执行引擎在执行过程中,JVM会用一段空间来存储程序执行期间需要用到的数据和相关信息,这段空间被称作为RUNTIME DATA AREA(运行时数据区),也就是我们常说的JVM内存。

(3)、执行引擎。

负责对字节码文件进行执行,输入的是字节码文件、处理过程是等效字节码解析过程、输出的是执行结果。Java虚拟机相当于一台虚拟的“物理机”,而物理机和虚拟机都有代码执行能力的,其区别主要是物理机的执行引擎是直接建立在处理器、硬件、指令集和操作系统层面上的。而JVM或Java虚拟机的执行引擎是程序员实现的,因此程序员可以自行制定指令集和执行引擎的结构体系,他能够执行那些不被硬件直接支持的指令集格式。

而在JVM规范中制定了虚拟机字节码执行引擎的概念模型,这个模型称之为JVM执行引擎的统一外观。在JVM实现中,一般会有两种执行方式:解释执行(通过解释器执行)和编译执行(通过即时编译器产生本地代码)。有些虚拟机只采用一种执行方式,有些则可能同时采用两种,甚至有可能包含几个不同级别的编译器执行引擎。

(4)、本地方法区。

存储Native层的接口,这些接口通过NATIVE LIBRARIES获取。JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C和C++)。

其中重点是运行时数据区(RUNTIME DATA AREA),也就是我们常说的JVM内存,而它主要由5个部分组成:

(1)、Method Area(方法区):

方法区在JVM中也是一个非常重要的区域,它与堆一样,是被线程共享的区域。在方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。被装载的class的元信息存储在Method Area中,它是线程共享的。

(2)、Heap(堆):

一个Java虚拟机实例中只存在一个堆空间,它用来存放一些对象信息,它是线程共享的。在C语言中,堆这部分空间是唯一一个程序员可以管理的内存区域。程序员可以通过malloc函数和free函数在堆上申请和释放空间。而Java中的堆是用来存储对象本身的以及数组的(当然,数组引用是存放在Java栈中的)。只不过和C语言中的不同,在Java中,程序员基本不用去关心空间释放的问题,Java的垃圾回收机制会自动进行处理。

(3)、Java栈:

Java栈也称作虚拟机栈(Java Vitual Machine Stack),也就是我们常常所说的栈,跟C语言的数据段中的栈类似。Java虚拟机直接对Java栈进行两种操作,以帧为单位的压栈和出栈(非线程共享) 操作。

(4)、程序计数器(非线程共享):

程序计数器(Program Counter Register),也有称作为PC寄存器。过汇编的同学对程序计数器应该不陌生,在汇编语言中,程序计数器是指CPU中的寄存器,它保存的是程序当前执行的指令的地址(也可以说保存下一条指令的所在存储单元的地址),当CPU需要执行指令时,需要从程序计数器中得到当前需要执行的指令所在存储单元的地址,然后根据得到的地址获取到指令,在得到指令之后,程序计数器便自动加1或者根据转移指针得到下一条指令的地址,如此循环,直至执行完所有的指令。

JVM中的程序计数器不像汇编语言中的程序计数器一样是物理概念上的CPU寄存器,但是JVM中的程序计数器的功能跟汇编语言中的程序计数器的功能在逻辑上是等同的,也就是说可以用来指示执行哪条指令。

(5)、本地方法栈(非线程共享):

地方法栈与Java栈的作用和原理相似。区别不过是Java栈是为执行Java方法服务的,而本地方法栈则是为执行本地方法(Native Method)服务的。
在这里插入图片描述

2、垃圾回收器(GC)

JVM的垃圾回收原理是这样的,它把对象分为年轻代(Young)、年老代(Tenured)、持久代(Perm),对不同生命周期的对象使用不同的垃圾回收算法。

年轻代(Young)

年轻代分为三个区,一个eden区,两个Survivor区。程序中生成的大部分新的对象都在Eden区中,当Eden区满时,还存活的对象将被复制到其中一个Survivor区,当此Survivor区的对象占用空间满了时,此区存活的对象又被复制到另外一个Survivor区,当这个Survivor区也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制到年老代。

年老代(Tenured)

年老代存放的是上面年轻代复制过来的对象,也就是在年轻代中还存活的对象,并且区满了复制过来的。一般来说,年老代中的对象生命周期都比较长。

持久代(Perm)

用于存放静态的类和方法,持久代对垃圾回收没有显著的影响。

如下图所示,
在这里插入图片描述
我们先来想一想,为什么需要把堆对象分代?不分代能完成它所做的事情么?答案是肯定的。

其实不分代完全可以,分代的唯一理由就是优化GC性能。

我们试想,如果没有分代,那我们所有的堆对象都在一块,GC的时候我们要找到哪些对象没用,这样就会对堆的所有区域进行扫描,而我们的很多对象都是朝生夕死的,效率会非常地低。如果分代的话,我们把新创建的对象放到某一地方,当GC的时候直接先把这块存“朝生夕死”对象的区域进行回收,这样就会节省大量的时间开销,也会腾出很大的空间出来。

具体的回收算法如下所示,

JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分别叫from和to)。1个Eden区和1个Survivor区的默认比例为8:1(from和to大小相同,所以总的比例是8:2)。一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一次GC,年龄就会增加1岁,当它的年龄增加到一定程度时,就会被移动到年老代中。

因为年轻代中的对象基本都是朝生夕死的(80%以上),所以在年轻代的垃圾回收算法使用的是复制算法,复制算法的基本思想就是将内存分为两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面,因此复制算法不会产生内存碎片。

在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值可以设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。

综上就是JVM的构造图和垃圾回收器及原理的简单介绍,很多公司在面试招人时一般都会问到这两个方面的知识,如果把以上总结的内容都掌握,基本上再回答这类问题时就会显得游刃有余了。下篇我们将介绍Dalvik虚拟机,敬请期待。

本公众号将以推送Android各种技术干货或碎片化知识,以及整理老司机日常工作中踩过的坑涉及到的经验知识为主,也会不定期将正在学习使用的新技术总结出来进行分享。每天一点干货小知识把你的碎片时间充分利用起来。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值