当需要对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