JVM 虚拟机(对象创建 class文件结构,类加载器,执行引擎等)

1.揭开 Java 对象创建的奥秘?
2.class 文件结构详解?
3.详解 Java 类的加载过程?

 从功能上来看,一个高级语言虚拟机主要分为两部分,一个是解释器部分,用来运行高级语言编译生成的ByteCode;还有一部分则是Runtime运行时,用来负责运行时的内存空间开辟、管理等等。

> Java 对象创建,class 文件结构
  Java对象模型 。Java对象保存在堆内存中。在内存中,一个Java对象包含三部分:对象头、实例数据和对齐填充。其中对象头是一个很关键的部分,因为对象头中包含锁状态标志、线程持有的锁等标志。

  每一个Java类,在被JVM加载的时候,JVM会给这个类创建一个instanceKlass,保存在方法区,用来在JVM层表示该Java类
当我们使用new创建一个Java对象实例的时候,JVM会创建一个instanceOopDesc对象来表示这个Java对象。同理,
当我们使用new创建一个Java数组实例的时候,JVM会创建一个arrayOopDesc对象来表示这个数组对象。
  HotSpot虚拟机中,对象在内存中存储的布局可以分为三块区域:对象头、实例数据和对齐填充。在虚拟机内部,一个Java对象对应一个instanceOopDesc的对象。其中对象头包含了两部分内容:_mark和_metadata,而实例数据则保存在oopDesc中定义的各种field中。

  每一个Java类,在被JVM加载的时候,JVM会给这个类创建一个instanceKlass,保存在方法区,用来在JVM层表示该Java类。当我们在Java代码中,使用new创建一个对象的时候,JVM会创建一个instanceOopDesc对象,这个对象中包含了两部分信息,对象头以及元数据。对象头中有一些运行时数据,其中就包括和多线程相关的锁的信息。元数据其实维护的是指针,指向的是对象所属的类的instanceKlass。

> JVM主要做两个事情(Java虚拟机):
 1.首先它要做的是把JavaC编译器生成的ByteCode(ByteCode其实就是JVM的虚拟机器指令)生成每台机器所需要的机器指令,让Java程序可执行(如下图)。
 2.第二步,JVM负责整个Java程序运行时所需要的内存空间管理、GC以及Java程序与Native(即C,C++)之间的接口等等。
Oracle的JVM SE7官方规范:https://docs.oracle.com/javase/specs/jvms/se7/html/
  对VM而言,先掌握规范才是最最重要和核心的事情。Class文件是理解Vm实现的关键。
  所有从Java层调用JNI的线程以及从Native线程往调用Java函数的线程都需要创建一个JNIEnv。说白了,JNIEnv环境是Java和Native世界的桥梁。

   关于JVM,需要说明一下的是,目前使用最多的Sun公司的JDK中,自从 1999年的JDK1.2开始直至现在仍在广泛使用的JDK6,其中默认的虚拟机都是HotSpot。2009年,Oracle收购Sun,加上之前收购 的EBA公司,Oracle拥有3大虚拟机中的两个:JRockit和HotSpot,Oracle也表明了想要整合两大虚拟机的意图,但是目前在新发布 的JDK7中,默认的虚拟机仍然是HotSpot,因此本文中默认介绍的虚拟机都是HotSpot,相关机制也主要是指HotSpot的GC机制。
  三大Java虚拟机:1.Oracle HotSpot;2.Oracle JRockit;3.IBM JVM

  好多人觉得JDK源码太多,无从下手,经常半途而非,以我的经验来说,最好的方法是当你在项目中用到了某个类,可以利用空闲时间去研究,当然这是在感兴趣的前提下,比如简单 ArrayList 是如何实现的, StringBuilder 和 StringBuffer 有什么区别, HashMap 的实现原理,带着问题去分析,从简单的类开始循序渐进,同时在分析过程中能够用自己的语言整理出来.

