Java 虚拟机

本贴最后更新于 2698 天前,其中的信息可能已经水流花落

1、前言
作为一名 Java 程序开发,几乎每天都在和虚拟机打交道,今天就说说我对于虚拟机的理解。
我们平常接触到的虚拟机有 jvm 、dvm 、art 虚拟机。 dvm 是基于 jvm 优化而用于移动端,art 针对 dvm 又进行了优化。 本质都是 jvm 优化版,所以本章以讲解 jvm 为主。

2、class 文件(jvm 的执行文件)

什么是 class 文件?
能被 jvm 识别、加载并执行的文件格式,是一种 8 位字节的二进制流文件,它记录了一个类文件的所有信息并远远多于 java 源文件的内容(如 this、super 等这些在 class 文件中都会被编译器赋值)

如何生成 class 文件?
编码生成.java 文件 通过 jdk 的 javac 命令生成 .java -> .class , 每个 java 类或接口都会编译生成一个对应的 class 文件(也因为此当虚拟机查找类文件中的内容时会有大量的 io 操作),

编译流程:
1ce24747dda84760a218c05d3d4a6d96.png
这里我们只做了解即可,编译原理已经忘的差不多了,很尴尬。

缺点?
每个类文件记录了大量的信息,占用内存较大(class 的文件结构可以通过二进制阅读软件去查看,有兴趣可以去详细了解其结构);
基于堆栈的加载模式,加载速度慢;
文件 io 操作多,类查找慢。
因此它并不适合移动端,由此产生了 dvm、art 这类移动端的虚拟机

3、jvm 的结构
a744cacf4f6a42e2834eee73dd16e4ec.png

class 文件由类加载器载入内存,加载时会先对 class 文件进行校验、解析等操作,如图:
27a6ab35b84148d0ae83503f3c52ceb6.png

4、内存管理
内存空间分为: 栈区、方法区、本地方法栈区、堆区
Java 栈区:
存放 Java 方法执行时的所有数据,由栈帧组成
Java 栈帧:
包含:局部变量表、栈操作数、动态链接、方法出口
每个方法从调用到执行完成对应了一个栈帧在虚拟机栈中的入栈和出栈,当超过栈所允许的最大深度时就会抛出 stackoverflow 异常(比如方法中死循环)
本地方法栈:
专门为 native 方法服务的栈区
方法区:
存放加载的类信息、常量、静态变量、即时编译器编译后的数据
该区会一直占用内存
堆区:
所有创建的对象都存放于该区,是虚拟机中最大的一块内存,也是 GC 要回收的部分

在堆区内存中又划分了几块: 新生代区(young generation)、老生代区(old generation)、永久区(permanent generation)

456c80f6c034441382562ab093e3a809.png

新生代区: 存放新生成的对象
老生代区: 当新生代区内存不足时,根据算法将新生代的某些对象移入该区,为新生代区提供内存存放新对象,当新生代区和老生代区都无可用内存时就会出现 OOM

为什么要在堆区区分新生代区和老生代区?有什么好处?
如此区分,可以允许开发者去动态调整新生代区和老生代区的大小,便于内存分配以适应不同场景,
如一些大型服务类场景 并不需要频繁创建对象 就可以让老生代内存大一些 方便这些服务常驻 提高服务稳定性
如即时通讯 临时消息对象多 就可以把新生代调整的大一些 老生带小一些 加快内存分配

5、内存垃圾回收 (GC)
虚拟机要去回收垃圾,首先得知道哪些对象是垃圾对象,然后才能去回收。这里就涉及到垃圾收集算法和垃圾回收算法

垃圾收集算法

1、引用计数算法
这是 jvm 早期使用的算法,jdk1.2 之前所用
创建对象时会为其产生引用计数器并加 1,都有新引用引用该对象时计数器 +1,引用该对象的引用销毁时计数器-1,当为 0 时即为垃圾对象,可以被回收
缺陷: 两个对象相互引用时(环形引用),计数器都为 1,但是两者都不可达,却导致无法回收
f65d424937b84b25a8c99518ae4031db.png
代码举例:
A a = new A();
B b = new B();
a.b = b;
b.a = a;
a = null;
b = null;
这个时候 a,b 引用被置空,但其 ab 两个对象还在堆中且相互引用,我们也没法通过引用找到这两个对象,他们也无法被回收。

2、可达性算法 (根搜索算法)

jdk1.2 之后对垃圾收集算法进行了改进

将所有引用关系视作一张图

