java语言与jvm虚拟机简介

 

1、java语言java

    1.1 支持面向对象编程oop算法

        强调支持,由于java一样能够面向过程编程,但java是为oop而生的。编程

        oop的三大特性是:封装、继承、多态。多线程

        封装主要针对成员变量而言,oop的思想要求成员变量均为私有,不该该对外可以访问,一个符合oop思想的类应该只有公共方法对外可以访问;并发

        继承,主要理解继承体系,private、protected、public在继承中的使用场景。理解java是单继承多实现的(与C++的区别);jvm

        多态主要指一个类的实例是运行时决定的,而不是声明时决定的。父类 a = new 子类();是能够的。这种做用在于能够面向抽象编程、面向接口编程,对象没必要必须和声明的类一致,只要是它的子类、孙子类等便可;oop

    1.2 jdk版本对java语言的改进优化

        1996年发布jdk1.0,java语言具有基础的oop语法;线程

        1997年发布jdk1.1,引入内部类;设计

        2004年发布jdk1.5,引入语法糖,自动拆装箱、泛型、动态注解、枚举、可变长参数、遍历循环(foreach);

        2014年发布jdk8,引入Lambda表达式;

2、jvm虚拟机(本文特指官方默认的HotSpot虚拟机)

    2.1 发展

        由一家小公司Longview Technologies开发出来,1997年被sun公司收购,jdk1.3以后正式成为官方默认虚拟机;

        HotSpot得名于其热点代码探测技术,能够有效的把热点代码探测出来,并利用JIT编译器将热点代码进一步优化并编译成机器代码,提升运行效率;

    2.2 jvm虚拟机的内存区域组成

        程序计数器,线程私有,指向字节码指令;

        java虚拟机栈,线程私有,主要就是描述java方法的,配合程序计数器一块儿一步一步往下执行方法(理解为何是栈);

        本地方法栈,线程私有,跟java虚拟机栈相似,区别是它用来执行非java方法;

        java堆,线程共享,这是最大的一块虚拟机内存区域,主要就是咱们new的对象都会分配在这里,这里分为新生代(Eden、Survivor一、Survivor2)和老年代;

        方法区,线程共享,在HotSpot里叫永久代(Permanent Generation),存放加载的类信息、常量、静态变量等,static代码块、static变量、static方法都会存放在这里有一个副本。为何叫永久代,主要是对这部分的对象实例回收的效率不高,这部分对象实例存活率较高;

        运行时常量池,是方法区的一部分;

        直接内存,不是虚拟机内存的一部分,指申请虚拟机内存外的内存。

    2.3 垃圾收集算法

        怎么样判断对象能够回收?有引用计数算法和可达性分析算法。引用计数算法很简单,给每一个对象一个引用计数器,每当有一个地方引用了它,那么就给它计数器+1,当这个引用失效以后计数器-1,这样作很是高效,但有一个缺陷是互相引用的对象,没法被回收,形成内存泄露。HotSpot使用可达性分析算法,可达性分析算法从GC Roots对象出发是否可达来判断对象是否能够回收,GC Roots对象包括虚拟机栈引用的对象、方法区类静态属性引用的对象、方法区常量引用的对象、本地方法栈中引用的对象;

        IBM研究指出98%的对象都是朝生夕死,故新生代中回收频率要较高,每次能够回收大量内存,老年代中通过两次以上的回收仍存活,说明回收的效率不高,回收频率能够低一点。另外,大对象不在新生代中分配,而是直接进入老年代。

        ①标记-清除算法。算法的思想是首先把须要回收的对象标注出来,而后统一清除回收。实现起来很简单,但标记和清除的效率不高,还会产生大量不连续的内存空间,影响后续为新对象分配内存,尤为是大对象。

        ②复制算法。针对标记-清除算法的问题,复制算法的思想是把内存区域均等的分红两块,好比10M的内存均等分为两块5M,每一个时刻只能使用一块,第一次将新对象分配给第一块内存,第一次GC时,先从第一块标记仍存活对象的对象,而后统一复制到第二块内存中,并按内存空间顺序排好,第一块内存则所有回收。第二次回收时就先从第二块开始,循环往复。这样作效率很高,而且内存空间能够连续分配。但形成一个问题是原本10M的内存只能用一半,形成内存的浪费。

        HotSpot在实际实现复制算法时,将内存空间划分为Eden和两个Survivor,且默认Eden和Survivor的比例是8:1:1,新对象分配在Eden中,第一次回收后存活对象被复制到Survivor 1中,Eden所有清除,第二次新对象仍分配在Eden中,第二次回收时Eden和Survivor 1中存活对象被复制到Survivor 2中,Eden和Survivor 1所有清除。

        这里有一个问题:若是存活对象超过内存的10%,Survivor放不下怎么办?这时候就须要从老年代中进行分配担保(Handle Promotion)。

        ③标记-整理算法。和新生代不一样,在老年代中GC回收的效率不会过高,使用复制算法Survivor空间极可能是不够的,若是将Survivor调大又浪费内存空间,这时就提出了标记-整理算法应对老年代的实际状况。标记-整理算法内存回收时先将全部存活对象标记出来,但不进行清除,而是将存活对象都往内存的一端移动,那么内存末端都是可回收的对象,当这些可回收对象被“挤出”内存边界的时候,则被清除了。

        因为HotSpot中把java堆中分为新生代、老年代,他们存活的概率不同,因此按新生代和老年代采起不一样的算法,这就叫分代收集算法,在新生代中采起复制算法,在老年代中使用标记-清除或标记-整理算法。

    2.4 垃圾收集器

        上面分析了新生代、老年代应该采起怎样的算法,HotSpot中针对实际应用场景,实现了不一样的垃圾收集器:

        ①Serial收集器,复制算法,单线程,Client模式下默认新生代收集器。会有Stop The World问题;

        ②ParNew收集器,复制算法,多线程,新生代收集器;

        ③Parallel Scavenge收集器,复制算法,多线程,新生代收集器,与ParNew的区别在于它针对吞吐量设计的;

        ④Serial Old收集器,标记-整理算法,单线程,Client模式下默认老年代收集器;

        ⑤CMS收集器,标记-清除算法,多线程,老年代收集器,以下降停顿时间为目标,只在初始标记、从新标记的时候须要Stop The World,采起并发标记和并发清除下降停顿时间;

        ⑥G1收集器,标记-整理算法+复制算法,多线程,新生代+老年代收集器,是JDK1.7最新的研究成果,以CMS收集器的基础上,加上复制算法的理念,将内存区域划分为多个大小相等的Region,每一个Region再分为新生代和老年代。每次GC时根据Region回收价值优先进行回收,避免之前收集器须要进行全区域垃圾收集和回收;

3、编译与运行

    3.1 javac编译器编译

        第一次编译,将.java文件编译成中间语言,输出.Class文件,这期间主要完成语法分析和词法分析(编译原理)、注解处理、语义分析(解语法糖等)、生成字节码Class文件;

    3.2 解释器

        类加载进解释器运行,类加载的过程有:加载、验证、准备、解析、初始化;

    3.3 JIT即时编译器

        解释器监控热点代码为JIT编译器进一步编译提供监控数据,触发JIT编译器将热点代码编译成机器代码;

 

    本人知识水平有限,文章不免有纰漏之处,请不吝纠正勘误。

相关文章
相关标签/搜索