quarkus-graalvm 可以救 Java native 一命 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
karottc
V2EX    Java

quarkus-graalvm 可以救 Java native 一命

  •  2
     
  •   karottc
    karottc 2024-07-16 14:12:26 +08:00 6658 次点击
    这是一个创建于 452 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前文是这个项目:果然吃内存,一个简单的 Java 程序就占用了 250M 内存

    用 quarkus + graalvm 重新了这个项目(顺便学习了下 quarkus, 获益匪浅),功能逻辑全部保持了一致,只是组件根据框架进行了平移(比如 okhttp -> rest-client ).

    程序的功能为:

    • 每 10 分钟抓取某个链接的数据
    • 抓取到的内容和 mysql 里面已有的内容作对比
    • 存在就更新,不存在就插入到 mysql 中
    • 每天早上 10 点发送一个企业微信通知
    • 提供一个 rest 接口,上面的功能,可以通过定时触发,也可以通过接口手动触发(保留为了调试和验证程序情况,稳定之后几乎不会调用)。
    • 使用 cdi ( springboot 里面叫 AOP ,quarkus 里面叫 cdi ),把上面 rest 请求记录到日志中。
    • 为了保持一致,日志格式内容都和之前的 springboot 项目保持了一致。

    这就是这个程序的所有功能,这版用到的组件和框架为 quarkus + gson + mybaits + jdbc + quarkus-rest-clint(发送网络请求) + jboss(quarkus 的默认日志)

    编译环境:linux-x86-64, graalvm-21.(社区版),编译之后的二进制大小是 100M 。编译耗时大概十几分钟。 openjdk version "21.0.2" 2024-01-16 OpenJDK Runtime Environment GraalVM CE 21.0.2+13.1 (build 21.0.2+13-jvmci-23.1-b30) OpenJDK 64-Bit Server VM GraalVM CE 21.0.2+13.1 (build 21.0.2+13-jvmci-23.1-b30, mixed mode, sharing)

    启动之后,占用 90M 内存,然后我调用了好几次 rest 请求,内存涨到了 120M ,过了一天程序稳定运行之后内存占用在 40-50M 左右。

    虽然和我预期的 20M 内存有差距,但是已经比较满意了。虽然量起来了,内存占用一样会升高,但是没量了会把内存还给操作系统。quarkus 的启动速度确实快,毫秒级。

    这次项目的意义,在于让我学会了 quarkus,(除了上面的项目,我还用 quarkus-picocli 写了个命令行工具,也很不错) 不愧是自古评论出人才。


    此外,我用 python 实现了上面项目的核心部分,稳定之后内存占用 20M 。


    最后,吹一下 quarkus, 在 native 这块很强,和 graalvm 的集成度非常高,比如反射问题单纯用 graalvm 需要繁琐的配置文件,在 quarkus 里面用一个注解就行了,方便很多。

    缺点就是编译略慢,而且需要高配机器,配置越高,编译越快。


    单纯的 graalvm 还是非常不行, 2024 年,graalvm native image 仍较为勉强 这个帖子也说了,但是和 quarkus 搭配一下就很不错了。

    25 条回复    2024-11-25 14:22:13 +08:00
    GogoGo666
        1
    GogoGo666  
       2024-07-16 14:27:31 +08:00   1
    这种情况还是换 go 吧,不管是编译后的二进制大小,编译耗时,内存占用,都比 java 强太多了
    yazinnnn0
        2
    yazinnnn0  
       2024-07-16 14:27:53 +08:00   1
    自己玩没问题, 在大规模生产环境中使用会变得不幸, 除非你在红帽工作, 身边都是 quarkus 贡献者
    karottc
        3
    karottc  
    OP
       2024-07-16 14:39:16 +08:00
    @GogoGo666 认真学了一段时间,然后没有工作环境持续,并且也不是很喜欢,然后就放弃了。
    wssy001
        4
    wssy001  
       2024-07-16 14:48:58 +08:00   3
    无论是 springboot 还是 Quarkus 在改造成 Native Image 项目中 都会面临重构轮子的窘境
    JVM 环境中,开发者 Google 到一个好轮子可以拿来直接用,而在 Native Image 中,开发者不得不考虑该怎么用原生的方式“曲线救国”
    这是我这几年玩 GraalVM Native Image 的心得,最后得出一个结论:鉴于目前 Native Image 的生态,我觉得把项目用 go 重构的代价要比改成 Native Image 低不少(时间+精力方面)
    feiyan35488
        5
    feiyan35488  
       2024-07-16 15:02:21 +08:00
    在意内存和制品大小的话,还考虑下 go 、rust 吧
    缺硬件资源的企业不会用 java 的
    zhaojun1998
        6
    zhaojun1998  
       2024-07-16 15:09:14 +08:00
    Spring Boot 改造现在已经还不错了,花了一周左右时间把一个 Spring Boot 2.6 (JDK1.8) 的项目,升级到 Spring Boot 3.3.0(JDK 21) Native 不算太麻烦,简单点说就是标记下哪些类用到了反射,要做 native 处理,如果库本身做了那你就不用做,如果没做,你自己添加到配置文件也行。

    编译速度也做过测试,内存占的比重大于 CPU 性能,编译时可用内存最好 16+

    也不必谈 Java 色变,我觉得在这方面的努力这几年还是挺多的,估计过几年打包 native 就跟打包一个 jar 方便了。

    不过缺点也有:编译速度慢,编译占用资源多,不支持交叉编译,社区不看好导致很多库不愿兼容 (我当时找兼容 flyway 时费了老大劲,后来发现还是 springboot 的工程师去兼容的,他们提交给 flyway 官方也不愿理睬)
    sunny352787
        7
    sunny352787  
       2024-07-16 15:14:07 +08:00
    用 go 写了个模拟,获取 baidu 首页然后插入 mongo ( upsert ),内存占用 2.9M ,rust 会不会是 k 级别?
    karottc
        8
    karottc  
    OP
       2024-07-16 15:14:13 +08:00
    @zhaojun1998 springboot 上,mybatis 加了配置文件也不行
    Goooooos
        9
    Goooooos  
       2024-07-16 15:14:41 +08:00
    正常程序员都会多种语言,不同场景选自己熟悉和合适的语言即可
    1. 日常业务开发,我会用 Java
    2. 导数据、数据分析,我用 python
    3. 小组件,用 go
    4. 前端,用 js 或 ts
    qq135449773
        10
    qq135449773  
       2024-07-16 15:22:07 +08:00
    其实有的时候解决 Java 的这种体积或者资源占用问题,最好的办法就是不用 Java
    javak
        11
    javak  
       2024-07-16 15:24:56 +08:00 via iPhone
    我也不喜欢 go ,推荐学习下 rust ,大家都说好。我也觉得好(虽然我没用过)
    sagaxu
        12
    sagaxu  
       2024-07-16 15:26:13 +08:00   1
    @yazinnnn0 quarkus 用户基数太少了,我直接用的 vertx ,小版本常有大改动,遇到过不少问题,比如

    redis client 超过 20 亿个请求之后 index 溢出;
    http server 某个版本 pause request 但 resume 处理不当,导致无法正常处理某些 HTTP 请求;
    redis 连接池连接泄漏;
    Templatehandler 找不到子目录下的模板;

    这 4 个其中 3 个我提了 issue ,好在都很快解决了。

    vertx 这玩意儿,不像 spring 家族那样可以无脑使用,但是用顺手了却非常舒适,即便出了问题,也很容易定位和解决,毕竟比 spring 全家桶简单太多了。

    =====================================================================================

    这类小工具,用 Go 写也很舒服,graalvm build 几次的时间都够我用 Go 写完上线了


    @zhaojun1998 我觉得 graalvm 的 native 要达到可用,还需要从预编译入手,发布 jar 的同时发布预编译文件,用户 build 时从预编译文件中提取组合,而不是把编译工作全部放在使用者的机器上做。解决了生态和资源消耗问题,可用性就起来了。这需要很长时间和很大存储空间,也不一定有足够多的资源投入。
    kneo
        13
    kneo  
       2024-07-16 15:27:24 +08:00 via Android
    你喜欢就好。
    chendy
        14
    chendy  
       2024-07-16 15:32:30 +08:00
    羡慕楼主玩这么花还没人管
    CodeCodeStudy
        15
    CodeCodeStudy  
       2024-07-16 15:53:46 +08:00
    我电脑 i7-6700 ,16G 内存,编译 hello,world 需要 34 秒,编译后的可执行文件是 6M 。

    编译很久,连 graalvm 官方都出教程,编译完后怎么系统通知开发者

    https://www.graalvm.org/latest/reference-manual/native-image/overview/Build-Overview/#getting-notified-when-the-build-process-is-done
    CodeCodeStudy
        16
    CodeCodeStudy  
       2024-07-16 16:42:44 +08:00
    我电脑 i7-6700 ,16G 内存,编译 hello,world 需要 34 秒,编译后的可执行文件是 6M 。

    编译很久,连 graalvm 官方都出教程,编译完后怎么系统通知开发者

    https://www.graalvm.org/latest/reference-manual/native-image/overview/Build-Overview/#getting-notified-when-the-build-process-is-done
    luciankaltz
        17
    luciankaltz  
       2024-07-16 16:58:23 +08:00
    我们之前某个小组件用 quarkus + native image 试了一下,结果发现有些莫名其妙的坑,比如上面说到的反射之类的。如果需要手动一个一个标注也太麻烦了(指没必要

    说起来 quarkus 坑也很多就是了,还是不如 spring (

    一定要扣这些资源,不如直接用 go 重写就是了(
    INCerry
        18
    INCerry  
       2024-07-16 17:03:23 +08:00
    可以直接用.NET 走 NativeAOT ,我之前搞个 ASP.NET Core 项目启动内存占用 15MB 不到,压测以后不到 50MB ,还可以用更轻量的 http 框架,C#语法和 Java 差不多,半天就能上手。
    artiga033
        19
    artiga033  
       2024-07-17 10:42:11 +08:00 via Android
    @INCerry .NET AOT 这么多年了其实也就刚到勉强能用的阶段,性能折扣且不论,好多库还是不兼容 AOT 的,ASP.NET Core 还好,像桌面开发那边一大堆库都不兼容,因为大量使用反射,而且不说外部库,就是自己写代码,写 Source Generator 也比写反射折磨多了,论元编程能力个人感觉没有语言比得过 rust 。

    Java 的 AOT 方案我不了解,不过感觉.NET 和 Java 这种感觉与其折腾预编译,不如考虑像 go 那样缩小 runtime 体积,但还是带一个 runtime 来做 gc 之类的。至少.NET AOT 直接就损失了大部分的运行时动态操作能力,而且 JIT 会对热点代码做高度优化,同一个函数调用次数越多性能越好,极有可能最终实际性能比 AOT 还高。
    rb6221
        20
    rb6221  
       2024-07-17 11:08:37 +08:00
    作为对比,你提到的 python 项目编译和启动速度如何
    karottc
        21
    karottc  
    OP
       2024-07-17 16:12:35 +08:00
    @janus77 python 不需要编译, 直接瞬间启动
    INCerry
        22
    INCerry  
       2024-07-17 16:58:28 +08:00
    @artiga033

    我举 AOT 的例子也只是贴近更小的体积和内存占用,实际上直接跑个 ASP.NET Core ( DATAs GC 模式)不走 AOT 内存也只是会多个 10M 左右的水平,只是发布会多个 runtime 的体积。

    从题主的需求来说,桌面端不用考虑,go 的桌面端几乎等于没有,而且基本没有动态代码功能。ASP.NET Core + Native AOT 写写题主要求的已经足够,而且编程效率比 Go 高多了,另外.NET AOT 有 Static PGO 功能,一样可以对热点代码高度优化。
    byte10
        23
    byte10  
       2024-07-22 10:16:51 +08:00
    @sagaxu vertx 使用 GraalVM 编译麻烦吗?
    CodeCodeStudy
        24
    CodeCodeStudy  
       2024-07-25 16:23:57 +08:00
    编译十几分钟也太长了吧
    layxy
        25
    layxy  
       320 天前
    用 java 就是图的他的动态能力,单纯想要低内存降低包大小,还是 go 比较好
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2689 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 14:10 PVG 22:10 LAX 07:10 JFK 10:10
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86