JDK源码编译

当需要对jdk的功能进行定制,或者需要修改jdk的源码时,需要编译出jdk,这里以jdk8为例
在linux服务器上编译源码需要有一定的服务器操作经验。
x86架构服务器的源码比较容易编译出来,参考https://zhuanlan.zhihu.com/p/206732661这篇文章编译即可
也可以到github上搜索jdk,根据官方的说明文档进行编译,大致步骤如下:

yum install libXtst-devel libXt-devel libXrender-devel cups-devel alsa-lib-devel
yum install hg
hg clone http://hg.openjdk.java.net/jdk8/jdk8 openjdk8
cd openjdk8 && ./get_source.sh
make all

等待编译完成即可,编译完成后生成的jdk在openjdk8/build/目录下。或者输入find . -name java 进行搜索即可找到。

arm64/aarch64架构的服务器编译,这里推荐一下bishengjdk(相当于华为对openjdk的定制版),编译步骤如下:

yum install libXtst-devel libXt-devel libXrender-devel cups-devel alsa-lib-devel
git clone https://gitee.com/openeuler/bishengjdk-8.git -b aarch64-shenandoah-jdk8u282-b08
cd bishengjdk-8
chmod +x ./configure
./configure
make all

等待编译完成即可,编译完成后生成的jdk在bishengjdk-8/build/目录下。

jdk的源码目录在bishengjdk8/jdk/src/share/classes/目录下。
我曾经在优化HashMap在hash冲突严重场景下的性能,尝试修改过String类的hashCode()方法,试图修改其使用的hash算法,譬如修改为返回一个固定的整数,结果编译出来的jdk在初始化jvm会报错,不支持utf-8字符集。原因是JVM的SPEC对String的hashcode算法做了规定,不允许使用其他算法(String的hashCode()使用的算法可以在API文档中查看)。
在大佬的指点下在String类中新增了一个hash2属性和hashCode2方法(采用c语言中一种高效的hash算法),同时将HashMap中的hash()方法改为hashCode2()。经过测试,发现在hash冲突严重的情况下,HashMap的get()方法性能提升了将近一倍。
在String类中添加以下代码:

private int hash2;
public int hashCode2(){
	int h = hash2;
	if(h == 0 && this.value.length > 0){
		//高效的hash算法实现,在这里不提供具体代码
		//...
		this.hash2 = h;
	}
	return h;
}

将HashMap的hash()方法修改如下:

static final int hash(Object key) {
	int h;
	if(key instanceof String){
		return (key == null) ? 0 : (h = ((String)key).hashCode2()) ^ (h >>> 16);
	}
	return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

根据上文的编译步骤编译jdk,然后编写如下测试代码:

import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Random;

public class Test {
    public static void main(String[] args) throws Exception {
        write();
        HashMap<String, String> map = init();
        viewMap(map);
        test(map);
    }

    /**
     * 测试map.get()方法的性能
     *
     * @param map
     */
    public static void test(HashMap<String, String> map) {
        String key1 = null;
        String key2 = null;
        String key3 = null;
        int i = 0;
        for (String key : map.keySet()) {
            if (i > 3) {
                break;
            } else if (i == 1) {
                key1 = key;
            } else if (i == 2) {
                key2 = key;
            } else if (i == 3) {
                key3 = key;
            }
            i++;
        }
        System.out.println("Running ... ,map.size():" + map.size());
        long time1 = System.currentTimeMillis();
        for (int j = 0; j < 5000000; j++) {
            String val = map.get(key1);
            val = map.get(key2);
            val = map.get(key3);
            val = map.get(key1);
            val = map.get(key2);
            val = map.get(key3);
        }
        long time2 = System.currentTimeMillis();
        System.out.println("Test cost time:" + (time2 - time1) + "ms");
    }

    /**
     * 构造内置数组长度为16384的map数据,其中包含大量的红黑树和少量的链表,map元素的数量必须小于16384*0.75
     */
    public static void write() {
        File f = new File("D:\\data.txt");
        if (f.exists()) {
            System.out.println("File exists!");
            return;
        }

        System.out.println("Start write...");
        try (FileWriter fw = new FileWriter(f);
             BufferedWriter bw = new BufferedWriter(fw);
        ) {
            int num = 100000000;
            int size = 0;
            for (int i = 0; i < 16384; i++) {//构造内置数组长度为16384长度的map数据
                List<Integer> list = new ArrayList<>();
                for (int j = 0; j < 100000; j++) {
                    num++;
                    //下面这三行代码是HashMap中决定元素存在数组中索引位置的算法
                    int hashcode = (num + "").hashCode();
                    int hash = hashcode ^ hashcode >>> 16;
                    int index = hash & (16384 - 1);
                    if (index == i) {
                        list.add(num);
                    }
                }
                if (size + list.size() > 12286) { //文本内容不能超过16384*0.75=16384,否则会导致map扩容
                    break;
                }
                Random r = new Random();
                if (list.size() > 8) { //这部分会变成红黑树
                    if (r.nextInt(100) < 40) {
                        for (Integer n : list) {
                            bw.write(n + "");
                            bw.newLine();
                            size++;
                        }
                    }
                } else {//这部分是普通链表
                    if (r.nextInt(100) < 2) {
                        for (Integer n : list) {
                            bw.write(n + "");
                            bw.newLine();
                            size++;
                        }
                    }
                }
            }
            bw.write(999);
            bw.close();
            fw.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 从文本中读取构造好的数据到map中并返回
     *
     * @return
     */
    public static HashMap<String, String> init() {
        System.out.println("Start init...");
        HashMap<String, String> map = new HashMap<>();
        try (FileReader fr = new FileReader("D:\\data.txt");
             BufferedReader br = new BufferedReader(fr);
        ) {
            while (true) {
                String line = br.readLine();
                if (line == null) {
                    break;
                }
                map.put(line, line);
            }
            br.close();
            fr.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return map;
    }

    /**
     * 通过反射技术查看map的内部结构,主要查看map.size(),内置数组table的长度,以及数组内链表和红黑树的数量。
     * HashMap底层实际是一个数组,每个数组元素可能是null、链表或红黑树,每个被添加到HashMap的元素都会成为其中的链表或红黑树的节点。
     *
     * @param map
     * @throws Exception
     */
    public static void viewMap(HashMap<String, String> map) throws Exception {
        Class hashMap = map.getClass();
        Field table = hashMap.getDeclaredField("table");
        table.setAccessible(true);//非public属性必须设置访问权限为true
        Object tables = table.get(map);
        int tableLength = Array.getLength(tables);

        int linkedListSize = 0;//用于统计map内置数组table中的链表数量
        int treeNodeSize = 0;//用于统计map内置数组table中的红黑树数量
        for (int i = 0; i < tableLength; i++) {
            Object obj = Array.get(tables, i);
            if (obj != null) {
                String className = obj.getClass().getName();
                if ("java.util.HashMap$Node".equals(className)) {//链表的类型为HashMap$Node
                    linkedListSize++;
                } else {//红黑树的类型为HashMap$TreeNode
                    treeNodeSize++;
                }
            }
        }

        System.out.println("map size:" + map.size());
        System.out.println("table length:" + tableLength);
        System.out.println("linkedList count:" + linkedListSize);
        System.out.println("treeNode count:" + linkedListSize);
    }

}

使用修改后的jdk和原始jdk分别运行测试代码进行对比即可得到测试结果。例如:

File exists!
Start init...
map size:12282
table length:16384
linkedList count:304
treeNode count:304
Running ... ,map.size():12282
Test cost time:299ms

猜你喜欢

转载自blog.csdn.net/qq_36779082/article/details/121151322