神仙打架?Rust大战JDK,卷死

432 阅读14分钟

神仙打架?Rust大战JDK,卷死

前言

背景

JYM好,我是退居互联网十八线攻城狮贤哥。

今天我们讲讲Java和Rust的性能问题。

\

话说最近Rust和Go如今纷纷正式入场,在互联网圈卷起来了。

疫情压力下,每天都会有很多Java er在纠结,要不要转语言换个赛道啊?

\

如果转语言的话,Go好还是Rust好?

Rust这种媲美C++的性能核武器登场,Java和Go是不是药丸?

\

我无法回答这些问题,但是接下来有时间我会出专题来测评各语言,JYM都来讨论一下,能为你提供一点片面的参考。

至于今天,我们主要是将一下Java和Rust的性能,没耐心的JY可以直接看 三个名为“结果” 的小节。

亿点点说明

  1. 发这篇文不是为了喷Rust或者Java,抛砖引玉,请大家正确食用。

  2. 代码尽量采用一样的结构实现,但我水平有限,实在难免存在

    不公平,哪位兄弟有更公平的测试代码的,希望可以贴出来与诸君共赏

  1. 文中有部分代码是用Kotlin写的,Kotlin与Groovy,Scala,Java一样是JDK语言,编译之后是Java字节码文件,同样是以JVM运行。

首轮测试,搞起

测试环境

  • Windows 10

  • 锐龙 3700x

  • rust1.60

  • openjdk 11

废话少说,上代码

以下是Kotlin实现。

需要注意JDK的数组是对象,并非纯栈值,严谨的说,数据大部分是保存在堆上的。

另外,我的CPU是8核的,所以代码里我都用的16线程,将CPU跑到100%,避免单线程调度产生的误差。

自己测试的话,建议修改线程数量。


import java.util.stream.Collectors

fun get_time(): Long {

    println(System.currentTimeMillis());

    return System.currentTimeMillis();

}

fun calc(iter:Int): Array<Int> {

    var  arr=Array(5001){0}

    var  arr2=Array(5001){0}

    for(item in 1..5000) {

        arr[item-1]=item +iter

    }


    for(item in 1..5000){

    var  sum=0;

    for(sub_item in 1..5000) {

        sum+=arr[sub_item]

    }

    arr2[item-1]=sum

    }

    return arr2

}

fun main() {

    val t=get_time()

    val a=ArrayList<Thread>();

    for (item in 1..16)

    {

        val t=Thread{->

            run {

                var sum=0

                var i=0

                var o=0

                while (true) {

                    val a = calc(t.toInt())

                    i += 1;

                    i = i % 500;

                    if (i == 1) {

                        o = o + 1;

                    }

                    if (o == 10) {

                        break

                    }

                    sum+=a[0]

                }

            }

        }

        t.start()

        a.add(t)

    }

    a.forEach{

        it.join()

    }

    println((get_time()-t)/1000)

    return

}


以下是Rust实现

fn get_time() -> u64 {

    let start = SystemTime::now();

    let since_the_epoch = start

        .duration_since(UNIX_EPOCH)

        .expect("Time went backwards");

    return since_the_epoch.as_secs();

}


fn calc(iter: i32) -> [i32; 2] {

    let mut arr = [0; 5000];

    let mut arr2 = [0; 5000];

    for item in 1..5000usize {

        arr[item] = iter;

    }

    for mut item in 1..5000 {

        let mut sum = 0;

        for sub_item in 1..5000 {

            sum += arr[sub_item] % 5000

        }

        arr2[item - 1] = sum

    }

    return [arr2[0],arr2[1]]

}

fn main() {

    let t = get_time() as i64;

    let ti32=t.clone() as i32;

    let mut b: [Option<JoinHandle<()>>; 16] = Default::default();

    let mut a = 0;

    for i in 0..16 {

        let h = thread::spawn(move || {

            let mut sum = 0i32;

            let mut i = 0;

            let mut o = 0;

            while true {

                let a = calc(ti32);

                i += 1;

                i = i % 500;

                if i == 1 {

                    o = o + 1;

                }

                if o == 10 {

                    break;

                }

                sum += a[0];

            }

            println!("log{}",sum)

        });

        b[i] = Option::Some(h);

    }

    for i in b {

        i.unwrap().join().expect("a")

    }

    println!("{}",get_time() as i64-t);

    return;

}

结果

|Java  |  Rust |

