深入理解Java虚拟机(JVM高级特性与最佳实践)读后感

深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)


前言

本文主要用于自我学习第3版深入理解Java虚拟机 周志明著这本书的辅助理解和读后感,主要用于自我学习和复盘。
jvm如何实现Java跨平台。

在这里插入图片描述

一、自己编译JDK

1.1实验系统环境

图一 实验系统环境

1.2获取源码

网址:openjdk12源码查看
openjdk12源码zip格式下载

1.3构建编译环境

表1:openJdk编译依赖库

工具 库名称 安装命令
FreeType The FreeType Project sudo apt-get install libfreetype6-dev
CUPS Common UNIX Printing System sudo apt-get install libcups2-dev
X11 X Window System sudo apt-get install libx11-dev libxext-dev libxrender-dev libxrandr-dev libxtst-dev libxt-dev
ALSA Advanced Linux Sound Architecture sudo apt-get install libasound2-dev
libffi Portable Foreign Function Interface Library sudo apt-get install libffi-dev
Autoconf Extensible Package of Macros sudo apt-get install autoconf

1.4安装"BootStrap JDK"

假设要编译大版本号为N的JDK,我们还需要另外准备一个大版本号至少为N-1的已经编译好的JDK,这是因为OpenJDK由多个部分(HotSpot、JDK类库、JAXWS、JAXP~~~~~~)构成,其中一部分(HotSpot)代码使用C、C++编写,而更多的代码则是使用Java语言来实现,因此编译这些Java代码就需要用另一个编译器可用的JDK,官方称这个JDK为"BootStrap JDK"。
因此编译OpenJDK12时必须使用JDK11及之后的版本。在Ubuntu下安装OpenJDK11:

sudo apt-get install openjdk-11-jdk

1.5进行编译

1.5.1编译前准备

图二
如上图所示为由链接 :openjdk12源码zip格式下载
或者根据下图点击网页源码OpenJDK12的zip按钮下载
点击网页源码OpenJDK12的zip按钮下载
注意:代码的大小为180.3MB左右,确保下载完整。
在这里插入图片描述

下载的代码。现在将其进行解压,解压命令为:

unzip jdk12-06222165c35f.zip;

解压之后大约675.6MB左右。
在这里插入图片描述

解压之后将当前解压文件移动到/home下进行编译

sudo mv ./jdk12-06222165c35f /home/;

进入编译工作目录等待:

cd /home/jdk12-06222165c35f/;
ls;

在这里插入图片描述

1.5.2了解OpenJDK编译参数

表二、OpenJDK提供范瑞编译参数表

编译参数 参数解析
- -with-debug-level=<level> 1.用来设置编译级别 2.可选值:release、fastdebug、slowdebug. 3.可选值越往后进行的优化措施越少,带的调试信息越多. 4.还有一些虚拟机调试参数必须在特定模式下使用. 5.默认值为:release
- -enable-debug 等效于- -with-debug-level=fastdebug
- -with-native-debug-symbols=<method> 确定调试符号信息的编译方式,可选值为none、internal、external、zipped.
- -with-version-string=<string> 设置编译JDK的版本号,如java -version的输出就会显示该信息。这个参数还有- -with-version-<part>=<value>的形式,其中part可以是pre、opt、build、major、minor、security、patch之一,用于设置版本号的某一个部分。
- -with-jvm-variants=<variant>[,<variant>…] 编译特定模式(Variants)的HotSpot虚拟机,可以多个模式并存,可选值为:server、client、minimal、core、zero、custom.
- -with-jvm-features=<feature>[,<feature>…] 针对**- -with-jvm-variants=custom时的自定义虚拟机特性列表(Features),可以多个特性并存,由于可选值较多,请参见help**命令输出.
–with-target-bits=<bits> 指明要编译32位还是64位的Java虚拟机,在64位机器上也可以通过交叉编译生成32位的虚拟机.
- -with-<lib>=<path> 用于指明依赖包的具体路径,通常使用在安装了多个不同版本的BootStrap JDK和依赖包的情况。其中lib的可选值包括boot-jdk、freetype、cups、x、alsa、libffi、jtreg、libjpeg、giflib、libpng、lcms、zlib
- -with-extra-<flagtype>=<flags> 用于设定C、C++Java代码编译时的额外编译器参数,其中flagtype可选值为cflags、cxxflags、laflags分别代表C、C++Java代码的参数。
- -with-conf-name=<name> 指定编译配置名称,OpenJDK支持使用不同的配置进行编译,默认会根据编译的操作系统、指令集架构、调试级别自动生成一个配置名称,比如"linux-x86_64-server-release",如果这些信息都相同的情况下保存不同的编译参数配置,就需要使用这个参数来自定义配置名称。
bash configure --help 该命令可以查看全部configure命令参数,所有参数均通过以下形式使用:bash configure [options]

