Java基础2:JVM学习总结

来源:互联网 发布:yy会员签到软件 编辑:程序博客网 时间:2024/05/01 21:26

一、JVM是什么?

JVM(Java Virtual Machine,Java虚拟机),通过在实际的计算机上仿真模拟各种计算机功能来实现的虚拟计算机。Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需编译生成在Java虚拟机上运行的目标代码即字节码,就可以在多种平台上不加修改地运行。JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行

二、JRE/JDK/ JVM的区别

①JRE(JavaRuntimeEnvironment,Java运行环境),也就是Java平台。所有的Java 程序都要在JRE下才能运行。普通用户只需要运行已开发好的java程序,安装JRE即可。
②JDK(Java Development Kit)是程序开发者用来来编译、调试java程序用的开发工具包。JDK的工具也是Java程序,也需要JRE才能运行。为了保持JDK的独立性和完整性,在JDK的安装过程中,JRE也是 安装的一部分。所以,在JDK的安装目录下有一个名为jre的目录,用于存放JRE文件。
③JVM(JavaVirtualMachine,Java虚拟机)是JRE的一部分。它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。JVM有自己完善的硬件架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。Java语言最重要的特点就是跨平台运行。使用JVM就是为了支持与操作系统无关,实现跨平台。

三、JVM体系结构

  • 类装载器(ClassLoader)(用来装载.class文件)
  • 执行引擎(执行字节码,或者执行本地方法)
  • 运行时数据区(方法区、堆、java栈、PC寄存器、本地方法栈)
  • 垃圾回收器自动管理内存的回收


  • 四、JVM内存管理机制

    JVM内存分哪几个区,每个区的作用是什么?


    第一块:PC寄存器(PC)

    PC寄存器是用于存储每个线程下一步将执行的JVM指令地址,如该方法为native的,则PC寄存器中不存储任何信息。

    第二块:栈(Stack)

    JVM栈是线程私有的,每个线程创建的同时都会创建JVM栈。

    JVM栈是用来存储当前线程中局部变量表(Java中定义的八种基本类型:boolean、char、byte、short、int、long、float、double)、方法返回结果(还有方法返回地址)以及StackFrame对于基本数据类型的变量,则直接存储它的值;对于引用类型的变量,则存的是指向对象的引用。

    栈是以帧为单位保存当前线程的运行状态,每当线程调用一个Java方法时,JVM就会在该线程对应的栈中压入一个帧,这个帧自然就成了当前方法使用的当前帧。栈帧由三部分组成:局部变量区、操作数栈、帧数据区。当执行这个方法时,它使用这个帧来存储对应方法的局部数据:参数、局部变量、中间运算结果等等。方法执行完,对应的帧则从栈中弹出,并把返回结果存储在调用方法的帧的操作数栈。

    JVM栈上的所有数据都是私有的。任何线程都不能访问另一个线程的栈数据,所以我们不用考虑多线程情况下栈数据访问同步的情况。

    第三块:堆(Heap)

    JVM堆是JVM中所有线程共享的,因此在其上进行对象内存的分配均需要进行加锁,这也导致new对象的开销比较大。

    JVM堆是用来存储对象实例以及数组值的区域,可以认为Java中所有通过new创建的对象的内存都在此分配,Heap中的对象的内存需要等待GC进行回收。

    第四块:方法区域Method Area)
    ①有时候也成为永久代,在该区内很少发生垃圾回收,但是并不代表不发生GC,在这里进行的GC主要是对方法区里的常量池和对类型的卸载
    ②方法区主要用来存储已被虚拟机加载的类的信息、常量、静态变量和即时编译器编译后的代码等数据。
    ③该区域是被线程共享的。
    ④方法区里有一个运行时常量池(Runtime Constant Pool)用于存放静态编译产生的字面量和符号引用。其中字面量比较接近Java语言层面的常量概念,比如文本字符串,声明为final的常量值等。
    该常量池具有动态性,也就是说常量并不一定是编译时确定,运行时生成的常量也会存在这个常量池中。

    第五块:本地方法堆栈(Native Method Stacks)

    JVM采用本地方法堆栈来支持native方法的执行,此区域用于存储每个native方法调用的状态。


    五、JVM垃圾回收机制

    1、定义
    在Java中,程序员是不需要显示的去释放一个对象的内存的,而是由虚拟机自行执行。在JVM中,有一个低优先级的垃圾回收线程,在正常情况下是不会执行的,只有在虚拟机空闲或者当前堆内存不足时,才会触发执行,扫瞄堆中没有被任何引用的对象,并将它们添加到要回收的集合中进行回收。
    GC(Garbage Collection)的基本原理:将内存中不再被使用的对象进行回收,GC中用于回收的方法称为垃圾回收器,由于GC需要消耗一些资源和时间,Java在对对象的生命周期特征进行分析后,按照新生代、旧生代的方式来对对象进行收集,以尽可能的缩短GC对应用造成的暂停。

    (1)对新生代的对象的收集称为minor GC;

    (2)对旧生代的对象的收集称为Full GC;

    (3)程序中主动调用System.gc()强制执行的GC为Full/Main GC。

    2、垃圾回收器常用的算法

    GC(Garbage Collectio)为了能够正确释放对象,会监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等,GC都需要进行监控。监视对象状态是为了更加准确地、及时地释放对象,而释放对象的根本原则就是该对象不再被引用。

    1. ①标记-清除(Mark-Sweep)
    2. 这是垃圾收集算法中最基础的,根据名字就可以知道,它的思想就是标记哪些要被回收的对象,然后统一回收。这种方法很简单,但是会有两个主要问题:1.效率不高,标记和清除的效率都很低;2.会产生大量不连续的内存碎片,导致以后程序在分配较大的对象时,由于没有充足的连续内存而提前触发一次GC动作。
    3. ②复制算法(Copying)
    4. 为了解决效率问题,复制算法将可用内存按容量划分为相等的两部分,然后每次只使用其中的一块,当一块内存用完时,就将还存活的对象复制到第二块内存上,然后一次性清楚完第一块内存,再将第二块上的对象复制到第一块。但是这种方式,内存的代价太高,每次基本上都要浪费一般的内存。
      于是将该算法进行了改进,内存区域不再是按照1:1去划分,而是将内存划分为8:1:1三部分,较大那份内存交Eden区,其余是两块较小的内存区叫Survior区。每次都会优先使用Eden区,若Eden区满,就将对象复制到第二块内存区上,然后清除Eden区,如果此时存活的对象太多,以至于Survivor不够时,会将这些对象通过分配担保机制复制到老年代中。(java堆又分为新生代和老年代)
    5. ③引用计数法 (Reference Counting)
    6. 引用计数器在微软的 COM 组件技术中、Adobe 的 ActionScript3 种都有使用。引用计数器的实现很简单,对于一个对象 A,只要有任何一个对象引用了 A,则 A 的引用计数器就加 1,当引用失效时,引用计数器就减 1。只要对象 A 的引用计数器的值为 0,则对象 A 就不可能再被使用
    7. ④标记-压缩算法 (Mark-Compact):标记-压缩算法是一种老年代的回收算法,它在标记-清除算法的基础上做了一些优化。也首先需要从根节点开始对所有可达对象做一次标记,但之后,它并不简单地清理未标记的对象,而是将所有的存活对象压缩到内存的一端。之后,清理边界外所有的空间。这种方法既避免了碎片的产生,又不需要两块相同的内存空间,因此,其性价比比较高。
    8. ⑤分代收集(Generational Collecting):现在的虚拟机垃圾收集大多采用这种方式,它根据对象的生存周期,将堆分为新生代和老年代。在新生代中,由于对象生存期短,每次回收都会有大量对象死去,那么这时就采用复制算法。老年代里的对象存活率较高,没有额外的空间进行分配担保,所以可以使用标记-整理 或者 标记-清除

    3、常见垃圾回收器分类
    新生代串行收集器、老年代串行收集器、新生代并行回收 收集器、老年代并行回收收集器、CMS 收集器