源码编译OpenJdk 8,Netbeans调试Java原子类在JVM中的实现(Ubuntu 16.04)

1、前言

前一阵子比较好奇,想看到底层(虚拟机、汇编)怎么实现的java 并发那块。html

volatile是在汇编里加了lock前缀,由于volatile能够经过查看JIT编译器的汇编代码来看。java

可是原子类,原本在jvm中就是汇编实现的,反而无法看。若是能实际跟踪一下断点,应该也算实际验证了。linux

 

这边基本参照下面文章来的,补充了不少让初学者头疼的细节,并拓展了一部分,c++

包括调试java 原子类在jvm中的实现的一些细节。git

https://marcin-chwedczuk.github.io/debugging-openjdk8-with-netbeans-on-ubuntugithub

 

源码编译OpenJDK8,主要有如下几个步骤:shell

  • 下载Ubuntu
  • 下载OpenJdk源码
  • 下载Boot  JDK,通常要比当前要编译的版本低
  • 安装必要的依赖
  • configure && make

上面几步搞完,基本虚拟机就可用了。但离调试,还有一点点距离。ubuntu

用NetBeans调试JVM代码,有如下几个步骤:缓存

  • 下载NetBeans
  • 配置OpenJdk工程
  • 配置Java工程
  • Debug OpenJdk(即虚拟机源码)

 

2、源码编译OpenJDK8

一、下载Ubuntu

我用的16.04,连接地址:https://www.ubuntu.com/download/alternative-downloadsbash

我是用vmvare装的,配置建议给高一点。

 

二、下载OpenJdk源码

据原文说法,OpenJDK 使用Mercurial进行版本管理。另一个名叫AdoptOpenJDK project.提供了OpenJDK的镜像,可让咱们用git下载。

站点的官网以下:https://adoptopenjdk.net/about.html 

主页上说他们的目标就是:

Provide a reliable source of OpenJDK binaries for all platforms, for the long term future.

据个人使用体验来讲,以前编译过一次OpenJDK,各类报错,各类改源码才能编译经过。此次确实编译很顺,代码一句没改。

看起来,代码仍是比较可靠的。

不扯别的了,直接clone搞下来吧,我这边是直接在/home/ckl目录下执行的shell:

git clone --depth 1 -b master https://github.com/AdoptOpenJDK/openjdk-jdk8u.git

 

三、下载Boot JDK

编译过jdk的同窗应该知道,咱们得先有只母鸡才能编译openJDK源码。

这边我用的oracle的jdk 1.7,这边贴个csdn的下载连接,我那天弄的时候官网速度太慢。

https://download.csdn.net/download/qq_33499492/10288883

 

怎么安装就不说了,我解压后放在/usr/local

记得修改环境变量(/ect/profile):

export JAVA_HOME=/usr/local/jdk1.7.0_80
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH

而后,source /ect/profile 使之生效。

 

四、安装依赖

sudo apt install \
        libx11-dev \
        libxext-dev \
        libxrender-dev \
        libxtst-dev \
        libxt-dev \
        libcups2-dev \
        libfreetype6-dev \
        libasound2-dev

 

这个依赖不够,我这边装的时候,还报了一些依赖缺失,直接安装报错提示里的执行命令下载就完了。

我这里遇到比较坑的一点是(固然我对ubuntu彻底不熟),一开始用的是官方的repository 源,后来换成阿里云的,各类报错。

吓得我赶忙改回来了,就没问题了。

这里遇到问题能够咨询我。

 

五、配置脚本

在/home/ckl/openjdk-jdk8u下,新建脚本build.sh:

build.sh:

bash ./configure --with-target-bits=64 --with-boot-jdk=/usr/local/jdk1.7.0_80/ --with-debug-level=slowdebug --enable-debug-symbols ZIP_DEBUGINFO_FILES=0
make all ZIP_DEBUGINFO_FILES=0

给build.sh增长可执行权限并执行:

chmod +x build.sh
./build.sh

 

六、编译成功的效果

 

 切换到对应目录下:

/home/ckl/openjdk-jdk8u/build/linux-x86_64-normal-server-slowdebug/jdk/bin
./java -version

 

在该目录下,新建个HelloWorld来运行一下:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("hello world");
    }
}

 

 

3、Netbeans调试JVM

一、下载NetBeans 8.2

下载安装主要参考:

https://netbeans.org/community/releases/82/install.html

 

NetBeans主页上,最新版本出到11.0了,可是在网上看到都是用的NetBeans 8开头版本的,有时间再折腾吧。

我这里下载的是8.2,连接:

https://netbeans.org/downloads/8.2/

由于OpenJDK是c++写的,因此咱们必须选择带C/C++支持的,我这里直接选All。

另外,注意选linux平台,最好选英语,省得出幺蛾子。

 

二、下载oracle jdk 1.8

为啥又要下载jdk? 这个jdk不是编译openJdk源码用的那个,这个是运行NetBeans 8.2须要。

The Java SE Development Kit (JDK) 8 is required to install NetBeans IDE.

下完安装后,把环境变量里设的jdk路径改掉吧:

export JAVA_HOME=/usr/local/jdk1.8.0_211
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH
source /etc/profile

 

三、安装net beans

 

 安装:

./netbeans

 

安装过程,记得不要所有默认,能够本身定制化:

 

 

 

四、配置Open JDK工程

安装完成后,在桌面上,会生成一个图标,直接双击启动。

后面的步骤,能够参考:

https://marcin-chwedczuk.github.io/debugging-openjdk8-with-netbeans-on-ubuntu

 

 

 

 

 

 

而后接下来几步均可以直接next,最后点finish会开始configure。

 

五、运行HelloWorld

配置咱们的openjdk工程去运行以前写的HelloWorld程序。

 

对着openjdk-jdk8u工程,右键选择properties:

 

选择编译出的jdk来运行咱们的class:

 

 

 

应该能看到输出Hello World了。

 

六、调试Hello World

System.out.println(...)会调用jdk/src/share/native/java/io/io_util.c的writeBytes

直接在该函数打个断点,而后debug

 

不出意外,应该会停到该断点。

 

七、在netbeans中新建java工程,并调试jvm

咱们来调一个有用点的程序,看看原子类在jvm中的实现究竟是不是像网上的博客那样运行的。

 

先像上面这样,建个java工程,写点代码,而后运行。

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;


/**
 *
 * @author ckl
 */
public class TestSample {
    private static AtomicInteger stop = new AtomicInteger(12);
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        
        Boolean result = stop.compareAndSet(1314, 1413);
        // TODO code application logic here
        if (result){
            System.out.println(" true result ");
        }else {
            System.out.println("false result");
        }
    }
    
}

 

 

 调试jvm:

对着openjdk-jdk8u工程点右键,properties。

 

创建断点,cas调用通常会调用unsafe.cpp的如下代码:

 

断点ok了,而后点选中openjdk-jdk8u工程后,点击debug按钮:

 

 果真,程序立刻就停在这了。可是,cas操做可能在不少地方都调用了,因此咱们要仔细观察Variables窗口,看看是否是咱们发起的那个调用:

 

跳过了十屡次之后。。。

。。。

 

 稍微跟一下:

直接进到这段汇编了,用了cmpxchg指令来实现cas,还加了lock前缀(mp为1)。lock前缀下次讲。主要是锁总线,或者锁缓存,达到原子操做的目的。

 

有问题欢迎留言,若是有问题,也能够加我微信交流。

相关文章
相关标签/搜索