从 GcRoot 节点开始寻找对应的所有引用的节点,找到节点后继续寻找它的引用节点,当所有引用节点寻找完毕后,没有被引用的节点不可达节点,就是垃圾对象,这样也解决了环引用对象回收问题
86f17dcdcbba434c82d4ce218bc64a6b.png

说到引用,这里简单说一下,引用有几种类型:
强引用 Object obj = new Object(); 不回收

软引用 内存不足时回收

弱引用 WeakReference wf = new WeakReference(obj); gc 时回收

虚引用 gc 时回收

垃圾回收算法

通过垃圾收集算法找到了要回收的垃圾如何进行回收呢?

1、标记-清除算法

将未被引用的对象(不可达对象)标记为可回收对象,垃圾回收时将其清除
a2d6159d876348458e4a4c1763d5dd3f.png
优点: 不需要对对象进行移动,仅对不存活的对象进行处理,在存活对象多时会极为高效
缺点: 直接清除对象置空,容易造成内存碎片,不利于后续内存分配

2、复制算法
将可达对象复制到空闲内存中,不可达的直接跳过,最后将原来的内存清空
b84aab34e6214e3a8018fffedb51800f.png
优势:存活对象少时高效
缺点:需要更多内存作交换空间 (需要内存大)

3、标记-整理算法
清除不可达对象后,将后续可达对象移动到该清除后的内存区域并更新引用的位置
d9d7d937c7c2499e89a842bafc99c1d8.png
在标记-清除算法的基础上进行了移动,成本更高,但解决了内存碎片问题

总结:
这三种算法各有优劣,在虚拟机中会动态根据情况采用不同的算法,而不是只用一种算法

6、垃圾回收的触发
1、jvm 无法再为新对象分配内存空间时触发

2、手动调用 System.gc() (不推荐使用) 不会立马去执行垃圾回收,会加大虚拟机压力

3、低优先级的 GC 线程被启动时会触发

看到这里,相信对 jvm 已经有了比较深的了解了,下面再将 jvm 、dvm、art 进行对比
7、jvm、dvm、art 之间的比较
** jvm 与 dvm**

1、执行文件格式不同,class / dex(将多个.class 文件通过命令一个生成 dex 文件)

2、dvm 类加载系统与 jvm 区别较大

3、dvm 可以同时存在多个(某一个挂掉的话不会影响其他 dvm 的运行,确保稳定性),jvm 只能同时存在 1 个

4、dalvik 基于寄存器的,jvm 基于栈,寄存器被内存更快。

ART

dvm 使用 JIT 动态将字节码转换成机器码效率低。 (JIT : Just In Time ,每次运行时转码)

ART 采用了 AOT(ahead of time 安装时就进行转码)预编译技术,执行速度快

