java基础之----jvm介绍
jvm体系结构
类装载器:加载类文件到内存中,注意类文件并不是.java文件,而是.class,是经过javac编译过后的文件,那类加载器把类加载到内存中,类的元数据信息存在哪里呢?答案是方法区中。但是类加载器并不负责执行,而是Execution Engine负责执行。
本地方法栈:本地方法的运行区,那什么是本地方法呢?本地方法也即native方法,因为Java在刚发布的时候,那时候是c和c++的世界,所以Java不得不作出一些妥协,兼容c和c++,所以我们有时看Java代码,如果是Java程序写的我们都可以点进去看看源代码,但是如果碰到了本地本地方法就无法点进去看源代码了。
程序计数器:每个方法运行的时候,jvm都需要知道这个方法中的字节码运行到什么位置了,来决定下一行运行什么,那就会又一个记录运行位置的计数器,其实就是一个指针,叫做程序计数器。
方法区:Java8中这个东东已经不存在了,在Java8中,在Java8以前叫做永久带或者叫着方法区,永久带是在Java的堆内存中的,所以Java8以前堆内存报OOM有两种,第一种是“OutOfMemoryError:java heap size”,第二种是”OutOfMemoryError: PermGen space”,第二种其实就是永久带堆内存溢出,那永久带为什么会溢出呢?如果只是存放一些类的元数据信息,不至于会内存溢出,真正的原因是现在很多的Java程序中的很多类都是动态加载,而当动态加载的时候是在方法区中进行的,比如我们经常在程序中使用的mysql驱动,就是动态加载的。Java8中为了避免这个问题,把元空间直接放到了内存中,理论上来说你内存多大,他都可以使用,不过有参数可以限制。上面说了一大堆,那元空间中放的是什么呢?放的是Java8原生自带的jar包,比如rt.jar等,类的元数据信息,静态变量,常量。方法区也是有垃圾回收的,GC发现某个类加载器不再存活了,会把相关的空间整个回收掉。这个垃圾回收的机制是不是堆中的垃圾回收机制相同,不太清楚,没有找到相关的资料。
栈:首先明白一点就是栈才是程序的运行区,伴随着程序运行的整个生命周期,当程序结束,栈中的数据也会随之释放。那栈里面放的是什么数据呢?栈里面放的是栈帧。那栈帧又是什么东东呢?栈帧就是存放方法运行时方法中的变量和方法的输入输出参数,栈的操作,包括出栈和入栈,类文件和方法。栈帧入栈的顺序是怎么的?大家都知道栈是符合“先进后出”的原则的。举个例子,如果方法A调用了方法B,那方法A会先入栈,之后方法B在入栈,出栈顺序刚好相反。如下图:
堆:堆是用来存放对象的地方。下面就介绍一下堆的结构和垃圾回收机制:
上面是堆空间模型,堆上的GC分为MinorGC和OldGC,其中MinorGC过程如下。
- 首先当Eden元区满的时候,会触发第一次Young gc,把活着的对象复制到Survivor From区,当Eden区再次满的时候,会同时扫描Eden和From区,把活着的对象全部复制到To区(如果To区中的对象的年龄达到老年代的标准会直接复制到老年区),同时把这些对象的年龄加1
- 清空Eden和From区,谁空谁时To区
- SurvivorFrom和ServivorTo互相交换,原来的ServivorFrom变成下次GC的To区,部分对象会在ServivorFrom和ServivorTo中来回复制15次(由JVM参数MaxTenuringThresholdz参数决定),如果对象仍然存活,会把这部分对象复制到Old区
常见的垃圾收集器参考这篇文章:https://crowhawk.github.io/2017/08/15/jvm_3/
如何判断某个对象为垃圾
引用计数法
目前主流的java虚拟机都摒弃掉了这种算法,最主要的原因是它很难解决对象 之间相互循环引用的问题。尽管该算法执行效率很高。
例如:在testGC()方法中,对象objA和objB都有字段instance,赋值令objA.instance=objB及objB.instance=objA,除此之外这两个对象再无任何引用,实际上这两个对象都已经不能再被访问,但是它们因为相互引用着对象方,异常它们的引用计数都不为0,于是引用计数算法无法通知GC收集器回收它们。
可达性分析算法
前主流的编程语言(java,C#等)的主流实现中,都是称通过可达性分析(Reachability Analysis)来判定对象是否存活的。这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。如下图所示,对象object 5、object 6、object 7虽然互相有关联,但是它们到GC Roots是不可达的,所以它们将会被判定为是可回收的对象。
常用的垃圾回收算法
复制算法
复制算法把内存分为两部分,两部分大小相同,每次只使用其中一个。一块内存用完后,把活着的对象复制到另一块内存,当前内存块全部回收。这个算法不会产生内存碎片,但是可用内存明显变少。复制算法把内存分为两部分,两部分大小相同,每次只使用其中一个。一块内存用完后,把活着的对象复制到另一块内存,当前内存块全部回收。这个算法不会产生内存碎片,但是可用内存明显变少。复制算法一般应用于新生代。
标记-清除算法
标记-清除算法分为两个阶段:标记、清除。在标记阶段,扫描并标记所有需要回收的对象;在清除阶段,统一回收被标记的对象。这个算法的效率并不高,并且清除对象后会产生大量不连续的内存碎片。一般应用于老年代。
标记-整理算法
标记-整理算法与标记-清除算法类似,不同的是,不会直接对垃圾对象进行回收,而是把所有存活的对象都移动到一端,然后清理掉其他部分。一般应用于老年代。
分代收集算法