-- Java虚拟机体系结构- http://geek.csdn.net/news/detail/238461
  在Java虚拟机内部有两种线程:守护线程和非守护线程。守护线程通常是由虚拟机自己使用的,比如执行垃圾收集任务的线程。但是,Java程序也可以把它创建的任何线程标记为守护线程。而Java程序中的初始线程-就是开始于main()的那个,是非守护线程。 
  只要还有任何非守护线程在运行,那么这个Java程序也在继续运行。当该程序中所有的非守护线程都终止时,虚拟机实例将自动退出。假若安全管理器允许,程序本身也能够通过调用Runtime类或者System类的exit()方法来退出。
  Java虚拟机是通过某些数据类型来执行计算的,数据类型可以分为两种:基本类型和引用类型,基本类型的变量持有原始值,而引用类型的变量持有引用值。
  Java虚拟机的引用类型被统称为“引用(reference)”,有三种引用类型:类类型、接口类型、以及数组类型,它们的值都是对动态创建对象的引用。类类型的值是对类实例的引用;数组类型的值是对数组对象的引用,在Java虚拟机中,数组是个真正的对象;而接口类型的值,则是对实现了该接口的某个类实例的引用。还有一种特殊的引用值是null,它表示该引用变量没有引用任何对象。 
  常量池存储了相应类型所用到的所有类型、字段和方法的符号引用,所以它在Java程序的动态连接中起着核心的作用。
  每当启动一个新线程时,Java虚拟机都会为它分配一个Java栈。Java栈以帧为单位保存线程的运行状态。虚拟机只会直接对Java栈执行两种操作:以帧为单位的压栈和出栈。 
  本地方法本质上时依赖于实现的,虚拟机实现的设计者们可以自由地决定使用怎样的机制来让Java程序调用本地方法。

深入java虚拟机即时编译详解- http://blog.csdn.net/u012124438/article/details/73410381
JVM几个要点- http://blog.csdn.net/SEU_Calvin/article/category/6382949
深入JVM关闭与关闭钩子- http://blog.csdn.net/dd864140130/article/details/49155179
白话JVM- http://blog.csdn.net/dd864140130/article/category/6072658
JVM学习笔记- http://blog.csdn.net/wingichoy/article/details/50508450
Java虚拟机详解,JVM常见问题总结- http://geek.csdn.net/news/detail/237706
不定参数函数牵扯出来的栈的生长方向和大端小端模式- http://blog.csdn.net/peng314899581/article/details/60597155?ref=myread
Java千百问_JVM架构- http://blog.csdn.net/ooppookid/article/category/6229592
深入JVM系列(1):http://blog.csdn.net/vernonzheng/article/details/8458483
深入JVM系列(2):http://blog.csdn.net/kobejayandy/article/details/8496663
深入JVM系列(3):http://blog.csdn.net/kobejayandy/article/details/8496665
Java虚拟机学习- http://blog.csdn.net/java2000_wl/article/category/1249100
【深入Java虚拟机】- http://blog.csdn.net/mmc_maodun/article/category/1823903
JVM分析- http://blog.csdn.net/RowandJJ/article/category/2181725
深入Java虚拟机: http://blog.csdn.net/column/details/java-vm.html

> JVM = 类加载器(classloader) + 执行引擎(execution engine) + 运行时数据区域(runtime data area)
 1.JVM,JVM的类加载器
  在 Java 虚拟机规范中,对这个 区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出 StackOverflowError 异 常;如果虚拟机栈可以动态扩展(当前大部分的 Java 虚拟机都可动态扩展,只不过 Java 虚拟机规范中也允许 固定长度的虚拟机栈),当扩展时无法申请到足够的内存时会抛出 OutOfMemoryError 异常。  在JAVA虚拟机中,负责查找并装载类型的那部分被称为类装载子系统。
  JAVA虚拟机有两种类装载器:启动类装载器和用户自定义类装载器。前者是JAVA虚拟机实现的一部分,后者则是Java程序的一部分。由不同的类装载器装载的类将被放在虚拟机内部的不同命名空间中。
深入探讨 Java 类加载器-- https://www.ibm.com/developerworks/cn/java/j-lo-classloader/

-- JVM的类加载器:
Java高新技术第一篇:类加载器详解-- http://blog.csdn.net/jiangwei0910410003/article/details/17733153
  Java虚拟机中可以安装多个类加载器,系统默认三个主要的类加载器,每个类负责加载特定位置的类:BootStrapClassLoader,ExtClassLoader,AppClassLoader。
  类加载器也是Java类,因为Java类的类加载器本身也是要被类加载器加载的,显然必须有第一个类加载器不是Java类,这个正是BootStrapClassLoader,使用C/C++代码写的,已经封装到JVM内核中了,而ExtClassLoader和AppClassLoader是Java类。