| ---- | ----- |

|77s   |  101s |

卧槽,JDK不讲武德,来骗,来偷袭Rust老同志?

JDK结果比rust还快25% ??

第二/三波测试

我怀疑是不是AMD对rust优化不好

或者AMD对Java优化太好。

或者是我的算法刚好命中Java的优势

所以我把JDK版本,处理器,系统的环境,换一下,又测试了一波

测试环境

  • Windows 11

  • 酷睿i3 10100

  • graalvm jdk 1.8

  • rust 1.6

废话少说,上代码

\


\


use std::thread;

\


use std::time::{SystemTime, UNIX_EPOCH};

\


fn timestamp1() -> i64 {

    let start = SystemTime::now();

    let since_the_epoch = start

        .duration_since(UNIX_EPOCH)

        .expect("Time went backwards");

    let ms = since_the_epoch.as_secs() as i64 * 1000i64 + (since_the_epoch.subsec_nanos() as f64 / 1_000_000.0) as i64;

    ms

}

\


fn calc(f: &i32) -> i32

{

    const len:usize=500;

    let mut sum =0;

    let mut arr=[[0i32;len];len];

    let mut loop_no= 500;

    while true {

        loop_no-=1;

        if(loop_no==0){

            break;

        }

        for i in 0..len {

            for j in 0..len {

                if (i > len / 2) {

                    arr[i / 2][j/2] = (i * 8 % 1000 + 2) as i32+f;

                } else {

                    arr[i/2][j/2] = (j * 8 % 1000 + 2) as i32+f;

                }

            }

        }

\


        for i in 0..len {

            for j in 0..len {

                sum+=arr[i][j];

                sum=sum%500000000;

            }

        }

    }

\


    return sum;

\


}

\


fn task()

{

    let mut sum=0;

    let mut i=0;

    while true {

        i+=1;

        let i2= &i;

        sum+=calc(i2);

        if(sum>500){

            sum=0;

        }

        if i>50 {

            break

        }

    }

    println!("{}",sum);

}

fn main() {

    let time=timestamp1();

    let mut arr=[thread::spawn(||{ task(); }),thread::spawn(||{ task(); }),thread::spawn(||{ task

        (); }),thread::spawn(||{ task(); }),thread::spawn(||{ task(); }),thread::spawn(||{ task()

    ; }),thread::spawn(||{ task(); }),thread::spawn(||{ task(); })];

\


    for item in arr {

        item.join().unwrap()

    }

    println!("{}",timestamp1()-time)

}

\


\

以下是Java实现:

\


\


import java.util.List;

import java.util.stream.Collectors;

\


public class BenchMark {

    public static int calc(int f) {

        final int len = 500;

        int sum = 0;

        int[][] arr = new int[len][len];

\


        var loop_no=500;

\


        while (true)

        {

            loop_no-=1;

            if(loop_no==0){

                break;

            }

        for (int i = 0; i < len; i++) {

            for (int j = 0; j < len; j++) {

                if (i > len / 2) {

                    arr[i / 2][j / 2] = (i * 8 % 1000 + 2) + f;

                } else {

                    arr[i / 2][j / 2] = (j * 8 % 1000 + 2) + f;

                }

            }

        }

\


        for (int i = 0; i < len; i++) {

            for (int j = 0; j < len; j++) {

                sum += arr[i][j];

                sum = sum % 500000000;

            }

        }

\


        }

\


        return sum;

    }

\


    public static void task() {

        var sum = 0;

        var i = 0;

        while (true) {

            i += 1;

            var i2 = i;

            sum += calc(i2);

            if (sum > 500) {

                sum = 0;

            }

            if (i > 50) {

                break;

            }

        }

        System.out.println(sum);

    }

\


