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

果然吃内存,一个简单的 Java 程序就占用了 250M 内存

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

    之前一直在用 Java 写 企业级代码,并没有很在意 java 的内存问题,比如多个/少个一两百兆就没关心。

    平时用 java 写的直接运行就结束的代码,基本都是本地电脑运行,也没有发现内存问题。

    直到最近,用 java 写了一个简单的程序,运行在我的 1C1G 的腾讯云机器上,才发现 java 确实内存大户。

    程序的功能为:

    1. 每 10 分钟抓取某个链接的数据
    2. 抓取到的内容和 mysql 里面已有的内容作对比
    3. 存在就更新,不存在就插入到 mysql 中
    4. 每天早上 10 点发送一个企业微信通知

    这就是这个程序的所有功能,由于用到了定时任务和操作 mysql, 所以我第一个版直接用了 springboot ,平时工作也用整起来快。 用到的库为:okhttp + gson + mybaits + jdbc + logback

    程序运行稳定之后内存占用:250M

    这个简单的功能这个内存占用实在是太大了。

    然后我觉得可能是 springboot 的原因,所以我写了第二版: 用了 okhttp + gson + mybatis + jdbc 去掉了框架和 logback, 直接用 print 输出 log , 定时任务也直接用了 while(true) + sleep 来实现。

    这版程序稳定运行之后内存占用:90M

    上面两版没有加任何优化参数,就是 java -jar xxx.jar 运行。 java 版本:java21


    java21 了,还是这个内存表现,失望啊。

    第 1 条附言  ·  128 天前

    我用python 写了核心功能,内存占用 20M。

    又用quarkus-graalvm 完全1:1重构了一遍,内存占用50M。 详见这个帖子 https://www.v2ex.com/t/1057699

    125 条回复    2024-07-14 13:14:48 +08:00
    1  2  
    medivh
        1
    medivh  
       136 天前 via iPhone
    试试 openj9
    powerman
        2
    powerman  
       136 天前
    加钱吧,Java 整个生态来讲,早就不是设计之初,应用于嵌入式设备 微波炉 洗衣机 等玩意的语言了,你要是从头撸起来,httpclient + 自己手撸一个 json 解析器 + jdbc template ,其实内存也用不了多少
    powerman
        3
    powerman  
       136 天前
    而且 Java 目标就是 作为一个企业级的后台开发生态工具了,早就不适合 嵌入式 等资源受限的场景
    cmdOptionKana
        4
    cmdOptionKana  
       136 天前
    没有完美的语言,总不能没有短板吧,要是 Java 那么完美,其他语言也没生存空间了。
    angrylid
        5
    angrylid  
       136 天前   ❤️ 89
    哪里多了,这么多年一直都是这个占用,不要睁着眼睛乱说,write once run anywhere 很难的。
    povsister
        6
    povsister  
       136 天前
    只能说 java 是这样的,不然也不会云原生时代被 go 按脑袋了。
    cdlnls
        7
    cdlnls  
       136 天前
    你看看 jmap -heap 内存占用多少,然后根据实际占用情况调整 Xmx
    Ayanokouji
        8
    Ayanokouji  
       136 天前
    既然这么在意内存,为何不上 graalvm 试试呢
    xuhengjs
        9
    xuhengjs  
       136 天前
    jvm 的内存占用,小项目确实不划算
    HFX3389
        10
    HFX3389  
       136 天前
    @powerman #2 J2ME 表示不服
    suyuyu
        11
    suyuyu  
       136 天前   ❤️ 5
    那么我可要宣布了。PHP 是世界上最好的语言
    serical
        12
    serical  
       136 天前
    @angrylid 李里李气的
    zhouxiaoben
        13
    zhouxiaoben  
       136 天前
    确实,我的个人网站换了 nuxt + go ,内存占用也少很多
    Donahue
        14
    Donahue  
       136 天前
    那我可要宣布了 go 语言是世界上最好的语言。
    非常适合写这种工具/脚本类的小应用
    chendy
        15
    chendy  
       136 天前   ❤️ 7
    嫌大就加参数限制最大堆呗,默认最大堆四分之一物理内存肯定大啊
    我在服务器上跑的一个小定时任务,spring-boot 弄的,xmx32m 一样跑
    Rorysky
        16
    Rorysky  
       136 天前
    估计 python 差不多
    ns09005264
        17
    ns09005264  
       136 天前
    换个语言吧,我从没想过用 java 写小工具。
    再说学习新的语言对编程技术有一定的提升。
    Vegetable
        18
    Vegetable  
       136 天前
    这也是我一直诟病的,JAVA 写习惯了的开发,起手就是 Spring Boot ,嘴里反复念叨着内存不值钱啊,依赖注入啊什么的。
    曾经因为我电脑只有 jdk 没有 ide ,问一个朋友,你知道怎么在命令行执行一个.java 文件吗?他说不知道。
    8620
        19
    8620  
       136 天前
    加入 C 的怀抱吧,内存管理编码者自己来~
    diagnostics
        20
    diagnostics  
       136 天前
    @Vegetable 说明你朋友培训班出来的,学 Java 课,起码作业会让你用 javac
    yazinnnn0
        21
    yazinnnn0  
       136 天前   ❤️ 2
    设置下堆内存

    不过 jvm 运行时就这么大, 先天就会吃掉一些

    你真的在乎的话可以换 quarkus+graalvm, 大概能控制在 20M 左右

    要么就换语言吧, 用 rust 或者 go

    不过我建议你用 crontab, 跑完任务就 return 了, 不用管占用了多少内存
    jry
        22
    jry  
       136 天前
    中小项目我站 PHP ,维护太方便了,本地就起一个 PHP-FPM 占用 50MB 内存,项目随便放多少个都不影响,维护访问才会占用一下内存访问完立刻就释放了。要是维护很多 java 就麻烦的不行,要各个项目常驻内存,启动就得老半天。
    0xD800
        23
    0xD800  
       136 天前
    我用 java 写的一个 ddns ,根本不关注内存,crontab 一分钟跑一次
    0xD800
        24
    0xD800  
       136 天前
    我记得新版的 GC 是对大内存友好,对小内存好像不怎么友好,你试试 jdk8 用老的 GC 看看
    Huelse
        25
    Huelse  
       136 天前
    话虽如此,但在大规模 IO 应用上 java 还是唯一选择
    powerman
        26
    powerman  
       136 天前
    @HFX3389 这个都不知道死了 多久了,上学那会 mtk 的山寨手机 估计还有在用,现在几乎绝迹了
    jackmod
        27
    jackmod  
       136 天前
    小工具应用我甚至已经决定放弃 Python 了,正在抓紧时间掌握 golang
    dcsuibian
        28
    dcsuibian  
       136 天前
    不是 java 内存大了,是你内存小了。我个人 2c4g 的腾讯云轻量,根本没想过内存占用
    arloor
        29
    arloor  
       136 天前   ❤️ 1
    @yazinnnn0 支持换 rust 或者 go 。今天刚用 rust 写了个小工具提效,同事说竟然是用 rust 写的,无形中秀了一把哈哈。
    cocalrush
        30
    cocalrush  
       136 天前
    你继续跑 1k 个链接任务,会发现仍然是这么大的内存
    Donaldo
        31
    Donaldo  
       135 天前
    说实话,你这个需求真没必要用 Java ,我估计在这个场景大部分内存都被 runtime 吃掉了,真正业务逻辑吃掉的内存少之又少。
    labdum
        32
    labdum  
       135 天前
    主要是最低开销大吧,但是上限对比之下还好

    https://pkolaczk.github.io/memory-consumption-of-async/

    ?si=-OluA5cgtfgahdWp
    kuanat
        33
    kuanat  
       135 天前 via Android   ❤️ 22
    这一套跑起来要多少内存属于常识性问题,不是说那遇到过才知道的。另外企业级代码也要有企业级的运行环境做支撑啊,没有配套的环境谈什么企业级。

    不如反过来你尝试回答这样一个问题,这个需求你还能用别的方式熟练的完成么?不能的话那你没得选,能的话说明决策有问题。

    比如这个需求 mysql 换成 sqlite 不行么,裸写 sql 不行么,甚至说存自定义格式不行么?既然都是 http 请求加 json 解析,换成 Python 不行么,如果 python 不方便部署,换成 go 之类的不行么。

    特别当你知道运行环境就是 1c1g 的时候,更应该考虑这些变通的手段啊。如果换成我的话这个需求我连 self host 都不会用,免费的 cf worker 比你的小主机可用性高太多了。也许你都没考虑过长期维护的事情。这种需求要的是省心,但你现在的选型和实施都没自动化的考量,配置一个简单的 cd/ci 之后这个差距会更大。

    说这么多其实是想表达,不要把完成需求仅仅局限在写代码的层面上,当成项目来管理运营会更容易做出整体上更合理的决策。
    Fule
        34
    Fule  
       135 天前   ❤️ 3
    纯粹说明一下个人对 @labdum 引用的那个文章的看法。

    那个文章的作者大概不是对他列举的每一门语言都足够了解。我对其它语言了解不多,但是一如那个文章回帖里说的,C#的写法不太行,而且都 .NET 6.0 的测试环境了,创建如此大量 Task ,要不要考虑下 ValueTask 的可行性。我相信列举的其它语言的写法也有类似问题,所以那篇文章的参考价值是:一个某某语言的初级程序员在不充分了解语言特性和机制的情况下写出的未优化的代码的内存开销有多遭。
    selca
        35
    selca  
       135 天前
    graalvm 试试
    luzemin
        36
    luzemin  
       135 天前
    @angrylid 你好,佳琦
    kandaakihito
        37
    kandaakihito  
       135 天前
    是这样的,而且 spring 那套脚手架即使是很熟练了起手也得搭半天
    wysnxzm
        38
    wysnxzm  
       135 天前   ❤️ 3
    java 不设置内存大小默认使用物理机 1/4 内存与代码无关,按理说都用 jdk21 了不至于连这个都不知道啊
    xubeiyou
        39
    xubeiyou  
       135 天前
    。。。用 Java 了-- 肯定耗内存啊 这个东西 用地方越小 越不值当
    Blanke
        40
    Blanke  
       135 天前
    都用 java 了还在乎啥内存
    yaott2020
        41
    yaott2020  
       135 天前 via Android
    Java 缺陷就是内存大户,个人用肯定 Python/Go 更好
    cheng6563
        42
    cheng6563  
       135 天前
    @wysnxzm 照你说的是不是一个物理机只能跑 4 个 Java 程序。
    内存占用如此大就是 Hotspot JVM 的设计问题,Hotspot JVM 很不喜欢把已 GC 的内存还给操作系统,换 openj9 就没这问题了
    zbatman
        43
    zbatman  
       135 天前   ❤️ 5
    Javaer 是这样的,JDK 维护人员只要全身心投入到底层,性能优化,添加新特性就可以,可是 Javaer 要考虑的事情就很多了。
    karben
        44
    karben  
       135 天前
    @zbatman 沟槽的公式
    kanepan19
        45
    kanepan19  
       135 天前
    企业级代码 亮了,
    话说,你可以指定堆内存,当然你这 spring 一套下来,200m 也差不多那样了。
    afantwtz
        46
    afantwtz  
       135 天前
    @diagnostics #20 然而这个情况我也会回“不知道”,多一事不如少一事
    louisxxx
        47
    louisxxx  
       135 天前
    你换 PHP 是不是就 10M 了
    VensonEEE
        48
    VensonEEE  
       135 天前
    这种事儿就是 Python 、node 、go 的事儿 ,java 的擅长点在生态利用、以及大型项目的编码组织上。
    没有工具是万能的。
    tomatocici2333
        49
    tomatocici2333  
       135 天前
    对于企业来说内存不值钱
    Navee
        50
    Navee  
       135 天前
    是这样的,可以换 openj9 运行环境,进一步减少内存占用,但还是解决不了“我就提供一个接口为什么占用几十 M 内存”的问题
    janda
        51
    janda  
       135 天前
    @kuanat 老哥思路不错、我之前也是陷入这样的泥潭中,一直都是这种固定思维禁锢了
    liuhuansir
        52
    liuhuansir  
       135 天前
    @Navee 因为 java 就是要占这么多,换语言就可以解决你的问题
    wanniwa
        53
    wanniwa  
       135 天前
    @jackmod #27 小工具用 python 写还是快吧,有 Chatgpt 加持。Chatgpt 写代码,python 基本上是最爽的了
    LPJD
        54
    LPJD  
       135 天前
    才 250M?我们起步都是 2G 的
    xingjue
        55
    xingjue  
       135 天前
    要编译快 要省内存 直接上 go 吧 适合的语言做适合的事情
    zzfly256
        56
    zzfly256  
       135 天前
    在这类数据爬取的定时任务上,轻量点的脚本语言的性价比会更高;
    另外不考虑一下利用 linux 的 crontab 吗,考虑到周期为 10 分钟一次的话,常驻进程应该大量时间都在空跑吧
    RandomJoke
        57
    RandomJoke  
       135 天前
    企业级大部分应用就是承载很多业务了,上手就是几 G 内存了,差距就拉小了,那种业务量很小的,你用个 lua + nginx 写写都行,根本没必要用 java 这种重生态的语言,java 的优势就在于生态非常成熟,就像 spring ,本身是让你依赖管理更加简单,你要是本身应用就很简单,有什么好管理的。
    mezhangkai
        58
    mezhangkai  
       135 天前
    用 quarkus 和 graal 21 拥抱云原生,压力内存,虽然相比 go 还是有些差距,但还是进步非常多。
    w568w
        59
    w568w  
       135 天前
    C/Zig/Rust/Go 占内存都小。说到底为什么要吊死在 Java 上?你也知道资源不足,还用企业级的 JVM 。要么 GraalVM 编译为原生,要么等 Kotlin Native 吧。
    libook
        60
    libook  
       135 天前
    java 默认是按照你物理内存大小按比例自动分配空间的,有配置内存分配的参数,比如 Xms 和 Xmx ,你看看是不是能解决你的问题。
    raptor
        61
    raptor  
       135 天前
    @Rorysky 差很多。我司原来的产品就是 python 写的,后来因为招不到人换 java ,内存使用大了四五倍都不止,不过反正硬件是客户提供的……
    Nitsuya
        62
    Nitsuya  
       135 天前
    我 N1 跑着各种代码, 差不多 400M.


    X86 用 GraalVM 做 native 吧.
    Torpedo
        63
    Torpedo  
       135 天前
    淡定,之前在快手,听服务端的说公司 java 框架启动要 8g 以上的内存。
    x2ve
        64
    x2ve  
       135 天前
    内存和带宽受限时,那种感觉是真难受。要是有能完美模拟浏览器行为且内存开销特别小的工具就好了
    caqiko
        65
    caqiko  
       135 天前
    相比之下一个 MySQL 就要 500MB 不是更吃惊了吗
    wysnxzm
        66
    wysnxzm  
       135 天前   ❤️ 1
    @cheng6563 #42 https://stackoverflow.com/questions/60107793/why-default-java-max-heap-is-1-4th-of-physical-memory 这是 stackoverflow 上对于 jvm 默认内存大小的一个讨论,其中提到从 jdk5 开始就是如此

    jvm 默认使用 1/4 内存对不对且不说,你要是有疑问完全可以给 jdk 开发者提 issue 我非常支持

    但是都是用 jdk21 了还不知道一个从 jdk5 延续至今未变更的特性,这很不合常理吧?
    joyhub2140
        67
    joyhub2140  
       135 天前
    springboot 已经是大规模企业级框架了,默认集成的组件也不少。

    介意内存的话,Java 也有很多小而美的框架。
    z836454898
        68
    z836454898  
       135 天前
    想要性能得用 go
    blackmirror
        69
    blackmirror  
       135 天前
    用 java 根本就不考虑小内存,小内存根本不会用 java
    zhouhu
        70
    zhouhu  
       135 天前
    要看内存实际使用情况吧,Java 申请了 256M ,不一定全部使用了,如果使用率较低,可以使用 xmx 限制一下最大内存。
    zhouhu
        71
    zhouhu  
       135 天前
    @zhouhu 这种小程序估计 128M 能够起得来
    Chinsung
        72
    Chinsung  
       135 天前
    画个靶子自己打可还行,你这个诉求你既然对内存有要求就用 c 和 go ,用 java 就要考虑 GraalVM 。
    你这个例子我可以用来吐槽任何语言。反正把那个语言的短板拿来直接套就完事了
    cqjdcheng
        73
    cqjdcheng  
       135 天前
    .NET,php 30M 就够了
    highFreqSurfer
        74
    highFreqSurfer  
       135 天前
    go 开发路过, 但建议用 nodejs (狗头
    yazinnnn0
        75
    yazinnnn0  
       135 天前   ❤️ 1
    用 quarkus+graalvm 试了一个简单 demo





    放 docker 容器里, 给 20M 内存就能愉快运行了



    让我震惊的是 mysql 啥也不干就吃掉 400+M 内存
    Goooooos
        76
    Goooooos  
       135 天前 via Android   ❤️ 2
    @wysnxzm 我猜那些学了几年 JAVA 的人都不知道,不指定堆大小的时候,默认最大机器内存的 1/4 。
    Features
        77
    Features  
       135 天前
    功能比你复杂很多的 php cli ( workerman )脚本,占用 5.3 MB
    dode
        78
    dode  
       135 天前
    go 写,内存可以回收,java 内存占的是全生命周期内存需求最大的内存,不会释放回操作系统
    satoru
        79
    satoru  
       135 天前
    大量 JVM 微服务,老板会很开心
    cmsyh29
        80
    cmsyh29  
       135 天前
    主要是 boot 自带了很多对象。其实你往上加一些正常业务,内存也不会涨的很明显
    shanai
        81
    shanai  
       135 天前
    用 Rust:)
    elevioux
        82
    elevioux  
       135 天前
    装 php 的时候加上 curl 和 mysql 扩展, 一个脚本搞定。然后让 crontab 定时运行 0 10 * * * php script.php 。

    不运行,0 占用。
    piecezzz
        83
    piecezzz  
       135 天前
    @Goooooos 还真不知道
    kenvix
        84
    kenvix  
       135 天前   ❤️ 1
    优化内存占用从未是 Oracle JVM 的重点,它们只关注吞吐量
    只有 ART 这种移动 JVM 才会稍微关注下这个事情
    wysnxzm
        85
    wysnxzm  
       135 天前
    @Goooooos #76 唉 学而不思则罔,思而不学则殆
    Rever4433
        86
    Rever4433  
       135 天前
    @kuanat 确实,如无必要勿增实体。本来很简单的需求非要用很重的语言和框架来实现,这本身就是错配的。
    loopq
        87
    loopq  
       135 天前   ❤️ 1
    @diagnostics #20 瞎说,培训班出来的也知道命令行 javac 直接运行.java 文件,明明连培训班的都不如
    edisonwong
        88
    edisonwong  
       135 天前
    shell + cron (doge
    Belmode
        89
    Belmode  
       135 天前
    噫嘘唏呜呼哀哉?
    mark2025
        90
    mark2025  
       135 天前
    @angrylid 就“write once run anywhere”来说,有这么难么,js 不也是 write once run anywhere 么
    cdlnls
        91
    cdlnls  
       135 天前 via Android
    @yazinnnn0 MySQL InnoDB 缓存大小,也是可以调的默认好像就是几百 M 。。。
    9c04C5dO01Sw5DNL
        92
    9c04C5dO01Sw5DNL  
       135 天前
    用 rust 重写
    yinmin
        93
    yinmin  
       135 天前 via iPhone
    如果程序不复杂,你可以试试把完整的 java 代码递交给 claude3.5sonnet ,让他改成 python 代码。

    python 的启动速度和内存占用都比 java 好些。 甚至你可以让 claude 改写成 sh 脚本试试。

    (我试过让 poe 里的 claude3.5-200k 把 cloudflare workers 的 js 完整代码改写成 python ,这货给出的代码居然能一次跑通,然后小细节沟通了几次,自己没写一行代码 100%完美运行)
    chainal7777
        94
    chainal7777  
       135 天前
    试试 rust ,可能会变成 9M
    bunny189
        95
    bunny189  
       135 天前 via iPhone
    “我的 hello world 怎么占用了 1 个 G”
    hillwall
        96
    hillwall  
       135 天前
    用 go 或者 node
    agdhole
        97
    agdhole  
       134 天前
    .net 8 aot 见
    levelworm
        98
    levelworm  
       134 天前 via Android
    @powerman 感觉楼主这个功能 C 问题也不大,就是不知道微信有没有 C 接口。
    msg7086
        99
    msg7086  
       134 天前
    @cheng6563 #42 有没有可能,「默认」设置是可以改的。
    fox0001
        100
    fox0001  
       134 天前 via Android
    这个简单需求,可以用 go ,甚至 shell
    1  2  
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2464 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 15:58 · PVG 23:58 · LAX 07:58 · JFK 10:58
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.