bash configure --help附图

1.5.3正式编译

1.5.3.1依赖项检查、参数配置和构建输出目录结构等

编译FastDebug版、仅含Server模式的HotSpot虚拟机:

bash configure --enable-debug --with-jvm-variants=server --disable-warnings-as-errors;

注:configure命令承担了依赖项检查、参数配置和构建输出目录结构等多项职责,如果configure执行过程中有需要的工具链或者依赖项有缺失,命令执行后将会得到明确的提示,并给出依赖的安装命令
如果一切顺利的话就会收到配置成功的提示,并输出调试级别、Java虚拟机的模式、特性,使用的编译器版本等配置摘要信息,如下图所示:
在这里插入图片描述

1.5.3.2执行整个OpenJDK编译

make images;

在这里插入图片描述
注:如果多次编译,或者目录结构成功后又再次修改了配置,必须先使用

make clean;
make dist-clean;

命令清理目录,才能确保新的配置生效。
编译完成后,进入OpenJDK源码的"build/配置名称/jdk"目录下就可以看到OpenJDK的完整编译结果了,把它复制到JAVA_HOME目录,就可以作为一个完整的JDK来使用,如果没有人为设置过JDK开发版本的话,这个JDK的开发版本号里默认会带上编译的机器名,如下图所示:
在这里插入图片描述

1.6编译成果检验

编译test.java文件:

vim test.java;

按键盘a字母键,将以下java代码粘贴进入文件内。

public class test{
     public static void main(String[] args){
         System.out.println("Hello World!");
     }
}

按Esc键,输入以下bash命令,保存并退出文件。

:wq!

输入命令:

./javac test.java;
./java  test;

详情见下图:
在这里插入图片描述

本章小结

本章主要工作是如何自己编译OpenJDK12,本章为后续章节的实验搭建了环境。

二、Java内存区域与内存溢出

章节导读

本章属于第二部分-自动内存管理的内容。 对于从事C、C++程序开发的开发人员来说,在内存管理领域,他们既是拥有最高权利的“emperor”,又是从事最基础工作的劳动人民- -既拥有每一个对象的“所有权”,又担负着每一个对象生命从开始到终结的维护责任。对于Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每一个new 操作去写配对的delete/free代码,不容易出现内存泄露和溢出方面的问题。正是因为Java程序员把控制内存的权利交给了Java虚拟机,一旦出现内存泄漏和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那排查错误、修正问题将会成为一项异常艰难的工作。

2.1运行时数据区域

2.1.1程序计数器

程序计数器(Program Counter Register):
  1.是一块较小的内存空间,可被看作是当前线程所执行字节码的行号指示器
  2.字节码解释器通过改变这个计数器的值选取下一条要执行的字节码指令
  3.它是程序控制流的指示器分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器完成。
  4.由于Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的。(听起来好像操作系统的时间片轮转),在任何一个时刻,处理器(多核处理器)的一个内核都会执行一条线程中的指令。为了线程切换后能恢复到正确的执行位置。每条线程都需要有一个独立的程序计数器,各条线程之间互不影响,独立存储,我们称这类内存区域为“线程私有”的内存
  5.如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是本地(Native)方法,这个计数器值则为空(Undefined)

2.1.2Java虚拟机栈

  1.与程序计数器一样,Java虚拟机栈(Java Virtual Machine Stack)也是线程私有的,它的生命周期与线程相同
  2.虚拟机栈描述的是Java方法执行的线程内存模型,每个方法被执行的时候,Java虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息
在这里插入图片描述
  3.每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程
  4.局部变量表存放了编译器可知的各种Java虚拟机基本数据类型对象引用和returnAddress类型
  5.这些数据类型在局部变量表中的存储空间以局部变量槽(Slot)来表示,其中64位长度的long和double类型的数据会占用两个变量槽,其余的数据类型只占用一个。
在这里插入图片描述

2.1.3本地方法栈

本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别只是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的本地(Native)方法服务
注:JAVA虚拟机栈和本地方法栈都规定了两种异常情况:
StackOverflowError异常:线程请求的栈深度大于虚拟机所允许的深度.
OutofMemoryError异常: Java虚拟机栈/本地方法栈容量可以动态扩展的情况下,当栈扩展无法申请到足够的内存会抛出内存溢出异常

2.1.4Java堆

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>