-- 自定义ClassLoader
  自定义的MyClassLoader, MyClassLoader->AppClassLoader->ExtClassLoader->BootStrapClassLoader.自定义的MyClassLoader首先会先委托给AppClassLoader,AppClassLoader会委托给ExtClassLoader,ExtClassLoader会委托给BootStrapClassLoader,这时候BootStrapClassLoader就去加载,如果加载成功,就结束了。如果加载失败,就交给ExtClassLoader去加载,如果ExtClassLoader加载成功了,就结束了,如果加载失败就交给AppClassLoader加载,如果加载成功,就结束了,如果加载失败,就交给自定义的MyClassLoader1类加载器加载,如果加载失败,就报ClassNotFoundException异常,结束。(责任链设计模式,委托机制)
import java.util.Date;  
import java.util.List;  
/** 
 * 测试类 
 * @author Administrator 
 */  
public class ClassLoaderTest {  
    @SuppressWarnings("rawtypes")  
    public static void main(String[] args){  
        //输出ClassLoaderText的类加载器名称  
        System.out.println("ClassLoaderText类的加载器的名称:"+ClassLoaderTest.class.getClassLoader().getClass().getName());  
        System.out.println("System类的加载器的名称:"+System.class.getClassLoader());  
        System.out.println("List类的加载器的名称:"+List.class.getClassLoader());  
        ClassLoader cl = ClassLoaderTest.class.getClassLoader();  
        while(cl != null){  
            System.out.print(cl.getClass().getName()+"->");  
            cl = cl.getParent();  
        }  
        System.out.println(cl);  
    }  
}   

 2.执行引擎
虚拟机字节码执行引擎- http://blog.csdn.net/u013678930/article/details/51980460
深入JVM字节码执行引擎- http://blog.csdn.net/dd864140130/article/details/49515403
Java虚拟机字节码执行引擎浅析- http://blog.csdn.net/kobejayandy/article/details/39620679
深入JVM字节码执行引擎- http://blog.csdn.net/dd864140130/article/details/49515403

3.运行时数据区域(runtime data area)
-- 1、栈区(stack),由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。  
 2、堆区(heap),一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。  
 3、全局区/static 静态区,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。  
 4、文字常量区,常量字符串就是放在这里的,程序结束后由系统释放  
 5、程序代码区,存放函数体的二进制代码。

-- 1.方法区:方法区存放的是类信息、常量、静态变量,所有线程共享区域。
  2.虚拟机栈:它的生命周期与 线程相同,每个方法在执行的同时都会创建一个栈帧(Stack Frame)用存储局部变量表、操作数栈、动态链接、方法出口等信息,线程私有区域。
  3.本地方法栈:与虚拟机栈类似,区别是虚拟机栈为虚拟机执行Java方法服务,本地方法栈为虚拟机使用到的Native方法。
  4.堆:线程共享;用来存放对象实例,几乎所有的对象实例都在堆上分配内存;此区域也是垃圾回收器(Garbage Collection)主要的作用区域,内存泄漏就发生在这个区域。
  5.程序计数器:用于指示当前线程所执行的字节码执行到了第几行,可以理解为是当前线程的行号指示器。如果线程在执行Java方法,这个计数器记录的是正在执行的虚拟机字节码指令地址。

-- 简单通俗的讲,一个完整的Java程序运行过程会涉及以下内存区域:
  寄存器:JVM内部虚拟寄存器,存取速度非常快,程序不可控制。
  栈:保存局部变量的值,包括:1.用来保存基本数据类型的值;2.保存类的实例,即堆区对象的引用(指针)。也可以用来保存加载方法时的帧。
  堆:用来存放动态产生的数据,比如new出来的对象。注意创建出来的对象只包含属于各自的成员变量,并不包括成员方法。因为同一个类的对象拥有各自的成员变量,存储在各自的堆中,但是他们共享该类的方法,并不是每创建一个对象就把成员方法复制一次。
  常量池:JVM为每个已加载的类型维护一个常量池,常量池就是这个类型用到的常量的一个有序集合。包括直接常量(基本类型,String)和对其他类型、方法、字段的符号引用(1)。池中的数据和数组一样通过索引访问。由于常量池包含了一个类型所有的对其他类型、方法、字段的符号引用,所以常量池在Java的动态链接中起了核心作用。常量池存在于堆中。
  代码段:用来存放从硬盘上读取的源程序代码。
  数据段:用来存放static定义的静态成员。

猜你喜欢

转载自blog.csdn.net/ShareUs/article/details/82181534