ART 会占用更多应用安装时间和存储空间(空间换时间)

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3187 引用 • 8213 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • V2EX

    V2EX 是创意工作者们的社区。这里目前汇聚了超过 400,000 名主要来自互联网行业、游戏行业和媒体行业的创意工作者。V2EX 希望能够成为创意工作者们的生活和事业的一部分。

    17 引用 • 236 回帖 • 328 关注
  • wolai

    我来 wolai:不仅仅是未来的云端笔记!

    2 引用 • 14 回帖
  • 设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    200 引用 • 120 回帖 • 1 关注
  • Android

    Android 是一种以 Linux 为基础的开放源码操作系统,主要使用于便携设备。2005 年由 Google 收购注资,并拉拢多家制造商组成开放手机联盟开发改良,逐渐扩展到到平板电脑及其他领域上。

    334 引用 • 323 回帖 • 2 关注
  • jsDelivr

    jsDelivr 是一个开源的 CDN 服务,可为 npm 包、GitHub 仓库提供免费、快速并且可靠的全球 CDN 加速服务。

    5 引用 • 31 回帖 • 59 关注
  • 开源

    Open Source, Open Mind, Open Sight, Open Future!

    408 引用 • 3575 回帖 • 1 关注
  • Mac

    Mac 是苹果公司自 1984 年起以“Macintosh”开始开发的个人消费型计算机,如:iMac、Mac mini、Macbook Air、Macbook Pro、Macbook、Mac Pro 等计算机。

    166 引用 • 595 回帖 • 1 关注
  • Git

    Git 是 Linux Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。

    209 引用 • 358 回帖
  • Netty

    Netty 是一个基于 NIO 的客户端-服务器编程框架,使用 Netty 可以让你快速、简单地开发出一个可维护、高性能的网络应用,例如实现了某种协议的客户、服务端应用。

    49 引用 • 33 回帖 • 22 关注
  • 大疆创新

    深圳市大疆创新科技有限公司(DJI-Innovations,简称 DJI),成立于 2006 年,是全球领先的无人飞行器控制系统及无人机解决方案的研发和生产商,客户遍布全球 100 多个国家。通过持续的创新,大疆致力于为无人机工业、行业用户以及专业航拍应用提供性能最强、体验最佳的革命性智能飞控产品和解决方案。

    2 引用 • 14 回帖
  • IBM

    IBM(国际商业机器公司)或万国商业机器公司,简称 IBM(International Business Machines Corporation),总公司在纽约州阿蒙克市。1911 年托马斯·沃森创立于美国,是全球最大的信息技术和业务解决方案公司,拥有全球雇员 30 多万人,业务遍及 160 多个国家和地区。

    17 引用 • 53 回帖 • 136 关注
  • Facebook

    Facebook 是一个联系朋友的社交工具。大家可以通过它和朋友、同事、同学以及周围的人保持互动交流,分享无限上传的图片,发布链接和视频,更可以增进对朋友的了解。

    4 引用 • 15 回帖 • 453 关注
  • 安全

    安全永远都不是一个小问题。

    199 引用 • 816 回帖
  • 前端

    前端技术一般分为前端设计和前端开发,前端设计可以理解为网站的视觉设计,前端开发则是网站的前台代码实现,包括 HTML、CSS 以及 JavaScript 等。

    247 引用 • 1348 回帖 • 2 关注
  • H2

    H2 是一个开源的嵌入式数据库引擎,采用 Java 语言编写,不受平台的限制,同时 H2 提供了一个十分方便的 web 控制台用于操作和管理数据库内容。H2 还提供兼容模式,可以兼容一些主流的数据库,因此采用 H2 作为开发期的数据库非常方便。

    11 引用 • 54 回帖 • 653 关注
  • 爬虫

    网络爬虫(Spider、Crawler),是一种按照一定的规则,自动地抓取万维网信息的程序。

    106 引用 • 275 回帖 • 2 关注
  • RESTful

    一种软件架构设计风格而不是标准,提供了一组设计原则和约束条件,主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

    30 引用 • 114 回帖 • 1 关注
  • 区块链

    区块链是分布式数据存储、点对点传输、共识机制、加密算法等计算机技术的新型应用模式。所谓共识机制是区块链系统中实现不同节点之间建立信任、获取权益的数学算法 。

    91 引用 • 751 回帖 • 4 关注
  • flomo

    flomo 是新一代 「卡片笔记」 ,专注在碎片化时代,促进你的记录,帮你积累更多知识资产。

    5 引用 • 107 回帖
  • Log4j

    Log4j 是 Apache 开源的一款使用广泛的 Java 日志组件。

    20 引用 • 18 回帖 • 29 关注
  • 外包

    有空闲时间是接外包好呢还是学习好呢?

    26 引用 • 232 回帖 • 4 关注
  • 微服务

    微服务架构是一种架构模式,它提倡将单一应用划分成一组小的服务。服务之间互相协调,互相配合,为用户提供最终价值。每个服务运行在独立的进程中。服务于服务之间才用轻量级的通信机制互相沟通。每个服务都围绕着具体业务构建,能够被独立的部署。

    96 引用 • 155 回帖 • 2 关注
  • SEO

    发布对别人有帮助的原创内容是最好的 SEO 方式。

    35 引用 • 200 回帖 • 23 关注
  • Rust

    Rust 是一门赋予每个人构建可靠且高效软件能力的语言。Rust 由 Mozilla 开发,最早发布于 2014 年 9 月。

    58 引用 • 22 回帖
  • jsoup

    jsoup 是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据。

    6 引用 • 1 回帖 • 476 关注
  • SpaceVim

    SpaceVim 是一个社区驱动的模块化 vim/neovim 配置集合,以模块的方式组织管理插件以
    及相关配置,为不同的语言开发量身定制了相关的开发模块,该模块提供代码自动补全,
    语法检查、格式化、调试、REPL 等特性。用户仅需载入相关语言的模块即可得到一个开箱
    即用的 Vim-IDE。

    3 引用 • 31 回帖 • 101 关注
  • 30Seconds

    📙 前端知识精选集,包含 HTML、CSS、JavaScript、React、Node、安全等方面,每天仅需 30 秒。

    • 精选常见面试题,帮助您准备下一次面试
    • 精选常见交互,帮助您拥有简洁酷炫的站点
    • 精选有用的 React 片段,帮助你获取最佳实践
    • 精选常见代码集,帮助您提高打码效率
    • 整理前端界的最新资讯,邀您一同探索新世界
    488 引用 • 384 回帖 • 9 关注