V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
karottc
V2EX  ›  Java

quarkus-graalvm 可以救 Java native 一命

  •  2
     
  •   karottc ·
    karottc · 160 天前 · 5460 次点击
    这是一个创建于 160 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前文是这个项目:果然吃内存,一个简单的 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  
       160 天前   ❤️ 1
    这种情况还是换 go 吧,不管是编译后的二进制大小,编译耗时,内存占用,都比 java 强太多了
    yazinnnn0
        2
    yazinnnn0  
       160 天前   ❤️ 1
    自己玩没问题, 在大规模生产环境中使用会变得不幸, 除非你在红帽工作, 身边都是 quarkus 贡献者
    karottc
        3
    karottc  
    OP
       160 天前
    @GogoGo666 认真学了一段时间,然后没有工作环境持续,并且也不是很喜欢,然后就放弃了。
    wssy001
        4
    wssy001  
       160 天前   ❤️ 3
    无论是 springboot 还是 Quarkus 在改造成 Native Image 项目中 都会面临重构轮子的窘境
    JVM 环境中,开发者 Google 到一个好轮子可以拿来直接用,而在 Native Image 中,开发者不得不考虑该怎么用原生的方式“曲线救国”
    这是我这几年玩 GraalVM Native Image 的心得,最后得出一个结论:鉴于目前 Native Image 的生态,我觉得把项目用 go 重构的代价要比改成 Native Image 低不少(时间+精力方面)
    feiyan35488
        5
    feiyan35488  
       160 天前
    在意内存和制品大小的话,还考虑下 go 、rust 吧
    缺硬件资源的企业不会用 java 的
    zhaojun1998
        6
    zhaojun1998  
       160 天前
    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  
       160 天前
    用 go 写了个模拟,获取 baidu 首页然后插入 mongo ( upsert ),内存占用 2.9M ,rust 会不会是 k 级别?
    karottc
        8
    karottc  
    OP
       160 天前
    @zhaojun1998 springboot 上,mybatis 加了配置文件也不行
    Goooooos
        9
    Goooooos  
       160 天前
    正常程序员都会多种语言,不同场景选自己熟悉和合适的语言即可
    1. 日常业务开发,我会用 Java
    2. 导数据、数据分析,我用 python
    3. 小组件,用 go
    4. 前端,用 js 或 ts
    qq135449773
        10
    qq135449773  
       160 天前
    其实有的时候解决 Java 的这种体积或者资源占用问题,最好的办法就是不用 Java
    javak
        11
    javak  
       160 天前 via iPhone
    我也不喜欢 go ,推荐学习下 rust ,大家都说好。我也觉得好(虽然我没用过)
    sagaxu
        12
    sagaxu  
       160 天前   ❤️ 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  
       160 天前 via Android
    你喜欢就好。
    chendy
        14
    chendy  
       160 天前
    羡慕楼主玩这么花还没人管
    CodeCodeStudy
        15
    CodeCodeStudy  
       160 天前
    我电脑 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  
       160 天前
    我电脑 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  
       160 天前
    我们之前某个小组件用 quarkus + native image 试了一下,结果发现有些莫名其妙的坑,比如上面说到的反射之类的。如果需要手动一个一个标注也太麻烦了(指没必要

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

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

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

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

    从题主的需求来说,桌面端不用考虑,go 的桌面端几乎等于没有,而且基本没有动态代码功能。ASP.NET Core + Native AOT 写写题主要求的已经足够,而且编程效率比 Go 高多了,另外.NET AOT 有 Static PGO 功能,一样可以对热点代码高度优化。
    byte10
        23
    byte10  
       154 天前
    @sagaxu vertx 使用 GraalVM 编译麻烦吗?
    CodeCodeStudy
        24
    CodeCodeStudy  
       151 天前
    编译十几分钟也太长了吧
    layxy
        25
    layxy  
       28 天前
    用 java 就是图的他的动态能力,单纯想要低内存降低包大小,还是 go 比较好
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1014 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 22:18 · PVG 06:18 · LAX 14:18 · JFK 17:18
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.