【JVM】初识JVM

一、JVM的种类:
在IOS和Android兴起之前,Java也广泛应用于各种手机平台和嵌入式平台;在IOS和Android兴起之后,Java在手机领域和嵌入式领域的应用受到了很大的挑战。

JVM的种类如下:

  • KVM:SUN发布,IOS Android之前,广泛应用于手机系统;
  • CDC/CLDC HotSpot:手机、电子书、PAD等设备上建立统一的Java编程接口;是J2ME的重要组成部分。
  • JRockit VM:BEA的一款产品,后面详细介绍一下
  • IBM J9 VM:IBM内部使用,通常用来运行IBM内部的一些程序
  • Apache Harmony:这款虚拟机的特点是开源的,兼容于JDK1.5和JDK1.6的Java程序运行平台。由于Apache和Oracle的关系恶劣,退出了JCP,Java社区的分裂。OpenJDK出现后,开源受到了巨大的挑战,所以这款虚拟机在2011年之后就没有再更新,基本处于停滞状态。这款虚拟机没有大规模的商用经历,但对Android的发展具有积极的推动作用。

    重点介绍一下JRockit VM和HotSpot.就目前来讲,最重要的虚拟机是HotSpot,HotSpot是LongView Technologies开发,后来被SUN收购。到2006年Java开源,建立了OpenJDK,所以HotSpot成为SUN JDK和OpenJDK中所带的虚拟机。
    除了HotSpot之外,还有一款很重要的虚拟机,就是JRockit VM,JRockit VM是BEA的一款产品,在2008年Oracle收购了BEA,得到了JRockit VM.2010年Oracle收购了SUN,得到了HotSpot,Oracle宣布在JDK8时整合JRockit VM和HotSpot,优势互补,在HotSpot的基础上,移植JRockit VM的优秀特性。

二、JVM的基本结构:
Java虚拟机会有一个类加载的系统ClassLoader,把Class文件装载到虚拟机(内存)中去。装载到内存中,内存是分区域的,有方法区、Java堆、Java栈、本地方法栈(本地方法栈其实就是native方法的调用).除此之外,在虚拟机运行的时候会跟一般的CPU一样,需要有一个指针来指向下一条指令的地址,这个就是PC寄存器。执行引擎是用来执行虚拟机的代码,就是Class的类的字节码。另外,虚拟机运行的时候还有一个非常重要的模块,就是GC垃圾收集。

  • PC寄存器:每一个线程拥有一个PC寄存器(就是线程起来的时候会分配一个PC寄存器)PC寄存器总会指向下一条指令的地址(这样程序在执行的过程中就知道下一步需要做什么)。在执行本地方法的时候,PC寄存器的值是未定义的(undefined)
  • 方法区:方法区是用来保存类的源信息的(也就是保存一些Class的信息),用来描述这个类,包括类的字段、方法信息、方法字节码、类型常量池。但这也不是绝对的,在JDK6中字符串(String)的常量信息还是放到方法区中的,到JDK7的时候已经移到了堆里面。从这个角度来说,方法区也好堆信息也好,到底保存什么信息是跟虚拟机的版本有关的,不是绝对的。但一般来讲,我们会理解方法去就是保存一些类的源信息,对类进行描述。方法区通常会和永久区(Perm)关联在一起,永久区保存一些相对静止、相对稳定的数据,但永久区并不是说它的数据永久不能回收,永久定死的,这个是不一定的。
  • Java堆:Java堆是程序开发过程中最为密切的一个内存区间了,永久区主要还是由Java虚拟机维护的,跟程序开发关系不是特别密切,而我们所有在程序中通过new操作出来的对象基本上都保存在Java堆中。Java堆是全局共享的,所有线程都共享Java堆,也就是你分配了一个对象之后,所有的线程都是能够访问的。Java堆的结构和GC算法是有关系的,不同的GC方式就需要一个不同的堆,就目前来讲,分代的GC是使用最为普遍的,用分代GC自然也就需要一个分代的堆。
  • Java栈:Java的堆是全局共享的,Java栈是线程私有的。栈是由一系列的帧组成,所以Java栈也叫帧栈。栈是一个先进后出的数据结构,在这个数据结构中放的是什么内容呢?就是放的帧。而帧里面保存的是什么内容呢?帧里面放的是一个方法调用保存的局部变量,操作数的栈,指向常量池的指针。每一次方法调用都会创建一个新的帧,并且把这个帧压到栈里面去。
  • 栈、堆、方法区的交互:类的实例以及对象本身保存到堆中,而对象的引用保存到栈中;类信息的描述包括类方法的实际字节码是不在堆当中的,保存在方法区中;栈会指向堆,堆的一些信息会去方法区存储和读取。

三、Java的内存模型
每一个线程都有一个工作内存和主存(主存就是大家共享的内存空间,可以理解为堆空间,就是所有的线程都共享,里面可以存储数据。但是考虑到效率的问题,每个线程都有一个工作内存).工作内存和主存之间是需要有一个同步关系的,很多变量(原始的变量)在主存中有一份,线程的工作内存中会有一份变量的拷贝.工作内存和主存之间可以做一些同步,就是通过assign、read、load、use、store、write这些操作去做。

当数据从主存复制到工作内存的时候,首先由主存发起一个read的操作,然后紧接着由工作内存执行一个load的操作;当数据从工作内存复制到主存的时候,首先由工作内存发起一个store的操作,然后紧接着由主存执行一个write的操作。

volatile:

  • 如果一个线程中想要修改一个变量之后不想让另一个线程立即可见,就需要用到volatile标识符
  • private volatile boolean stop = false; 如果使用这个标识符,需要在声明变量的时候声明为volatile
  • 一般会认为volatile的性能比锁好一点(不是绝对的,肯定是比重量级的锁性能要好)

跟内存模型相关的几个概念:

可见性:一个线程修改了某个变量,其他线程立刻就可以知道
保证可见性的方法:

 - volatile
 - synchronized(做线程间的同步,在解锁(unlock)之前,写变量值回主存。同步之后,所有线程直接就会变成顺序执行)
 - final(被定义成常量的一些变量,这些常量在初始化完成之后,其他线程就可见了)

有序性:在一个线程中,所有的指令、所有的操作都是有序执行的。在线程之外,在多线程的情况下观察,操作都是无序的(产生无序的可能性有两种:指令重排或主存和工作内存之间数据同步的延时)

字节码运行的两种方式:

  • 解释运行:读一句执行一句
  • 编译执行(JIT):是运行过程中执行字节码的时候编译成机器码,然后直接执行机器码。这个编译是在运行时编译,而不是静态时编译。编译后的性能会有数量级的提升。
    保守估计,解释运行和编译运行,性能会差10倍以上

你可能感兴趣的:(JVM)