JVM 从入门到出门

一、JVM 是什么

Java虚拟机(Java Virtual Machine,JVM)是运行所有 Java 程序的抽象计算机,是Java语言的运行环境。

Java 是一种跨平台的语言,但是 Java 源文件是不能直接运行的,而是需要将 Java 源文件编译成一种“中间码”——字节码,但是字节码也是不能直接运行,字节码是需要在 Java 虚拟机(Java Virtual Machine,JVM)上运行。并且每个系统平台都有自己的 JVM,所以 Java 语言编译后能通过 JVM 在不同平台上运行,从而实现了跨平台。JVM 的功能就解释执行字节码。

简单的说,JVM 就是一个操作系统,这个操作系统是基于其他操作系统之上的一个运行 Java 程序的操作系统。所以 Java 的跨平台性实质上是 字节码 和 JVM 的跨平台性。

总结起来就是,Java 的跨平台性并不是 Java 文件能跨平台运行,而是编译后的字节码文件能由不同平台上的 JVM 转化成平台上的机器指令。

Java 跨平台

JVM 执行程序时,主要做了一下几点事情:

  1. 加载 Class 文件
  2. 管理并分配内存
  3. 执行垃圾回收(参见《Java 垃圾回收(GC)机制》

下面分别说说。

二、JVM 如何加载 Class 文件

上面说了 Java 文件编译成字节码文件,然后将字节码文件交给 JVM 加载,那 JVM 如何加载呢?

Java 程序启动时,并不是一次把所有的类全部加载、运行,而是把保证程序运行的基础类一次性加载到 JVM 中,其他类等到 JVM 用到的时候再加载。

加载有两种方式:

  1. 隐式装载:程序在运行过程中,遇到 new 等方式生成类或者子类对象、使用类或者子类的静态域时,隐式调用类加载器(ClassLoader)加载对应的的类到 JVM 中。
  2. 显式装载:通过调用Class.forName()或者ClassLoader.loadClass(className)等方法,显式加载需要的类。
类的生命周期

加载的过程包括了加载、验证、准备、解析、初始化五个阶段。在这五个阶段中,加载、验证、准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始,这是为了支持 Java 语言的运行时绑定。另外注意这里的几个阶段是按顺序开始,而不是按顺序进行或完成,因为这些阶段通常都是互相交叉地混合进行的,通常在一个阶段执行的过程中调用或激活另一个阶段。

上述只是概念性的过程,对应到 JVM 的具体的执行情况如下图:

JVM 加载 Class 文件

其中:

  • Class Loader:依据特定格式,加载 Class 文件到内存。
  • Runtime Data Area:JVM 内存空间结构模型。
  • Execution Engine:对命令进行解析。
  • Native Interface:融合不同开发语言的原生库为 Java 所用。

小结一下,类从编译到执行的过程:

  1. 编译器将 Java 源文件编译为 Class 字节码文件;
  2. ClassLoader 将字节码转化为 JVM 中的对象;
  3. JVM 根据字节码初始化对象。

接下来将结合上图,具体谈谈 JVM 加载 Class 过程中很重要的两部分:ClassLoader 和 Runtime Data Area。

三、ClassLoader 与 双亲委派模型

ClassLoader 在 Java 中有着非常重要的作用,它主要工作在 Class 的加载阶段,其主要作用是从系统外部获取 Class 二进制数据流。它是 Java 的核心组件,所有的 Class 都是由 ClassLoader 进行加载的,ClassLoader 负责通过将 Class 文件里的数据流装载进系统,然后交给 Java 虚拟机进行链接、初始化等操作。

那么 ClassLoader 的加载流程是怎样的呢?这就涉及双亲委派模型了。

双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。

双亲委派模型

如上图,双亲委派模型的工作过程是:

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成。

每一个层次的类加载器都是如此。因此,所有的加载请求最终都应该传送到顶层的启动类加载器中。

只有当父加载器反馈自己无法完成这个加载请求时(搜索范围中没有找到所需的类),子加载器才会尝试自己去加载。

ClassLoader 加载过程

为什么需要双亲委托机制?

采用双亲委派模式的是好处是 Java 类随着它的类加载器一起具备了带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父类已经加载了该类时,就没必要子 ClassLoader 再加载一次。

其次是考虑到安全因素,Java 核心 API 中定义类型不会被随意替换,假设我自定义一个名为 java.lang.Integer 的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心 Java API 发现这个名字的类,发现该类已被加载,并不会重新加载自定义的 java.lang.Integer,而直接返回系统已加载过的 Integer.class,这样便可以防止核心 API 库被随意篡改。

四、Runtime Data Area

Runtime Data Area

线程共享:Heap、Metaspace。
线程私有:本地方法栈、程序计数器、虚拟机栈。

程序计数器

程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器,用来指示当前执行的是哪条指令

由于 Java 虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器内核都只会执行一条线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储,称这类内存区域为“线程私有”的内存。

Java 虚拟机栈

与程序计数器一样,Java 虚拟机栈(Java Virtual Machine Stacks)也是线程私有的,它的生命周期与线程相同。

虚拟机栈描述的是 Java 方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame,是方法运行时的基础数据结构)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。

本地方法栈

本地方法栈(Native Method Stack)与虚拟机栈所发挥的作用是非常相似的,它们之间的区别是虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一。

Java堆

对于大多数应用来说,Java 堆(Java Heap)是 Java 虚拟机所管理的内存中最大的一块。Java 堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例都在这里分配内存。同时这里也是垃圾回收的核心区域。

方法区

方法区(Method Area)与 Java 堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

JVM 三大性能调优参数调优参数:

  • -Xss:规定了每个线程堆栈的大小。一般情况下256K是足够了。影响此进程中并发线程数大小;
  • -Xms:设置堆的初始分配大小,默认为物理内存的 1/64;
  • -Xmx:堆的最大分配内存,默认为物理内存的 1/4;

堆与栈的区别:

  • 管理方式:栈由系统自动释放,堆需要 GC 管理;
  • 空间大小:栈比堆小;
  • 碎片相关:栈产生的碎片远少于堆;
  • 分配方式:栈支持静态和动态分配,而堆仅支持动态分配;
  • 效率:栈的效率比堆高。

五、总结

本篇文章先从 Java 的跨平台性说起,提到了 Java 源文件编译成字节码文件,字节码文件通过 JVM 运行在各个平台上,然后通过了解类的生命周期,解释了 JVM 是如何加载字节码文件,接着介绍了在加载的过程,需要深入了解的 ClassLoader 和 Runtime Data Area。

这些也都是 JVM 的基础。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,039评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,426评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,417评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,868评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,892评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,692评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,416评论 3 419
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,326评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,782评论 1 316
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,957评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,102评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,790评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,442评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,996评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,113评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,332评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,044评论 2 355

推荐阅读更多精彩内容