前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java-JVM

Java-JVM

原创
作者头像
Quinlan
修改2021-05-24 10:24:55
1K0
修改2021-05-24 10:24:55
举报
文章被收录于专栏:用户8645177的专栏

JVM就是:

Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范。

它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

Java虚拟机包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收堆和一个存储方法域。

JVM屏蔽了与具体操作系统平台相关的信息,使Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。

JVM在执行字节码时,实际上最终还是把字节码解释成具体平台上的机器指令执行。

JVM内存模型
JVM内存模型

JVM包含两个子系统和两个组件

两个子系统为:Class loader(类装载)、Execution engine(执行引擎);

两个组件为:Runtime data area(运行时数据区)、Native Interface(本地接口)。

  1. Class loader(类加载器):根据给定的全限定名类名(如:java.lang.Object)来装载class文件到JVM内存的方法区
  2. Execution engine(执行引擎):执行classes中的指令。包含 垃圾回收器
  3. Native Interface(本地接口):与本地接口交互,是与其它编程语言交互的接口。
  4. Runtime data area(运行时数据区域):这就是我们常说的JVM的内存。

JVM的运行时数据区域(JVM内存)

Java 虚拟机在执行 Java 程序的过程中会把它所管理的内存区域划分为若干个不同的数据区域

这些区域都有各自的用途,以及创建和销毁的时间,有些区域随着虚拟机进程的启动而存在,有些区域则是依赖线程的启动和结束而建立和销毁。

不同虚拟机的运行时数据区可能略微有所不同,但都会遵从 Java 虚拟机规范, Java 虚拟机规范规定的区域分为以下 5 个部分:

  • 程序计数器(Program Counter Register):当前线程所执行的字节码的行号指示器,字节码解析器的工作是通过改变这个计数器的值,来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能,都需要依赖这个计数器来完成;
  • Java 虚拟机栈(Java Virtual Machine Stacks):用于存储局部变量表、操作数栈、动态链接、方法出口等信息;
  • 本地方法栈(Native Method Stack):与虚拟机栈的作用是一样的,只不过虚拟机栈是服务 Java 方法的,而本地方法栈是为虚拟机调用 Native 方法服务的;
  • Java 堆(Java Heap):Java 虚拟机中内存最大的一块,是被所有线程共享的,几乎所有的对象实例都在这里分配内存;
  • 方法区(Methed Area):用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。

JVM的作用

作用 :

当我们调用 Java 命令运行某个 Java 程序时,该命令将会启动一条 Java 虚拟机进程,

首先通过编译器把 Java 代码转换成字节码,在通过类加载器(ClassLoader)再把字节码加载到内存中的方法区,执行引擎将字节码翻译成底层系统指令,再交由 CPU 去执行,而交由 CPU 去执行这个过程中需要调用本地接口(Native Interface)来实现。

Java类加载的过程

类加载分为三步:加载、连接、初始化

加载:是将class文件读入内存并为之创建一个Class对象,存放在JVM的方法区。任何类被使用时系统都会建立一个Class对象。

连接

(1)检查:检查载入的class文件数据的正确性

(2)准备:给类的静态变量分配存储空间

(3)解析:将符号引用转成直接引用

初始化:

1)如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;

2)如果类中存在初始化语句,就依次执行这些初始化语句。

Java中创建对象有5中方式

使用new关键字

调用了构造函数

使用Class的newInstance方法

调用了构造函数

使用Constructor类的newInstance方法

调用了构造函数

使用clone方法

没有调用构造函数

使用反序列化

没有调用构造函数

Java对象创建的流程

虚拟机遇到一条new指令时,先检查常量池是否已经加载相应的类,如果没有,必须先执行相应的类加载。

类加载通过后,接下来分配内存。然后内存空间初始化操作,接着是做一些必要的对象设置,最后执行<init>方法。

为对象分配内存

类加载完成后,接着会在Java堆中划分一块内存分配给对象。内存的分配会根据Java堆是否规整,

有两种方式:

处理并发安全问题

对象的创建在虚拟机中是一个非常频繁的行为,哪怕只是修改一个指针所指向的位置,在并发情况下也是不安全的,可能出现正在给对象 A 分配内存,指针还没来得及修改,对象 B 又同时使用了原来的指针来分配内存的情况。解决这个问题有两种方案:

对象的访问定位

Java程序需要通过JVM栈上的引用访问堆中的具体对象。对象的访问方式取决于JVM虚拟机的实现。

目前主流的访问方式有句柄直接指针两种方式。

指针: 指向对象,代表一个对象在内存中的起始地址。

句柄: 可以理解为指向指针的指针,维护着对象的指针。句柄不直接指向对象,而是指向对象的指针(句柄不发生变化,指向固定内存地址),再由对象的指针指向对象的真实内存地址。

直接指针

如果使用直接指针访问,引用 中存储的直接就是对象地址,那么Java堆对象内部的布局中就必须考虑如何放置访问类型数据的相关信息。

优势:速度更,节省了一次指针定位的时间开销。由于对象的访问在Java中非常频繁,因此这类开销积少成多后也是非常可观的执行成本。HotSpot 中采用的就是这种方式。

句柄访问

Java堆中划分出一块内存来作为句柄池,引用中存储对象的句柄地址,而句柄中包含了对象实例数据对象类型数据各自的具体地址信息,具体构造如下图所示:

优势:引用中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中实例数据指针,而引用本身不需要修改。

深拷贝和浅拷贝

浅拷贝:增加一个指针指向一个已存在的内存地址

深拷贝:增加一个指针并申请一块新的内存,使这个增加的指针指向这个新的内存,

深拷贝和浅拷贝的区别就是在于会不会申请新的内存

在Java中对于基本数据类型就是深拷贝,对于引用类型区分深拷贝还是浅拷贝

如果在拷贝这个对象的时候,只对基本数据类型进行了拷贝,而对引用数据类型只是进行了引用的传递,而没有真实的创建一个新的对象,则认为是浅拷贝。

反之,在对引用数据类型进行拷贝的时候,创建了一个新的对象,并且复制其内的成员变量,则认为是深拷贝。

java中如何进行深拷贝,有两种方式

  1. 序列化(serialization)这个对象,再反序列化回来,就可以得到这个新的对象,无非就是序列化的规则需要我们自己来写。
  2. 继续利用 clone() 方法,既然 clone() 方法,是我们来重写的,实际上我们可以对其内的引用类型的变量,再进行一次 clone()。

Java 堆 和 Java 虚拟机栈 有什么区别

Java 堆 和 Java 虚拟机栈 有四个方面的区别:

物理地址的区别:

堆的物理地址分配对对象是不连续的,因此性能慢些。在GC的时候也要考虑到不连续的分配,所以有各种算法。

栈使用的是数据结构中的栈,先进后出的原则,物理地址分配是连续的。所以性能快。

内存大小的区别:

堆因为是不连续的,所以分配的内存是在运行期确认的,因此大小不固定。一般堆大小远远大于栈。

栈是连续的,所以分配的内存大小要在编译期就确认,大小是固定的。

存放内容不同:

堆存放的是对象的实例和数组。因此Java堆更关注的是数据的存储

栈存放:局部变量,操作数栈,返回结果。Java虚拟机栈更关注的是程序方法的执行。

程序的可见度的区别

堆对于整个应用程序都是共享、可见的。

栈只对于线程是可见的。所以也是线程私有。他的生命周期和线程相同。

队列和栈有什么区别

队列和栈都是被用来预存储数据的。

  • 操作的名称不同。队列的插入称为入队,队列的删除称为出队。栈的插入称为进栈,栈的删除称为出栈。
  • 可操作的方式不同。队列是在队尾入队,队头出队,即两边都可操作。而栈的进栈和出栈都是在栈顶进行的,无法对栈底直接进行操作。
  • 操作的方法不同。队列是先进先出(FIFO),即队列的修改是依先进先出的原则进行的。新来的成员总是加入队尾(不能从中间插入),每次离开的成员总是队列头上(不允许中途离队)。而栈为后进先出(LIFO),即每次删除(出栈)的总是当前栈中最新的元素,即最后插入(进栈)的元素,而最先插入的被放在栈的底部,要到最后才能删除。

目前主流的JVM有

  • HotSpot VM
  • J9 VM

HotSpot VM

HotSpot VM是绝对的主流。大家用它的时候很可能就没想过还有别的选择,或者是为了迁就依赖了Oracle/Sun JDK某些具体实现的烂代码而选择用HotSpot VM省点心。

Oracle / Sun JDK、OpenJDK的各种变种,用的都是相同核心的HotSpot VM。 从Java SE 7开始,HotSpot VM就是Java规范的“参考实现”。把它叫做“标准JVM”完全不为过。

当大家说起“Java性能如何如何”、“Java有多少种GC”、“JVM如何调优”云云,经常默认说的就是特指HotSpot VM。可见其“主流性”。

JDK8的HotSpot VM已经是以前的HotSpot VM与JRockit VM的合并版,也就是传说中的“HotRockit”,只是产品里名字还是叫HotSpot VM。

这个合并并不是要把JRockit的部分代码插进HotSpot里,而是把前者一些有价值的功能在后者里重新实现一遍。移除PermGen、Java Flight Recorder、jcmd等都属于合并项目的一部分。

不过要留意的是,这里我说的HotSpot VM特指“正常配置”版,而不包括“Zero / Shark”版。Wikipedia那个页面上把后者称为“Zero Port”。用这个版本的人应该相当少,很多时候它的release版都build不成功…

J9 VM

J9是IBM开发的一个高度模块化的JVM。

在许多平台上,IBM J9 VM都只能跟IBM产品一起使用。这不是技术限制,而是许可证限制。 例如说在Windows上IBM JDK不是免费公开的,而是要跟IBM其它产品一起捆绑发布的;使用IBM Rational、IBM WebSphere的话都有机会用到J9 VM(也可以自己选择配置使用别的Java SE JVM)。 根据许可证,这种捆绑在产品里的J9 VM不应该用于运行别的Java程序…大家有没有自己“偷偷的”拿来跑别的程序IBM也没力气管(咳咳

而在一些IBM的硬件平台上,很少客户是只买硬件不买配套软件的,IBM给一整套解决方案,里面可能就包括了IBM JDK。这样自然而然就用上了J9 VM。

所以J9 VM得算在主流里,虽然很少是大家主动选择的首选。

J9 VM的性能水平大致跟HotSpot VM是一个档次的。有时HotSpot快些,有时J9 快些。 不过J9 VM有一些HotSpot VM在JDK8还不支持的功能,最显著的一个就是J9支持AOT编译和更强大的class data sharing。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • JVM就是:
    • JVM包含两个子系统和两个组件
      • JVM的运行时数据区域(JVM内存)
        • Java类加载的过程
        • Java中创建对象有5中方式
        • Java对象创建的流程
        • 处理并发安全问题
        • 对象的访问定位
        • 深拷贝和浅拷贝
        • 深拷贝和浅拷贝的区别就是在于会不会申请新的内存
        • java中如何进行深拷贝,有两种方式
        • Java 堆 和 Java 虚拟机栈 有什么区别
        • 队列和栈有什么区别
        • HotSpot VM
        • J9 VM
    • JVM的作用
    • 目前主流的JVM有
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档