    public static void main(String... args) {

        var time=System.currentTimeMillis();

        List.of(0,1,2,3,4,5,6,7).stream().map(

                it->new Thread(()->task())

        ).peek(it->it.start()).collect(Collectors.toList()).forEach(

                it->{

                    try {

                        it.join();

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                }

        );

        System.out.println(System.currentTimeMillis()-time);

    }

}

\

结果

\

| Rust  |  Java |

| ---- | ----- |

|96s   |  112s |

\

这一波,rust耗时96秒,Java耗时112秒,Rust比Java快了15%。

\

感觉差不多打平手

\

有朋友说,Runtime语言在内存申请方面比较有优势,这有点矛盾

\

更多的说法是Runtime语言在内存方面存在GC的劣势

\

所以后面我减少了内存申请,代码就被我后来改成第三轮。第二轮就没有另外保存。

\

改了一下算法

\

| Rust  |  Java |

| ---- | ----- |

|79s   |  99s |

\

这一改,rust耗时79秒,Java耗时99秒,Rust有了20%的优势。

\

综合下来其实rust与Java打的有来有回,拉不开差距。

\

大家都不是盖的,连编程语言互相卷,性能拉不开太大差距。

\

第四波测试

\

续上第四波测试:

\

有人说上面都不是真实场景。

\

这次的测试是针对学生数据的处理:

\

对学生信息进行解析和对比

\

测试环境

\

  • Windows 10

\

  • 锐龙 3700x

\

  • rust1.60

\

  • openjdk 11

上代码

\

测试数据样例CSV文件如下:

\

都是假数据,就没必要全发了,格式对上就没问题

\

实际数据12929行,有兴趣的朋友不妨试试

\


00003648732,昂高,2019,1,440000000003648704,在校,36007@mock.com,,15000000036,本科,财经学院,国际经济与贸易,20国贸专科1班,2019,0,男

00004651006,冬凡,2019,2,440000000004651008,在校,35522@mock.com,,15000000047,本科,财经学院,会计学,19立信注会创新2班,2019,0,女

00016981406,杰昂,2020,3,440000000016981376,在校,45352@mock.com,,15000000170,本科,财经学院,财务管理,19财务专科1班,2020,0,男

00021199306,震震,2020,4,440000000021199296,在校,41234@mock.com,,15000000212,本科,财经学院,财务管理,19财务本科1班,2020,0,女

00028630182,月元,2020,5,440000000028630208,在校,45572@mock.com,,15000000286,本科,财经学院,会计学,20会计本科8班,2020,0,男

00036510342,昂治,2020,6,440000000036510336,在校,40650@mock.com,,15000000365,专科,财经学院,会计学,19会计本科4班,2020,0,男

00036977932,菱翠,2019,7,440000000036977920,在校,33818@mock.com,,15000000370,本科,财经学院,证券与期货,19证券专科1班,2019,0,女

00038356442,彰昆,2020,8,440000000038356416,在校,41798@mock.com,,15000000384,本科,财经学院,会计学,19会计本科3班,2020,0,男

00045988090,曼又,2019,9,440000000045988096,在校,36595@mock.com,,15000000460,本科,财经学院,金融工程,19金融本科4班,2019,0,男

00055533180,东旭,2020,10,440000000055533184,在校,42107@mock.com,,15000000555,本科,财经学院,会计,19会计专科4班,2020,0,男

\

Kotlin 实现

\


\


import java.io.File

\


class Student(

    val id: ULong,

    val name: String,

    val enrollment_year: UInt,

    val user_id: UInt,

    val id_card_no: String,

    val student_status: UInt,

    val email: String,

    val tel: String,

    val edu_level: String,

    val department: String,

    val major: String,

    val class_in_school: String,

    val grade: UInt,

    val sex: UInt,

)

\


class StudentCompareResult(

    val id: Pair<ULong, ULong>,

    val name: Pair<String, String>,

    val enrollment_year: Pair<UInt, UInt>,

    val user_id: Pair<UInt, UInt>,

    val id_card_no: Pair<String, String>,

    val student_status: Pair<UInt, UInt>,

    val email: Pair<String, String>,

    val tel: Pair<String, String>,

    val edu_level: Pair<String, String>,

    val department: Pair<String, String>,

    val major: Pair<String, String>,

    val class_in_school: Pair<String, String>,

    val grade: Pair<UInt, UInt>,

    val sex: Pair<UInt, UInt>

)

\
\


fun parseStudent(row: String): Student {

\


    val fields = row.split(",");

\


    val student = Student(

        id = fields[0].toULong(),

        name = fields[1],

        enrollment_year = fields[2].toUInt(),

        user_id = fields[3].toUInt(),

        id_card_no = fields[4],

        student_status = if (fields[5] == "在校") 1u else 0u,

        email = fields[6],

        tel = fields[8],

        edu_level = fields[9],

        department = fields[10],

        major = fields[11],

        class_in_school = fields[12],

        grade = fields[13].toUInt(),

        sex = fields[14].toUInt()

    )

    return student

}

\


fun getStudentMap(content: String): HashMap<ULong, Student> {

    val rows = content.split("\n");

\


    val students = rows.map({

        parseStudent(it)

    }).toMutableList()

\


    students.removeAt(0);

\


    val idToStudentMap = HashMap<ULong, Student>();

    for (s in students) {

        idToStudentMap.put(s.id, s);

    }

    return idToStudentMap;

}

\
\


fun <T> compare(a: T, b: T): Pair<T, T>? {

    return (if (a!!.equals(b)) null else Pair(a, b))

}

\
\


fun compareStudent(src: Student, target: Student): StudentCompareResult {

    val compareResult = StudentCompareResult(

        id = compare(src.id, target.id)!!,

        name = compare(src.name, target.name)!!,

        enrollment_year = compare(src.enrollment_year, target.enrollment_year)!!,

        user_id = compare(src.user_id, target.user_id)!!,

        id_card_no = compare(src.id_card_no, target.id_card_no)!!,

        student_status = compare(src.student_status, target.student_status)!!,

        email = compare(src.email, target.email)!!,

        tel = compare(src.tel, target.tel)!!,

        edu_level = compare(src.edu_level, target.edu_level)!!,

        department = compare(src.department, target.department)!!,

        major = compare(src.major, target.major)!!,

        class_in_school = compare(src.class_in_school, target.class_in_school)!!,

        grade = compare(src.grade, target.grade)!!,

        sex = compare(src.sex, target.sex)!!,

    )

    return compareResult;

}

\


fun readText(path: String): String {

    return File(path).readText()

}

\


fun calc(srcText: String, targetText: String) {

\


    var i = 0;

\


    while (true) {

\


        val srcMap = getStudentMap(srcText)

        val targetMap = getStudentMap(targetText)

\


        val keys = HashSet<ULong>();

\


        val resultMap = HashMap<ULong, StudentCompareResult>();

\


        for (key in keys) {

            val src = srcMap.get(key);

            val target = targetMap.get(key);

            if (null != src && null != target) {

                resultMap.put(key, compareStudent(src, target));

            }

        }

\


        // for x in resultMap {

        //     prUIntln!("OK{:?},{:?}",x.0,x.1);

        // }

\


        if (i == 500) {

            break;

        }

        i += 1;

    }

\
\


}

\


fun main(args: Array<String>) {

    val srcText = readText("C:\\Users\\CDX\\Desktop\\实验室\\导出\\mock_student.csv");

    val targetText = readText("C:\\Users\\CDX\\Desktop\\实验室\\导出\\mock_student2.csv");

\


    val now = System.currentTimeMillis();

\


    println("开始计时:" + now);

    (0..15).toList().map {

        Thread { calc(srcText, targetText) }

    }.map {

        it.start()

        it

    }.forEach {

        it.join()

    }

    println("计时结束:{}" + (System.currentTimeMillis() - now));

}

\

Rust:

(与Kotlin代码大体对齐,但代码写的很糙,有兴趣的兄弟不妨自己实现一个)

\


\


use std::{fmt, thread};

use std::collections::{HashMap, HashSet};

use std::fs::File;

use std::io::Read

use std::io;

use std::thread::{JoinHandle, Thread};

use std::time::SystemTime;

\


#[derive(Debug ,Clone)]

struct Student{

