`

JVM初步探索

阅读更多

JVM体系结构
1、类装载器(ClassLoader)子系统
    用来转载.class文件
2、执行引擎
    执行字节码,或者执行本地方法
3、运行时数据区
    方法区,堆,java栈,PC寄存器,本地方法栈

            JVM体系结构之类加载器
类加载过程
1、转载
    装载过程负责找到二进制字节码并加载至JVM中,JVM通过类名、类所在的包名通过ClassLoader来完成类的加载,同样也可采用以上三个元素来标示一个加载的类类名+包名+ClassLoader实例ID
2、链接
    连接过程负责对二进制字节码的格式进行校验,初始化装载类的静态变量以及解析类中调用的接口、类。
    在转成校验后,JVM初始化类中的静态变量,并将其值赋为默认值。
    最后一步为对类中的所有属性、方法进行验证,以确保其需要调用的属性、方法存在,以及具备应有权限(例如public、private域权限),会造成NoSuchMethodError、NoSuchFieldError等错误信息。
3、初始化
    初始化过程即为执行类中的静态初始化代码、构造器代码以及静态属性的初始化,在四种情况下初始化过程会被触发执行:
调用new;反射调用类中的方法;子类调用了初始化;JVM其中过程中指定的初始化类。

类加载器种类
一、包括类装载器和用户自定义类加载器,启动类装载器是JVM实现的一部分,用户自定义装载器则是Java程序的一部分,必须是ClassLoader的子类。
二、 主要分为以下几类:
(1) Bootstrap ClassLoader
      这是JVM的根ClassLoader,它是用C++实现的,JVM启动时初始化此ClassLoader,并由此ClassLoader完成$JAVA_HOME中jre/lib/rt.jar(Sun JDK的实现)中所有class文件的加载,这个jar中包含了java规范定义的所有接口以及实现。
(2) Extension ClassLoader
     JVM用此classloader来加载扩展功能的一些jar包
(3) System ClassLoader
      JVM用此classloader来加载启动参数中指定的Classpath中的jar包以及目录,在Sun JDK中ClassLoader对应的类名为AppClassLoader。
(4) User-Defined ClassLoader
       User-DefinedClassLoader是Java开发人员继承ClassLoader抽象类自行实现的ClassLoader,基于自定义的ClassLoader可用于加载非Classpath中的jar以及目录
三、ClassLoader抽象类提供了几个关键的方法:
(1)loadClass
      此方法负责加载指定名字的类,ClassLoader的实现方法为先从已经加载的类中寻找,如没有则继续从parent ClassLoader中寻找,如仍然没找到,则从System ClassLoader中寻找,最后再调用findClass方法来寻找,如要改变类的加载顺序,则可覆盖此方法
(2)findLoadedClass
    此方法负责从当前ClassLoader实例对象的缓存中寻找已加载的类,调用的为native的方法。
(3) findClass
    此方法直接抛出ClassNotFoundException,因此需要通过覆盖loadClass或此方法来以自定义的方式加载相应的类。
  (4) findSystemClass
    此方法负责从System ClassLoader中寻找类,如未找到,则继续从Bootstrap ClassLoader中寻找,如仍然为找到,则返回null。
  (5)defineClass
    此方法负责将二进制的字节码转换为Class对象
  (6) resolveClass
  此方法负责完成Class对象的链接,如已链接过,则会直接返回。

            JVM体系结构之执行引擎
一、JVM通过执行引擎来完成字节码的执行,在执行过程中JVM采用的是自己的一套指令系统,每个线程在创建后,都会产生一个程序计数器(pc)和栈(Stack),其中程序计数器中存放了下一条将要执行的指令,Stack中存放Stack Frame,表示的为当前正在执行的方法,每个方法的执行都会产生Stack Frame,Stack Frame中存放了传递给方法的参数、方法内的局部变量以及操作数栈,操作数栈用于存放指令运算的中间结果,指令负责从操作数栈中弹出参与运算的操作数,指令执行完毕后再将计算结果压回到操作数栈,当方法执行完毕后则从Stack中弹出,继续其他方法的执行。

      在执行方法时JVM提供了invokestatic、invokevirtual、invokeinterface和invokespecial四种指令来执行
     (1)invokestatic:调用类的static方法
     (2) invokevirtual: 调用对象实例的方法
     (3) invokeinterface:将属性定义为接口来进行调用
     (4) invokespecial: JVM对于初始化对象(Java构造器的方法为:<init>)以及调用对象实例中的私有方法时。
二、执行技术
    主要的执行技术有:解释,即时编译,自适应优化、芯片级直接执行
    (1)解释属于第一代JVM,
    (2)即时编译JIT属于第二代JVM,
    (3)自适应优化(目前Sun的HotspotJVM采用这种技术)则吸取第一代JVM和第二代JVM的经验,采用两者结合的方式
   
    (4)自适应优化:开始对所有的代码都采取解释执行的方式,并监视代码执行情况,然后对那些经常调用的方法启动一个后台线程,将其编译为本地代码,并进行仔细优化。若方法不再频繁使用,则取消编译过的代码,仍对其进行解释执行。

            JVM运行时数据区

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

  第二块:JVM栈
      JVM栈是线程私有的,每个线程创建的同时都会创建JVM栈,JVM栈中存放的为当前线程中局部基本类型的变量(java中定义的八种基本类型:boolean、char、byte、short、int、long、float、double)、部分的返回结果以及Stack Frame,非基本类型的对象在JVM栈上仅存放一个指向堆上的地址

  第三块:堆(Heap)
       Heap是大家最为熟悉的区域,它是JVM用来存储对象实例以及数组值的区域,可以认为Java中所有通过new创建的对象的内存都在此分配,Heap中的对象的内存需要等待GC进行回收。
    JVM将Heap分为New Generation和Old Generation(或Tenured Generation)两块来
进行管理:
(1)New Generation
        又称为新生代,程序中新建的对象都将分配到新生代中,新生代又由Eden Space和两块Survivor Space构成,可通过-Xmn参数来指定其大小
(2) Old Generation
       又称为旧生代,用于存放程序中经过几次垃圾回收还存活的对象,例如缓存的对象等,旧生代所占用的内存大小即为-Xmx指定的大小减去-Xmn指定的大小。
  第四块:方法区域(Method Area)
       (1)方法区域存放了所加载的类的信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息,当开发人员在程序中通过Class对象中的getName、isInterface等方法来获取信息时,这些数据都来源于方法区域,可见方法区域的重要性,同样,方法区域也是全局共享的,在一定的条件下它也会被GC,当方法区域需要使用的内存超过其允许的大小时,会抛出OutOfMemory的错误信息。
       (2)在Sun JDK中这块区域对应的为Permanet Generation,又称为持久代,默认为64M,可通过-XX:PermSize以及-XX:MaxPermSize来指定其大小。

 第五块:运行时常量池(Runtime Constant Pool)
       类似C中的符号表,存放的为类中的固定的常量信息、方法和Field的引用信息等,其空间从方法区域中分配。

第六块:本地方法堆栈(Native Method Stacks)
       JVM采用本地方法堆栈来支持native方法的执行,此区域用于存储每个native方法调用的状态。
            JVM体系结构之内存回收
一、 JVM中自动的对象内存回收机制称为:GC(Garbage Collection)

1、 GC的基本原理:
      为将内存中不再被使用的对象进行回收,GC中用于回收内存中不被使用的对象的方法称为收集器,由于GC需要消耗一些资源和时间的,Java在对对象的生命周期特征进行分析后,在V 1.2以上的版本采用了分代的方式来进行对象的收集,即按照新生代、旧生代的方式来对对象进行收集,以尽可能的缩短GC对应用造成的暂停
      (1)对新生代的对象的收集称为minor GC,
      (2)对旧生代的对象的收集称为Full GC,
      (3)程序中主动调用System.gc()强制执行的GC为Full GC,
二、 JVM中自动内存回收机制
(1)引用计数收集器
 原理:
      引用计数是标识Heap中对象状态最明显的一种方法,引用计数的方法简单来说就是对每一个对象都提供一个关联的引用计数,以此来标识该对象是否被使用,当这个计数为零时,说明这个对象已经不再被使用了。

 优点:
      引用计数的好处是可以不用暂停应用,当计数变为零时,即可将此对象的内存空间回收,但它需要给每个对象附加一个关联引用计数

 缺点:
      并且引用计数无法解决循环引用的问题,因此JVM并没有采用引用计数。
(2)跟踪收集器
 原理:
      跟踪收集器的方法为停止应用的工作,然后开始跟踪对象,跟踪时从对象根开始沿着引用跟踪,直到检查完所有的对象。

根对象的来源主要有三种:
        1.被加载的类的常量池中的对象引用
        2.传到本地方法中,没有被本地方法“释放”的对象引用
        3.虚拟机运行时数据区中从垃圾收集器的堆中分配的部分

存在问题:
     跟踪收集器采用的均为扫描的方法,但JVM将Heap分为了新生代和旧生代,在进行minor GC时需要扫描是否有旧生代引用了新生代中的对象,但又不可能每次minor GC都扫描整个旧生代中的对象,因此JVM采用了一种称为卡片标记(Card Marking)的算法来避免这种现象。
(3)卡片标记算法
     卡片标记的算法为将旧生代以某个大小(例如512字节)进行划分,划分出来的每个区域称为卡片,JVM采用卡表维护卡的状态,每张卡片在卡表中占用一个字节的标识(有些JVM实现可能会不同),当Java代码执行过程中发现旧生代的对象引用或释放了对于新生代对象的引用时,就相应的修改卡表中卡的状态,每次Minor GC只需扫描卡表中标识为脏状态的卡中的对象即可

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics