之前看了一本书《深入理解Java虚拟机》,里面将的主要就是关于Java虚拟机的发展史,Java虚拟机的主要功能介绍,还有其他的关于Java虚拟机底层的东西。
对于Java开发者来说,现在最新的JDK版本应该是JDK12,我们都知道在Java语言是夸平台的,这里的夸平台并不是指Java这个编程语言的夸平台,而是指JVM的夸平台特性。我们在不同的环境上可以使用不同的虚拟机来实现不同的平台上运行Java程序,这个也就造就了Java语言的跨平台特性。从Java语言诞生以来就有很多的关于Java虚拟机的变化,现在就来分享一下关于Java虚拟机的知识点
JVM 版本查看
我们都知道,在完成Java环境变量的配置之后我们会使用一个命令
java -version
用这个命令来查看我们Java的版本号。通过这命令可以得到关于Java运行环境和Java虚拟机的很多的知识点。通过这个部分的了解可能会学到之前没有了解过的东西,可能会对Java虚拟机技术产生很大的兴趣
Let’s go
首先 打开cmd 输入 java -version
当我执行完这个命令之后会出现图中所表示的东西。那么这些东西都表示什么意思呢?怎么去改变这些东西呢?
- 第一行:表示现在机器上使用的JDK的版本号是多少
- 第二行:表示现在所用的机器上的JRE版本号以及小版本号是多少
- 第三行:表示所使用的Java虚拟机是什么,例如图中表示的就是使用的Hotspot虚拟机,64位的,并且使用的是ServerVM也指定了对应的build的版本号。
其中呢第三行是比较重要的下面就来带着大家来看一下。在JDK中的D:\jdk1.8\jre\lib\amd64下面有一个jvm.cfg文件。我们打开不同版本的JDK中的文件来看一下
JDK1.6
可以看到在JDK1.6中的路径是 D:\jdk1.6\jre\lib\i386 ,其中文件内容如下
#
# %W% %E%
#
# Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
# ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
#
#
#
#
# List of JVMs that can be used as an option to java, javac, etc.
# Order is important -- first in this list is the default JVM.
# NOTE that this both this file and its format are UNSUPPORTED and
# WILL GO AWAY in a future release.
#
# You may also select a JVM in an arbitrary location with the
# "-XXaltjvm=<jvm_dir>" option, but that too is unsupported
# and may not be available in a future release.
#
-client KNOWN 表示用客户端模式启动
-server KNOWN 表示用服务器端模式启动
-hotspot ALIASED_TO -client
-classic WARN
-native ERROR
-green ERROR
可以看到下面这个选项是作为一个JVM的参数进行操作的,而这里默认加载的第一个参数这个我们可以通过命令的方式进行查看,如下图
可以看到第三行的参数是通过Client VM的方式启动的。这就是说默认加载了第一个参数,而这个参数的加载是那个在第一个位置就先加载那个。所以说我们可以通过调整参数的上下位置决定是加载ClientVM还是ServerVM。对于这两个东西的区别在下面会详细分析到。
JDK1.7
文件路径 D:\jdk1.7\jre\lib\i386 文件内容如下
# Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
# ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
#
# List of JVMs that can be used as an option to java, javac, etc.
# Order is important -- first in this list is the default JVM.
# NOTE that this both this file and its format are UNSUPPORTED and
# WILL GO AWAY in a future release.
#
# You may also select a JVM in an arbitrary location with the
# "-XXaltjvm=<jvm_dir>" option, but that too is unsupported
# and may not be available in a future release.
#
-client KNOWN 表示用客户端模式启动
-server KNOWN 表示用服务端模式启动
-hotspot ALIASED_TO -client
-classic WARN
-native ERROR
-green ERROR
可以看到在JDK1.7里面和JDK1.6的设置并没有太大的变化。所以说配置文件的内容也没有太大的变化。
这里再提供一个 64位的JDK1.7的路径 D:\jdk1.7\jre\lib\amd64 ,对于64位的配置文件来书和32位的并没有什么太大的区别
JDK1.8
文件路径 D:\jdk1.8\jre\lib\amd64 文件内容如下
#
#
#
# Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
# ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
# List of JVMs that can be used as an option to java, javac, etc.
# Order is important -- first in this list is the default JVM.
# NOTE that this both this file and its format are UNSUPPORTED and
# WILL GO AWAY in a future release.
#
# You may also select a JVM in an arbitrary location with the
# "-XXaltjvm=<jvm_dir>" option, but that too is unsupported
# and may not be available in a future release.
#
-server KNOWN 表示用服务器端模式启动
-client IGNORE 表示用客户端模式启动
通过上面的内容,可以看到从JDK1.8开始jvm.cfg的内容发生了很大的变化,对于客户端模式的后面的标注也成了 ignore(忽略),也就意味Java8 相比较于之前的Java 版本来说在性能上有了很大的提升。
但是可以看到无论怎么变化都使用的是Hotspot虚拟机
而对于HotspotVM来说 SunJDK和OpenJDK都是用这个虚拟机。Hotspot虚拟机继承了Sun公司的其他的虚拟机的有点,拓展出了自己的很多的独特的优势,例如正如他的名字一样Hotspot(热点),它的意思就是指其中使用的热点代码探测技术。HotSpot热点探测技术可以通过执行计数器找出最具有编译价值的代码然后通知JIT编译器以方法为单位进行编译,如果其中一个方法被频繁的调用,或者方法循环的有效次数过多,将会触发标准编译和OSR(栈上替换)编译动作,通过编译器和解释器的协同工作,可以在最优的时间和最佳的性能上获得平衡。而在JDK8中整合和JRockitVM和HotSpotVM两大虚拟机的优势互补,从虚拟机上直接提升了性能以及垃圾回收GC。
对于之前我们选择使用ClientVM或者使用ServerVM来说就是对于HotSpotVM的启动方式进行指定,当然了对于我们一般的开发来说两者都是可以的,但是对于大型应用开发来说ServerVM要比ClientVM的性能更好。
补充
对于i386和amd64的区别
i386=Intel 80386。其实i386通常被用来作为对Intel(英特尔)32位微处理器的统称。
AMD64,又称“x86-64”或“x64”,是一种64位元的电脑处理器架构。它是建基于现有32位元的x86架构,由AMD公司所开发。
i386 简单理解就是是32位的amd64 是64位的版本,因为是amd把64位率先引进桌面系统的,英特尔也是要追随amd并且保持兼容,一般在软件包里包含这样的字符
JVM 内存介绍
首先需要了解的一个概念就是 运行时数据区域
对于一个Java虚拟机在执行Java程序的时候会把它管理的内存区域分成若干个不同的数据区域,对于这些数据区域来说各自有各自的用途。下面就来看看Java运行时的数据区域有哪些。
程序计数器
程序计数器(Program Counter Register)是一块比较小的内存空间,可以看做是当前线程所有执行的字节码的行号指示器,字节码解释器工作的时候就是要通过改变这个计数器的值来选择下一条指令。所有的分支、跳转、循环、异常处理都是要通过这个计数器来完成。
Java虚拟机栈
Java虚拟机栈(Java Virtual Machine Stacks)作为线程私有的部分,它的生命周期和线程是相同的,在Java程序运行的时候每一个方法都会在虚拟机栈中创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用到完成,就是一个入栈出栈的过程。
本地方法栈
本地方法栈(Native Method Stack)与虚拟机栈之间的区别就是虚拟机栈为虚拟机执行的Java方法服务,本地方法栈则是为虚拟机使用的Native方法服务。
Java堆
Java 堆(JavaHeap)作为Java虚拟机管理的一个比较大的一块内存。是被所有的线程共享的一块内存,在Java虚拟机启动的时堆内存存在的目的就是为了存放所有的对象的实例。几乎所有的对象实例都在这个地方被分配内存。当然Java堆也是垃圾回收的最主要的一个区域,所以也被称为是GC堆也会有很多的垃圾分类算法出现,这个以后在做详细的介绍
方法区
方法区(Method Area)和堆一样都是所有线程共享的,用于存储虚拟机加载的类信息、常量、静态变量、及时编译器编译的代码的数据。在Hotspot虚拟机上很多的人把方法区称为是永久代,其实这个说法是不准确地,只是因为Hotspot虚拟机把GC分代收集扩展到了方法区而已,或者说使用永久代来实现方法区而已,二者本质上是不等价的。
运行时常量池
运行时常量池(Runtime Constant Pool)作为方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息之外,可能还会有常量池的信息,存放了很多的符号引用等等的,这些数据都是存放在运行时常量池中。
直接内存
直接内存(Direct Memory)并不是作为虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存会被频繁的调用,也会导致OutOFMemoryError异常的出现。在JDK1.4中加入了NIO的概念,引入了通道channel和缓冲区buffer的IO方式。可以使用Native函数直接分配堆外的内存,然后通过DirectByteBuffer直接对于这块内存进行操作。减少了在Java堆中和Native堆中复制数据的操作,提高的IO的性能。
总结
作为一个虚拟机来说,就跟使用虚拟机工具在本地搭建服务器一样,我们可以使用不同的虚拟机工具使得虚拟机环境在不同的机器上运行而不影响我们虚拟机内部的东西,这个就是Java跨平台技术的实质。其实无论在哪个平台上都是通过类加载机制将Class文件变为想要的程序结果,只是实现的虚拟机类型不同,有些是在Linux上有些是在Windows上。但是最终还是逃不开JVM。