深入理解Java虚拟机

一、引子

在写Java代码的时候,都知道写的代码都是在一个叫做Java虚拟机的东西上执行的。今天突然开始思考到底什么是虚拟机,它是怎么实现的,发现自己确实有点模棱两可了。在本文中,我会写下对虚拟机的总结理解。


二、概述

2.1、定义

Java虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机有自己完善的硬体架构,如处理器、堆栈、寄存器等,还具有相应的指令系统。Java虚拟机屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。

它是用来执行一系列虚拟计算机指令,大体上虚拟机可以分为系统虚拟机和程序虚拟机。visual Box、VMare就属于系统虚拟机。他们完全是对物理计算机的仿真,提供一个可运行完整操作系统的软件平台。而java虚拟机就是典型程序虚拟机,它专门为执行单个计算机程序而设计,在java虚拟机中执行的指令我们称之为java字节码指令。java发展到今天,出现了很多虚拟机,最初sun使用的叫Classic的java虚拟机,到现在使用最广泛的是HotSpot虚拟机,除了sun以外还有BEA的JRockit,目前JRockit和HotSpot都被甲骨文公司收入旗下,大有整合的趋势。

一般情况下我们不需要知道虚拟机的运行原理,只要专注写java代码就可以了,这也正是虚拟机之所以存在的原因–屏蔽底层操作系统平台的不同并且减少基于原生语言开发的复杂性,使java这门语言能够跨各种平台(只要虚拟机厂商在特定平台上实现了虚拟机),并且简单易用。


2.2、基本结构

Java虚拟机基本结构
Java虚拟机运行时数据区
接着用一张图来介绍每个区域存储的内容:
Java虚拟机运行时数据区


2.3、Java的平台无关性

java体系结构包括四个独立却相关的技术:
1:java程序设计语言,就是我们通俗的代码
2:java class文件,就是我们编译后的代码
3:java应用编程接口(API),就是我们经常查阅的公共代码
4:java虚拟机

  • 首先确定一个观念:我们运行的是class文件,我们是在java虚拟机上运的class文件,我们的代码的交互对象是java虚拟机。
  • 其次再明确两个概念:java中有两种方法,java方法和本地方法
    • java方法:java语言编写的,编译成字节码,存储在class文件中
    • 本地方法:其它语言(c,c++)编写的,编译成处理器相关的机器代码,保存在动态链接库,动态链接库和操作系统绑定
      在这里插入图片描述

如图,无论是什么操作系统,只要提供可以和java虚拟机关联的本地方法,那么java程序就无须修改,直接在此系统运行。这其实是转移了问题的处理位置,将矛盾放在java虚拟机和本地方法之间,让程序代码独立出来。之后只要完成,各类java虚拟机的实现,和各种本地方法的支持,java的移植就变得非常容易。


2.4、虚拟机中对象创建过程(HotSpot 虚拟机)
2.4.1、对象的创建

(1)遇到 new 指令时,首先检查这个指令的参数是否能在 常量池 中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有,执行相应的类加载。

  • 运行时常量池:属于方法区一部分,用于存放编译期生成的各种 字面量符号引用。编译器和运行期(String 的 intern() )都可以将常量放入池中。内存有限,无法申请时抛出 OutOfMemoryError。
  • 字面量:int i=1;这里的整数1就是字面量;
  • 符号引用:用一组符号来描述引用的对象,在编译时,java类并不知道所引用的类的实际地址,因此只能使用符号引用来代替。比如org.simple.People类引用了org.simple.Language类,在编译时People类并不知道Language类的实际内存地址,因此只能使用符号org.simple.Language来代替

(2)类加载检查通过之后,为新对象分配内存(因为对象所需内存大小在类加载完成后是确定的)。在堆的空闲内存中划分一块区域(‘ 指针碰撞-内存规整 ’或‘ 空闲列表-内存交错 ’的分配方式)。

  • 指针碰撞:java堆中内存是规整的,分配内存仅仅就是把指针向空闲空间挪动一段和对象大小相同的距离;
  • 空闲列表:java堆中内存不是规整的,维护一个列表,用于记录哪些内存区域是空闲的和占用的,在分配时从列表中找出足够的内存给予分配,然后更新列表。

每个线程在堆中都会有私有的分配缓冲区(TLAB),这样可以很大程度避免在并发情况下频繁创建对象造成的线程不安全。

(3)内存空间分配完成后会初始化为 0(不包括对象头(Object Head)),保证了java代码中不赋初值时,程序能访问到这些字段的数据类型所对应的零值。
(4)接下来就是填充对象头,把对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希码、对象的 GC 分代年龄等信息存入对象头。执行 new 指令后执行 init 方法后才算一份真正可用的对象创建完成。
(5)以虚拟机角度看,新对象已经完成创建,而从程序角度看还没有init,所有字段还是零值。所以最后一步是按照程序员意愿进行初始化数据.


2.4.2、对象的内存布局

在 HotSpot 虚拟机中,分为 3 块区域:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)


2.4.3、对象的访问定位

使用对象时,通过栈上的 reference 数据来操作堆上的具体对象。
Java 堆中会分配一块内存作为句柄池。reference 存储的是句柄地址。
reference 中直接存储对象地址(使用直接指针访问)


三、总结

1、虚拟机从操作系统的角度来看,它只是一个普通进程。

2、只是这个叫做虚拟机的进程比较特殊,它能够加载我们编写的class文件。如果把JVM比作一个人,那么class文件就是我们吃的食物。

3、加载class文件的是一个叫做类加载器的子系统。就好比我们的嘴巴,把食物吃到肚子里。

4、虚拟机中的执行引擎用来执行class文件中的字节码指令。就好比我们的肠胃,对吃进去的食物进行消化。

5、虚拟机在执行过程中,要分配内存创建对象。当这些对象过时无用了,必须要自动清理这些无用的对象。清理对象回收内存的任务由垃圾收集器负责。就好比人吃进去的食物,在消化之后,必须把废物排出体外,腾出空间可以在下次饿的时候吃饭并消化食物。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值