一:内存
为什么需要内存?
程序可以简化为:数据 + 逻辑;
硬盘:存储数据。
cpu:执行逻辑;
但是硬盘的速度远远跟不上cpu的速度,因此在它们之间增加了一层速度介于cpu与硬盘的存储介质,即内存;
另外内存的速度与cpu也不在同一个量级,为此cpu与内存之间还有一个中间存储层:高速缓存;
为什么需要jvm内存
内存的分配和回收是由os内核管理,这种操作代价高昂,频繁的申请和回收会性能严重下降。jvm为了提高性能以及内存的可控性,一次性从os内核申请足够多的内存,长期使用,“永”不回收至到jvm退出;也就是说:内核一次分配,长期占有,自己管理;类似内存池。同时:jvm自动管理内存的分配和回收,解放开发人员对内存的手工管理。
二:JVM内存模型
上图包含了整个jvm概念模型中的主要组件;虚线部分为jvm内存模型。
内存模型中包含了很多组件,时间长了总会忘记。实际上任何一个事物的存在都有它存在理由,只要理解了其中的原理,通过推理就可以轻松记忆。
编写的java代码最终需要加载到计算机中进行执行运算。class会首先加载到内存,并存储在“方法区”,以供cpu调用执行。
jvm执行是通过调用一个一个的java方法,完整的执行一个业务逻辑。一次业务执行就是一个方法链。下层方法执行结束如何跳回到上层方法,以及如何恢复上层方法的上下文(环境及变量等)
栈就是解决这个问题的数据结构:每个方法为一个最小单元,即栈帧;cpu调用方法时,按调用顺序一一入栈,一直到最后一个方法,当最后一个方法执行结束,此栈帧出栈就自动回到了上一个方法的栈帧。
如:A方法调用B方法,B方法又调用C方法; 栈中的结构为: A->B->C ; C先执行结束,C出栈;再执行B结束,B出栈;再执行A结束,A出栈;整个业务执行结束。
jvm是一个虚拟机,最终的调用仍然依赖os的内核,所以部分功能需要调用os方法,这些os提供的方法即本地方法。只要是方法调用就涉及到
栈,因此本地方法也需要对应栈:即本地方法栈。
栈的最小单位是栈帧也即方法,通过栈可以解决方法层级调用时,下层方法执行结束后回到上层方法。
现代计算机为了提高高性能,通过使用多线程并发执行业务。当一个线程被切换出cpu时,它对应的栈的执行行数(栈帧即方法中的行数)必须暂存起来,以用来在当前线程切换回cpu时,找到原来执行位置继续执行。程序计数器就是保存线程中方法执行位置,以供以后恢复现场。
栈的最小单位是栈帧也即方法,它不仅保存执行到哪个方法;还维护方法调用链之间的关系;以及方法中的临时变量(基本数据类型非包装类型、对象引用)。
程序计数器的最小单位是方法的行数,即最后一个执行命令。
程序=数据+逻辑; 如果说栈对应的是逻辑,那么堆对应的就是数据。
java中,一切皆对象(基本类型非包装类型除外),那么所有对象都是存储在堆中。
jvm提供内存管理,是为了解放开发人员对内存的操作,使开发人员更专注业务。但是任何事情都有两面性,jvm提供了便捷的内存管理功能,但是在某些情况下也牺牲了高性能;如:从网络接收数据然后再转发到网络或持久化,如果不需要对数据进行修改,就不需要把数据先从内核复制到jvm中,再转发或持久化,以减少数据的复制次数,以及减少堆中的垃圾回收。
此时,为了高性能就可以使用直接内存,即:从自动内存管理转为手动内存管理。
总结:
方法区:维护class文件(即程序执行模版);
堆:提供数据;
栈:维护方法调用链;
程序计数器:维护方法具体执行到的行数;
本地方法栈:维护本地方法调用链
直接内存:堆外,手动维护的内存;
三:堆内存模型
堆的作用:存储对象,包括Class对象。
堆的结构:新生代和老年代组成; 新生代由:eden 和两个大小相同的小内存块s1及s2组成。
为什么需要分代?
如果不分代,jvm堆是一整块内存区域。所有对象都存储在这个堆中。由于jvm是自动管理内存,即自动分配置和回收内存,那么每次回收内存时,都要遍历整个堆中的对象,检查是否需要回收,以及执行回收操作。
由于不同对象的生命周期不同,有的活几十毫秒,有的几分钟,有的可能数小时。假如堆中有100万个对象,基中有60万对象生命周期为几分钟,其它40万只有一个回收周期的生命长度,那么每次回收都要毫无用处的遍历60万老对象,严重降低了垃圾回收性能。
总结:为了提高性能,根据不同对象生命周期不同,选择不同回收策略,分而治之的原则。
新生代为什么需要分为三块区域?
这个也与垃圾回收有关,后续垃圾回收章节会详细描述,此处简单理解为:为了减少回收后内存碎片,提高回收效率(死的多活的少,一次遍历:标记同时复制)而选择的一种空间换时间的策略。