    id: u64,

    name: String,

    enrollment_year: u32,

    user_id: u32,

    id_card_no: String,

    student_status: u32,

    email: String,

    tel: String,

    edu_level: String,

    department: String,

    major: String,

    class_in_school: String,

    grade: u32,

    sex: u32,

}

\


impl fmt::Display for Student{

    fn fmt(&self,f: &mut fmt::Formatter) -> fmt::Result{

        write!(f,"x:{}, y:{}",self.id,self.name)

    }

}

\


#[derive(Debug ,Clone)]

struct StudentCompareResult {

    id: Option<[u64; 2]>,

    name: Option<[String; 2]>,

    enrollment_year: Option<[u32; 2]>,

    user_id: Option<[u32; 2]>,

    id_card_no: Option<[String; 2]>,

    student_status: Option<[u32; 2]>,

    email: Option<[String; 2]>,

    tel: Option<[String; 2]>,

    edu_level: Option<[u64; 2]>,

    department: Option<[String; 2]>,

    major: Option<[String; 2]>,

    class_in_school: Option<[String; 2]>,

    grade: Option<[u32; 2]>,

    sex: Option<[u32; 2]>,

}

\


impl fmt::Display for StudentCompareResult{

    fn fmt(&self,f: &mut fmt::Formatter) -> fmt::Result{

        write!(f,"x:{:?}, y:{:?}",self.id,self.name)

    }

}

\


#[inline]

fn parseStudent(row: & str) -> Student {

    let field_split = row.split(",");

\


    let fields: Vec<&str> = field_split.collect();

\


    let student = Student {

        id: fields[0].trim_start_matches("0").parse::<u64>().unwrap(),

        name: fields[1].parse().unwrap(),

        enrollment_year: fields[2].parse::<u32>().unwrap(),

        user_id: fields[3].parse::<u32>().unwrap(),

        id_card_no: fields[4].parse().unwrap(),

        student_status: if fields[5].eq("在校") { 1 } else { 0 },

        email: fields[6].parse().unwrap(),

        tel: fields[8].parse().unwrap(),

        edu_level: fields[9].parse().unwrap(),

        department: fields[10].parse().unwrap(),

        major: fields[11].parse().unwrap(),

        class_in_school: fields[12].parse().unwrap(),

        grade: fields[13].parse::<u32>().unwrap(),

        sex: fields[14].parse::<u32>().unwrap(),

    };

    student

}

\


#[inline]

fn getStudentMap<'a>(content: &str) -> HashMap<u64, Student>

{

    let rows = content.split("\n");

\


    let mut students = rows.into_iter().map(|e| {

        return parseStudent(&e);

    }).collect::<Vec<Student>>();

\


    students.remove(0);

\


    let mut idToStudentMap = HashMap::new();

    for s in students {

        idToStudentMap.insert(s.id, s);

    }

    return idToStudentMap;

}

\


#[inline]

fn compareField< T: PartialEq>(src: Box<T>, target: Box<T>) -> Option<[T; 2]>

{

    return if src.eq(&target) {

        Option::from([*src,*target])}

    else { Option::None }

}

\


macro_rules! compare {

    ($src:expr,$target:expr) => {

        compareField(Box::new($src),Box::new($target))

    };

}

\


#[inline]

fn compareStudent(src: Student, target: Student) -> Box<StudentCompareResult>

{

\


    let compareResult = StudentCompareResult {

        id: compare!(src.id, target.id),

        name: compare!(src.name,target.name),

        enrollment_year: compare!(src.enrollment_year,target.enrollment_year),

        user_id: compare!(src.user_id,target.user_id),

        id_card_no:compare!(src.id_card_no,target.id_card_no),

        student_status: compare!(src.student_status,target.student_status),

        email: compare!(src.email,target.email),

        tel: compare!(src.tel,target.tel),

        edu_level: compare!(src.id,target.id),

        department: compare!(src.department,target.department),

        major: compare!(src.major,target.major),

        class_in_school: compare!(src.class_in_school,target.class_in_school),

        grade: compare!(src.grade,target.grade),

        sex: compare!(src.sex,target.sex),

    };

    return Box::new(compareResult );

}

\


#[inline]

fn readText(path:&str) -> String

{

    let mut studentCsvFile = File::open(path).unwrap();

    let mut content = String::new();

    studentCsvFile.read_to_string(&mut content).unwrap();

    return content;

}

\


fn calc()

{

\


    let srcText=readText("C:\\Users\\CDX\\Desktop\\实验室\\导出\\mock_student.csv");

    let targetText=readText("C:\\Users\\CDX\\Desktop\\实验室\\导出\\mock_student2.csv");

\


    let now = SystemTime::now();

    println!("开始计时:{}",now.elapsed().unwrap().as_millis());

\


    let mut i=0;

    loop{

\


        let srcMap = getStudentMap(srcText.as_str());

        let targetMap = getStudentMap(targetText.as_str());

\


        let mut keys=HashSet::new();

        {

            let ref srcMap = srcMap;

            let ref targetMap = targetMap;

            let srcKeys = &srcMap.into_iter().map(|e| { *e.0 }).collect::<HashSet<u64>>();

            let targetKeys = &targetMap.into_iter().map(|e| { *e.0 }).collect::<HashSet<u64>>();

            keys.extend(srcKeys);

            keys.extend(targetKeys);

        }

\


        let mut resultMap=HashMap::new();

\


        for key in keys {

            let src=&srcMap.get(&key);

            let target=&targetMap.get(&key);

            if src.is_some()&&target.is_some() {

                &resultMap.insert(key,(src.unwrap(),target.unwrap()));

            }

        }

\


        // for x in resultMap {

        //     println!("OK{:?},{:?}",x.0,x.1);

        // }

\


        if(i==500){

            break;

        }

        i+=1;

    }

\


    let mut input = String::new();

    println!("计时结束:{}",now.elapsed().unwrap().as_secs());

    io::stdin().read_line(&mut input);

}

\


fn main() {

    let mut b: [Option<JoinHandle<()>>; 16] = Default::default();

\


    for i in 0..16 {

        let h = thread::spawn(|| {

            calc()

        });

        b[i]=Option::Some(h)

    }

    for i in b {

        i.unwrap().join().expect("a")

    }

}

\

结果

这一波就有点遗憾,在稍微复杂的场景下,JDK创建了很多对象,GC运行也频繁,所以....

.

.

.

.

.

.

.

| Rust  |  Java |

| ---- | ----- |

|70s   |  42s |

个鬼啊,看走眼了,42s的才是Java

JDK这次真的不当人了,竟然比Rust快将近 40%。

总结

这样的测试结果真的有点骇人听闻

\

我在知乎发的前面测试结果的时候,反响热烈就很热烈

有一些人都在发友好的互动(往死里黑)

所以我后面这一波不敢发测试结果了,就JYM独家,You 明白我的意思吧。在知乎发了必然被

喷出翔。

\

这些只是我个人的测试结果,跟算法乃至于硬件和操作系统平台都有关系,毕竟

我Rust也就初学水平,但我还是觉得掘金社区氛围比较好,JYM都是人才,说话也好听,应该

不会那么狭隘,所以还是要给你们看看结果。

一点点心得

关于实践和测试

姜承尧老师曾经说过:网上的技术言论,很多都是荒谬的,学技术一定要自己动手实践才能得到

真实的结果。这句话我一直记得,也亲生验证过很多次,所以我写本文其实动机源于此。

\

网上很多人会告诉你:“生存环境下千万不要用反射,Java反射性能慢几十倍”

“XFS数据库性能比EXT格式快多了,数据库不要用EXT格式”

“Java执行效率差,比Go要慢好几倍”

\

这些问题我先不解谜,希望有人在评论区@我解一下

关于傲慢与偏见

\

可能有人认定Java就是垃圾,要逃离它,而Rust是系统级编程语言,他一定是能甩过Java

几条街的,如果测试结果不是这样,那测试一定是假的。

\

每个人是有偏见的,这是人性的弱点,须得小心克服。潜意识认定一件事,我们就往往

很难改变,有人认为我们理工科的,会理智一点,但其实往往理工科的人,傲慢偏见,也很重。

\

最后Rust学习曲线真的很陡,语言本身的所有权是重难点,的确不好掌握,而且Rust目前没有Java

一样成熟的开发工具,调试面板也没法一目了然看到整个对象的细节。CLion对Rust的支持不太稳定,

在其MSVC,MinGW,CygWin三个平台的Debuger中都出现了三种不同的Bug,【Debuger出现了Bug】这听起来很像一个段子。

回到语言

Rust属于C++同一层的系统级编程语言,设计上非常大胆先进,但它本身就不是为Java和Go一类的应用层场景设计的,学习曲线很陡峭。但除此之外,Rust浑身都是优点,它没有GC也没有运行时,编译出来跟C++一样是二进制文件,连LLVM都没有,程序大小以kb计算,且启动极快。此外还能编译成WASM在浏览器运行。严格的生命周期和所有权管理,就很革新,让人减少很多bug,非常有利于开发稳定的多线程程序。

\

而JDK经过这么多年的优化,也早已成仙不当人了,可不是当年那个龟速虚拟机了,Java与Rust的性能都比NodeJs,Php,Python要快很多。此外我在测试中也有用到比较先进的Graalvm,据说其AOT技术,能静态编译部分Java代码,使得性能更好,事实上动态就不一定输给静态,实际测试中OpenJDK与Graalvm的性能差距非常小。 (但是顺便一说,GraalVM可以运行Js等多种语言,以接近OpenJDK性能的方式打开JS,Python的功能,还支持 用Java控制vm中JS的运行,包括权限拦截,就非常好用)

\

文章两万多字,加上代码高亮,知乎的编辑器都被卡到掉帧了,走过路过留下个评论不过分吧,XDM!!