初识JVM
这几天因为看类加载器的东西,所以顺便看了些有关JVM的,所以就在这里说一下我对JVM的初次认识
首先,我们来说什么是JVM
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。这种说法比较官方,其实我们关心的就是JVM到底是干什么的,它能干什么,粗略的说,JVM就是执行JAVA程序的,它的运行原理就是把Java语言写的源程序通过Java编译器,编译成与平台无关的“字节码程序”(.class文件,也就是0,1二进制程序),然后在OS之上的Java解释器中解释执行。
大致的我们可以认为jvm的结构分为方法区,堆区,栈区,但是在这里,详细的说一下JVM的结构,下面这张图很经典,是我从网上找来的.(1)类加载子系统: 它就是负责从网上或者文件中加载Class信息,所谓的Classloader就是从属于它的一个部件,然后将取得的类信息存放于方法区。
(2)JAVA堆: 它里面存放的应该是所有的类的实例,几乎所有的JAVA对象实例都存储在这里
(3)JAVA栈: 它里面存储的为当前线程中局部基本类型的变量(java中定义的八种基本类型:boolean、char、byte、short、int、long、float、double)、部分的返回结果以及Stack Frame(栈帧:用于存储局部变量表、操作栈、动态链接、方法出口等信息),非基本类型的对象在栈中仅存放一个指向堆上的地址。
(4)方法区: 方法区里存的都是类型信息,也就是类的信息,而类的信息又包括以下内容:
类的全限定名(类的全路径名)
类的直接超类的全限定名(如果这个类是Object,则它没有超类)
这个类是类型(类)还是接口
类的访问修饰符,如public、abstract、final等
所有的直接接口全限定名的有序列表(假如它实现了多个接口)
常量池(存放基本类型常量和字符串常量)
字段、方法信息、类变量信息(静态变量) 装载该类的装载器的引用(classLoader)、类型引用(class)
(5)本地方法栈: 它和JAVA栈非常的相似,不同的是JAVA栈用于方法调用,本地方法栈用于本地方法的调用,意味着它可以调用一个接口,但这个接口可能不是用JAVA实现的,而是用比如C,C++等开发的,为什么有本地方法栈以及本地方法(native),与JAVA刚诞生时面临的境地有关,具体的话建议大家去自行百度本地方法&本地方法栈)
(6)PC寄存器: 每一个线程都有自己的寄存器空间,是独立的,用于存储每个线程下一步将执行的JVM指令,如该方法为native method,则PC寄存器中不存储任何信息。
(7)GC (垃圾回收机制) 他是JVM核心的部分,它让程序员不再关心超出生命周期的对象如何处理,实现了对于堆,栈,以及方法区内存的全自动化管理
对于堆这一块,还有个不得不说,就是对于堆空间的划分
前面我们说过了,堆中存放的是所有类的对象实例,可以说,GC的大量操作,都是针对于堆空间的,假如没有堆划分的话,会有个大麻烦,一是内存碎片的问题,被GC处理的内存空间变为碎片空间,无法被充分利用,二是每次GC操作的对象无疑是非常庞大的,这是灾难性的问题,所有就需要对堆空间进行划分。下面图就能说明划分的结果:Young(新生代):几乎所有新生成的对象优先存放在新生代(大对象除外),细的划分的话,分为Eden,Survivor;而Survivor又分成了两个From和To;先来说一下Eden,方才说过,新生成的对象都在新生代,准确的说,新生成的对象都在Eden,而如果Eden区的内存已满,就会**一次GC,而没有被回收的对象就会移入Survivor区,那如果下一次的GC,Survivor区中的一些对象被回收了,那么就会产生内存碎片,为了避免这个问题,将survivor区又分为From和To,处理方法和Eden处理类似,就可以避免内存碎片的问题,JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。因此,新生代实际可用的内存空间为 9/10 ( 即90% )的新生代空间。
Old(老年代): 如果有对象在新生代经过了15次GC也就是达到了年龄阈值15,却还没有被回收,那么这些对象就会被移入老年代
分为老年代和新生代的原因在于,我们可以对不同的区采用不同的GC算法,不同的GC算法都有自己的优缺点,比如对于新生代就采用的是minorGC,老年代采用的就是FullGC,FullGC耗时很长,所以**一次也不容易。
GC的常见算法
这个是需要提到的,大致有下面几种:
1.引用计数
就是说,如果这个对象有一个引用,就增加一个引用计数,如果删除一个引用,则减少一个计数,垃圾回收的时候,就会回收掉引用计数为0的对象
2.复制
其实这个算法就是运用在survivor的From和To的算法,它就很好的处理的内存碎片,但是缺点就是需要两倍的空间。
3.标记-清除
此算法分为两个阶段,第一阶段是从根节点标记所有被引用的对象。第二阶段是清除未被标记的对象。解释一下就是,先将当前对于内存的操作停下,然后标记,在清除未被标记的,但是也有缺点就是,它需要停止当前的应用,而且会产生内存碎片
4.标记-整理
这个算法结合了2 3,第一阶段,从根节点标记所有被引用的对象。第二阶段是清除未被标记的对象,同时将所有存活对象压缩到堆中的一块连续的内存空间。