前言:jvm是java的底层,是全部java框架的底层,凡事需深刻现象看本质,知晓其原理才能更好的作上层开发。java
1、jvm的体系结构python
类装载器classloader:用来装载.class文件算法
执行引擎:执行字节码,或者执行本地方法c#
运行时数据区(这个就是本章讨论的重点):方法区、虚拟机栈、本地方法栈、堆、程序计数器 数组
2、运行时数据区缓存
一、程序计数器:能够当作当前线程所执行的字节码的行号指示器( 线程私有)多线程
二、 java虚拟机栈:存放基本数据类型、对象引用类型(线程私有)并发
三、本地方法栈:存放本地(native)方法相关信息框架
四、方法区:存放已被虚拟机加载的类型信息、常量、静态变量、及时编译器编译后额代码缓存等数据jvm
五、堆:对象实例及数组
3、对象的内存布局
对象在堆内存的存储布局能够分为三个部分:对象头、实例数据、填充数据(保证对象大小都是8字节的整数倍)
Hotspot虚拟机对象的对象头中包括两类信息:第一类是用于存储对象自身的运行时的数据,如hashcode、gc分代年龄、锁状态标志、线程持有的锁、偏向线程id、偏向时间戳,这些被称为Mark Word,另外一类是类型指针(经过其判断是哪一个类的实例)
4、垃圾收集(GC)
显然,不能全部对象一直在内存中存在,这样不停有新对象,内存就会爆掉,全部须要进行GC,而在进行GC的时候,咱们首先就须要判断哪些对象存活,哪些对象已死,下面就介绍常见的2种算法判断对象已死
一、引用计数算法(python)
在对象中添加一个引用计数器,每当有一个地方引用它时,计数器就加一;当引用失效时,计数器就减一;任什么时候刻计数器为零的对象就是不可能再被使用的(这个算法看似很好,实则否则,当出现对象循环引用时就很难解决)
二、可达性分析(java、c#)
经过一系列称为"GC Roots"的根对象做为起始节点,根据其引用关系向下搜索,搜索过程所走过的路径被称为“引用链”,当一个对象到GC Roots之间没有任何引用链,可断定为可回收对象,不过这个时候不会直接回收掉,需进行第一次标记,随后再经过一次筛选,筛选的条件就是此对象是否有必要执行finalize(),加入对象没有覆盖finalize(),或者已经执行过一次,这认为此对象能够被回收了。
常见的GC Roots:当前或者的线程所引用的对象、方法区的静态变量、常量
三、引用的分类
引用分为如下四类:强引用、软引用、弱引用、虚引用
其中强引用就是咱们普通的new对象,只要强引用关系还存在,就不会被回收
软引用,被软引用的对象在系统进行下下次GC则回收
弱引用,被弱引用的对象在系统进行下次GC则回收
虚引用,彻底无影响对象生存时间,GC时会收到系统通知,就是一个对象存活的监控
5、垃圾收集算法
一、标记-清除算法
首先标记出须要回收的对象,在标记完成后,同一回收(显然会效率低、内存碎片化)
二、标记-复制算法
将可用内存划分为等大小2块,当一块快用完了,将其或者的对象按顺序移到另外一块上(显然浪费空间)
三、标记-整理算法
首先标记全部可回收对象,而后将其存活的对象都放一块儿,其余位置清掉
四、分代收集算法
分代是基于把堆分红如下3个部分的
新生代:通常状况下,全部新生成的对象首先放在新生代,新生代又分为Eden区域和两个Survivor(survivor0、suirvivor1)区,大部分对象在Eden区生成
老年代:存放的都是些生命周期较长的对象,在新生代中经历了屡次垃圾回收仍然存活的对象就放入老年代
元空间:存放静态文件,如java类、方法
新生代采用标记复制算法,老年代采用标记整理算法
6、经典垃圾收集器
一、Serial收集器
看其名字便知道其为单线程收集器,单线程收集器比较大的一个问题就是,会Stop the world,就是停掉正常工做的全部线程。
serial收集器中,对新生代采用标记复制算法,对老年代采用标记整理算法
二、ParNew收集器
多线程Serial收集器
三、CMS收集器
分四个步骤初始标记(STW)、并发标记、从新标记、并发清除(STW)
缺点:并发处理、致使应用程序变慢 且CMS收集器是基于标记清除的,会形成内存碎片化
四、G1收集器
大体分四个步骤初始标记、并发标记、最终标记、筛选回收
除并发标记外,其余步骤均STW
筛选回收:任意选择多个Region区进行GC,将一个Region区活着的对象放入空的Region区
G1经过把堆分红多个相对独立的区域,并行的选择性回收,兼顾STW与吞吐量,基于标记-复制算法