V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
MakHoCheung
V2EX  ›  问与答

关于 Java 和 go 高并发的话题

  •  
  •   MakHoCheung · 2021-07-22 22:45:05 +08:00 · 6975 次点击
    这是一个创建于 1221 天前的主题,其中的信息可能已经有所发展或是发生改变。
    go 因为有协程,所以高并发是它的一大优势,但是我看博客,别人说 groutine 的底层也是线程池,这样跟 Java 也一样,为什么 go 在高并发这块就很厉害呢(我不知道 Java 在高并发这块是不是比 go 弱,我没做过什么高并发项目)。发散一下就是 Java 的 NIO +线程池(现实例子就是 netty )跟 go 在高并发这块差距是不是巨大?
    第 1 条附言  ·  2021-07-23 00:36:15 +08:00
    不过我又想到了 Java 的 jdbc 和对外的 http 请求是无法用到 NIO 的,基于 TCP 的 RPC 就可以。所以针对于 Java 后端服务程序,很多情况下还是需要一个请求一条线程来处理,数据库这种请求无法利用 evenloop 来减少线程使用。所以这种情况应该还是不如 go 高效
    93 条回复    2021-08-22 15:59:47 +08:00
    dcalsky
        1
    dcalsky  
       2021-07-22 22:52:14 +08:00   ❤️ 1
    如果底层都是 epoll 那性能都差不多。只不过 goroutine 比较轻量,又有 GMP 帮你调度,关键是还写起来方便心智负担小。

    所以总结起来就是:写并发有优势,而不是并发有优势(相对 java )。
    caliburn1994
        2
    caliburn1994  
       2021-07-22 22:55:48 +08:00
    最近看了一些材料。go 协程比 Java 得线程好,我觉得主要是 Java 堵塞就是堵塞了,一个 goroutine 堵塞了就会启动切换到另外一个 goroutine
    ihipop
        3
    ihipop  
       2021-07-22 22:55:49 +08:00 via Android   ❤️ 1
    go 用线程来管理和执行一个或者多个协程,开 1w 协程无需 1w 线程。
    MakHoCheung
        4
    MakHoCheung  
    OP
       2021-07-22 22:59:29 +08:00
    @ihipop 问题是干一件事,开 1w 个协程干和 Java 用线程数池干,协程更加高效更快?
    MakHoCheung
        5
    MakHoCheung  
    OP
       2021-07-22 23:00:29 +08:00
    @dcalsky 这个回答可以的
    caliburn1994
        6
    caliburn1994  
       2021-07-22 23:01:36 +08:00   ❤️ 1
    另外一点,Java 使用的是 user thread,而 goroutine 使用的是 green thread,green thread 的依赖是 user thread 。越靠近底层 context switch 成本越大。

    Java 的 green thread 是 Loom,还在发展中。
    MakHoCheung
        7
    MakHoCheung  
    OP
       2021-07-22 23:03:30 +08:00
    @caliburn1994 这个怎么做到的呢?堵塞的 goroutine 和切换的新的 goroutine 在底层也是两个不同的线程吧,一个在等 io 一个在运行
    ihipop
        8
    ihipop  
       2021-07-22 23:07:22 +08:00 via Android   ❤️ 1
    @MakHoCheung 自然是的,至少协程没有线程切换 /系统中断开销,开 1w 协程切换开销远远小于 1w 线程,协程比线程轻量的多自然服务能力也可以同步提升。
    caliburn1994
        9
    caliburn1994  
       2021-07-22 23:11:05 +08:00
    @MakHoCheung goroutine 不是线程,而且网上说它是一个复杂的协程。切换的问题就涉及 preemption (抢占),goroutine 现在是 asynchronous preemption (异步抢占)


    等 IO 的那个算是异步的问题。


    (我也在自学中~)
    MakHoCheung
        10
    MakHoCheung  
    OP
       2021-07-22 23:23:31 +08:00
    @ihipop 你误会我的意思了,我的意思是 1w 协程 VS 线程池( 4 条线程),假如 1 个线程里面有 2500 个协程,那 1w 个协程全部切换个遍,底层也要线程切换的吧。另外又延伸一下,2 个 goroutine 进行 2 个 http 请求里面要不要线程切换?
    MakHoCheung
        11
    MakHoCheung  
    OP
       2021-07-22 23:30:45 +08:00
    @ihipop 还有个我不太懂的地方,有什么场景下,2 条 goroutine 同时进行的一样的操作,底层是不需要线程切换的
    caliburn1994
        12
    caliburn1994  
       2021-07-22 23:34:00 +08:00
    建议从 Java 的 1.2 版本的为什么放弃 green thread 开始看,挺有趣的
    iyaozhen
        13
    iyaozhen  
       2021-07-23 00:00:45 +08:00
    "别人说 groutine 的底层也是线程池" 这个不太对吧

    “假如 1 个线程里面有 2500 个协程,那 1w 个协程全部切换个遍,底层也要线程切换的吧” 不用呀,简单来说,第一个线程里面 2500 个协程自己切换,不用切换线程。

    go 的优势是,第一个线程里面有个协程卡了,剩下的 2499 个协程可以调度到另一个线程上去
    MakHoCheung
        14
    MakHoCheung  
    OP
       2021-07-23 00:29:43 +08:00
    @iyaozhen “第一个线程里面有个协程卡了,剩下的 2499 个协程可以调度到另一个线程上去”这里不就有线程上下文切换了吗
    MakHoCheung
        15
    MakHoCheung  
    OP
       2021-07-23 00:31:02 +08:00
    @iyaozhen groutine 的底层也是线程池,这个我看博客的,我不是很懂 goroutine
    wellsc
        16
    wellsc  
       2021-07-23 00:42:57 +08:00
    @dcalsky 并发还得看你是 io 密集还是 cpu 密集呢,不够严谨
    ccde8259
        17
    ccde8259  
       2021-07-23 01:06:50 +08:00 via iPhone
    @MakHoCheung
    发生的是协程上下文切换?
    Leviathann
        18
    Leviathann  
       2021-07-23 01:19:42 +08:00
    主要是 go 写并发程序的心智负担小,且 go 编译后为机器码
    听说 loom 的目标也是类似这种,再加上 graalvm 的 aot 编译,以后应该会差不太多
    不过离正式支持 loom 的 lts 版本 jdk 估计还得等个几年。。
    sutra
        19
    sutra  
       2021-07-23 01:31:21 +08:00
    Servlet 3.0, 3.1 有 Async 、NIO 之类的支持。
    btnokami
        20
    btnokami  
       2021-07-23 01:53:11 +08:00 via iPhone
    协程是在 user space 切换,线程要从 user space 切到 kernel space,开销不一样的。而且线程要在 memory 里自己新建 stack,协程是共享的 stack
    btnokami
        21
    btnokami  
       2021-07-23 02:01:29 +08:00 via iPhone   ❤️ 1
    @MakHoCheung
    并没有线程上下文切换,这几个协程还是跑在同一个 os thread 上。线程上下文切换是指 cpu 上的实体线程里 os thread 切换的过程,这个过程是在会有 user space 到 kernel space 的切换同时 kernel space 里也会做 os thread 的切换,而协程的切换全发生在 user space 里且没有 os thread 的切换
    sagaxu
        22
    sagaxu  
       2021-07-23 02:46:17 +08:00 via Android
    http 怎么就不能用 nio 了? jcbc 怎么就不能了?楼主你太想当然了。
    MakHoCheung
        23
    MakHoCheung  
    OP
       2021-07-23 08:59:18 +08:00
    @sagaxu 我学的 NIO 是 Java 的 IO 多路复用,register 和 select 的 socket,现在 urlconnection 和 jdbc 怎么做到让 selector select 呢?
    MakHoCheung
        24
    MakHoCheung  
    OP
       2021-07-23 09:12:18 +08:00
    @btnokami 这个说得跟官方文档说得一样,但要追究到底层原理,协程遇到了阻塞的 IO 被阻塞了,运行当前代码的线程( os 只有线程)怎么办,很明显为了非阻塞被调度到其他线程去等待 io 吧。当然有某些操作完全可以由线程内部调度,不需要切换线程
    securityCoding
        25
    securityCoding  
       2021-07-23 09:21:17 +08:00 via Android
    一个用户态,一个内核态
    cheng6563
        26
    cheng6563  
       2021-07-23 09:27:05 +08:00
    只要你能一个线程同时处理多个连接,那么性能就是一个级别的。

    区别就是 Java 要用 aio 甚至 nio,写起来麻烦的要死
    用 go 的话直接无脑用类似 bio 的写法就行了。
    slipper
        27
    slipper  
       2021-07-23 09:58:59 +08:00
    底层也是线程,只不过在上层封装了一个 goroutine,这个 goroutine 能让你用同步的逻辑写出异步的代码。

    这个封装层就是 runtime,他还帮你做了 goroutine 调度,也就是上面说的:当线程在 epoll wait 时,让这个线程处理另一个 goroutine 。如果没有这一层,java 中是要你手动实现调度的。调度还是比较麻烦的一件事。

    runtime 同时要帮你进行 goroutine 注册回收挂起等待等一系列帮你减轻编码难度的逻辑。

    goroutine 严格来说是在用户态保存了执行上下文的数据结构。而线程是内核态保存上下文的结构,所以更加轻量。所以线程是进程的执行路径,goroutine 是线程的执行路径。
    sagaxu
        28
    sagaxu  
       2021-07-23 10:08:28 +08:00 via Android
    @MakHoCheung
    https://docs.oracle.com/en/java/javase/11/docs/api/java.net.http/java/net/http/HttpClient.html#sendAsync(java.net.http.HttpRequest,java.net.http.HttpResponse.BodyHandler)

    r2dbc 和 vertx-sql-client 都提供了异步版的 db 访问。但个人觉得异步 db 目前意义不大,并发稍高 db 服务端就挂了,瓶颈并不在 client 这里。db 的瓶颈是 CPU 和磁盘 IO,这两个都害怕高并发。
    MakHoCheung
        29
    MakHoCheung  
    OP
       2021-07-23 10:30:18 +08:00
    @sagaxu r2dbc 这些我知道,但是人家并不是基于 jdbc,我说的是应该最广泛的 jdbc 。然后就是 java11 的 httpclient 的异步请求底层就是基于 completableFuture,实际基于线程池,跟我说的 NIO 或者 eventloop 不是一回事。我说的 NIO 是 IO 多路复用,希望(注意只是我臆想)可以把整个 web 应用的所有(注意是所有)数据库请求等待放到 eventloop 里面,一条线程进行 IO 轮询,另外一条线程处理到来的数据库数据
    btnokami
        30
    btnokami  
       2021-07-23 10:33:47 +08:00
    @MakHoCheung
    并不是,如果当前线程运行遇到了 IO 阻塞,runtime 让当前协成 yield 然后把另一个协成安排到当前线程上跑,线程本身并没有切换。协成的切换不是 pre-emptive 的,而是 cooperative 的。
    MakHoCheung
        31
    MakHoCheung  
    OP
       2021-07-23 10:35:41 +08:00
    @sagaxu 其实我猜想的是目前服务接受可以用 netty 的 reactor 模式处理收到请求,能不能也用 netty 的 reactor 模式处理服务对外发送的请求呢( rpc ),这种 eventloop 跟 connection per thread 对比又会不会高效呢?这都是我的猜想
    MakHoCheung
        32
    MakHoCheung  
    OP
       2021-07-23 10:40:47 +08:00
    @btnokami go 遇到 io 阻塞居然不需要线程切换,我有空好好研究研究
    dqzcwxb
        33
    dqzcwxb  
       2021-07-23 10:44:39 +08:00   ❤️ 5
    ![3KG0M0234Q([email protected]]( )
    btnokami
        34
    btnokami  
       2021-07-23 10:51:52 +08:00
    @MakHoCheung
    也不是完全不需要,go runtime 会首先试着讲 blocking syscall 换成 non-blocking 的版本然后 yield,如果实在不行,go runtime 会建新的 thread 然后讲 blocking call 移过去
    但是大体上来说协程切换并不需要线程切换
    sagaxu
        35
    sagaxu  
       2021-07-23 11:03:34 +08:00 via Android
    @MakHoCheung 那你可以看看 vertx,发起 http 请求也是走 netty eventloop 。我有个项目之前用多线程阻塞(每台 server 1000 或 2000 个线程),后来重构成 vertx(每台 4 个 eventloop 线程),日均十几亿来自 app 的 http 请求,两种模型都扛住了,但非阻塞版的上限更高,至少能扛住 50 亿请求。

    也有使用 Go 开发的服务,并发性能跟 Java 没多少区别,用协程就是写起来简单一点,没有回调地狱。
    3dwelcome
        36
    3dwelcome  
       2021-07-23 11:28:50 +08:00
    @sagaxu 那你改成 UDP 服务器,请求上限更高。

    脱离实际场景,单纯对比数据没意义,33 楼就说了,代码不是不可以改,而是费了那么大功夫,这精力花的值不值。

    具体还是要看服务器上,CPU 瓶颈,还是内存瓶颈,还是网络瓶颈。具体案例具体分析。
    jingslunt
        37
    jingslunt  
       2021-07-23 11:53:40 +08:00
    @iyaozhen
    1. 协程不过是用户态的线程。
    2.在 CPU 密集型任务下,多进程更快,或者说效果更好;而 IO 密集型,多线程能有效提高效率。
    lesismal
        38
    lesismal  
       2021-07-23 12:48:36 +08:00
    普通并发量 go 还是有优势,写起来简单,不容易写出瓶颈,java 如果不异步性能太差,netty 了又 callback hell

    go 标准库方案面对海量并发时协程数量、内存消耗、调度和 GC 等开销太狠,干不过 netty

    我写了份异步库,应该可以干得过 netty:
    https://github.com/lesismal/nbio

    日几十亿这种提法应该是业务、运营数据这样吹牛比较好。对于技术,最好落到 qps/tps 之类的,这种才能算是性能指标。不信你算一下,10 亿请求每日,相当于多少请求每秒:
    >>> print(1000000000/24/3600)
    11574.074074074073

    才 1 w多,二八原则,我算你峰值 10-20w 每秒。这种瓶颈在于数据层的承载力,不在于框架层,比如我上面自己的框架,4c8t 的单节点几十万连接数,echo 压测照样能跑十几、几十万 qps
    aguesuka
        39
    aguesuka  
       2021-07-23 13:14:13 +08:00
    使用到 jdbc 的场景, 请求的绝大部分的性能开销是在数据库, 也就没有必要通过异步来优化性能. 反之使用异步中间件的场景都是 eventloop + epoll, 性能绝不会比等价的 c 语言低太多
    x940727
        40
    x940727  
       2021-07-23 14:11:22 +08:00
    @lesismal 不是 Java 不异步性能太差,而是 Servlet 性能太差,Netty 实现的 Web Server 性能就很明显强过 Servlet,甚至不会比那些 Go 的 Web Server 性能差。说白了还是框架设计上的问题,如果单论语言性能,Java 是要强于 Go 的。
    gollwang
        41
    gollwang  
       2021-07-23 14:12:24 +08:00
    打个比方:Java 的多线程就有点类似于,我要找 10 个人去干 10 件事,每个人干一件刚刚好
    golang 的协程呢,10 个人我可以干 10 件事,也可以干 100 件事,因为当有人干某一件事发生了阻塞,他可以再干其他的事。
    所以就会出现一种情况,当人数是相当的,比如:都是 10 个人,要干的事情是一定的,比如要干 100 件事,但是事情的复杂度不是一样的,那么对应 Java 来说可能就是 10 个人每人干 10 件,时间和工作量调度可能就不是均衡的,对于 go 来说,我 10 个人就不断干事情,可能有的人干的数量多,有的人干的数量少,但是我们的工作量相对来说是均衡的,因为有可能我干一件事,你要干几件事。

    我不知道这样说对不对,纯属个人理解,大佬们别喷
    php01
        42
    php01  
       2021-07-23 14:22:53 +08:00
    GOLANG 中,P 列表和 M 相绑定,极大减少了协程切换带来的系统调用。这就是差距,如果说的不对请大佬们指出。
    MakHoCheung
        43
    MakHoCheung  
    OP
       2021-07-23 14:23:14 +08:00   ❤️ 1
    @gollwang Java 的 forkjoin 框架就有工作窃取,一个线程干完可以干另外一个线程剩下的活
    honkki
        44
    honkki  
       2021-07-23 14:44:33 +08:00
    没有线程频繁的上下文切换、GMP 一个线程管理多个 goroutine 、goroutine 是可伸缩的栈内存大小
    ylsc633
        45
    ylsc633  
       2021-07-23 14:48:14 +08:00
    Carlgao
        46
    Carlgao  
       2021-07-23 15:54:37 +08:00
    说一段我的理解:
    go 的线程会预先创建一批线程,真正执行的线程对应 CPU 数量,每个执行线程都有一个任务队列,没有多线程同时取任务加锁的问题。
    当执行线程阻塞时会从睡眠线程中唤醒一个与原任务队列绑定,这点类似于线程池。
    如果没有阻塞一个任务执行完之后切换下一个就行了,这样避免了线程上下文切换。
    kksco
        47
    kksco  
       2021-07-23 16:09:06 +08:00
    一言以蔽之,golang 的 runtime 在 os 下面又实现了一套自己的规则,好处就是吹得比较多的代码并发无脑写,同步的写法确实心智负担低,开多个 goroutine 没啥压力。缺点也有,c/c++ 大佬想掌控一切的路就被 runtime 堵死了,比如像 nginx 这种亲核性就没办法实现。
    Jooooooooo
        48
    Jooooooooo  
       2021-07-23 16:11:00 +08:00
    你用 netty 就好了.
    lesismal
        49
    lesismal  
       2021-07-23 16:14:23 +08:00
    @x940727
    "不是 java 不异步性能差"
    ——这是最基础的错误认知,所有语言都做不到同步还性能强的(这里的同步是指 fd 阻塞 io,不是指接口同步,go 标准库是 fd 非阻塞但接口同步的),因为同步意味着进程 /线程占用,而进程 /线程数量有限,你 fd 阻塞 io 随便就让线程池都在那等来等去的,处理频率大大降低。所以单比性能的时候不只是以前 c/c++吊打 java 阻塞 io 的框架,连 nodejs 都吊打。
    既然知道 netty 强,你就应该了解为啥强,如果不异步还能高并发高性能,那 erlang/golang 之外的语言还搞异步出来干嘛?


    "如果单论语言性能,Java 是要强于 Go 的。"
    ——醒醒。不同消耗对应的指令性能,java 和 go 各有千秋,但是整体算下来,java 并不比 go 强。
    lesismal
        50
    lesismal  
       2021-07-23 16:20:31 +08:00
    @kksco
    "缺点也有,c/c++ 大佬想掌控一切的路就被 runtime 堵死了,比如像 nginx 这种亲核性就没办法实现。"
    ——我这搞了一份 poller 的,协成、线程数量仍然可控,业务协程数量可控并且可以做到业务代码同步、网络层异步、性能、内存各方面的平衡:
    github.com/lesismal/nbio

    只是:go 的指令、runtime 、gc 这些,确实让 go 性能比 c/c++还差很多

    更多细节有在另一个异步库 gev issue 中做更多的阐述:
    github.com/Allenxuxu/gev/issues/4
    x940727
        51
    x940727  
       2021-07-23 17:13:00 +08:00
    @lesismal 嗯。Java 不比 Go 强……我就只问你一个问题,如果是现在那些互联网项目,高并发,高吞吐量的场景下,Go 的 GC 表现怎么样?和 JVM 比?为什么头条现在也在大规模的招聘 Java 的程序员?为什么不继续用 Go ?说白了 Go 的适用场景还是在那些要求低延时,低吞吐量的基础建设场景,真去抗大规模流量和 Java 比就差远了(有时间优化的另说,类似 Facebook 的 PHP hhvm )。
    x940727
        52
    x940727  
       2021-07-23 17:15:41 +08:00
    @lesismal Java Servlet 同步性能依旧不好的原因就是因为在对象的生命周期中做了太多的事情,不然并不会比那些很快的同步 Web 框架差多少的。 [普通并发量 go 还是有优势,写起来简单,不容易写出瓶颈,java 如果不异步性能太差,netty 了又 callback hell] 这句话是你说的,你说人家最基础的错误认知?
    MakHoCheung
        53
    MakHoCheung  
    OP
       2021-07-23 17:18:04 +08:00
    @x940727 头条什么时候开始找 Java 了?我问字节的朋友招不招 Java,他都说字节不招 Java
    lesismal
        54
    lesismal  
       2021-07-23 18:07:59 +08:00
    @x940727 go 的 gc 整体表现还真是比 java 强,而且特定业务或者框架用 pool 优化,内存、gc 更优。我自己那个库,就用了大量的 pool 优化。我们很多项目也是很多语言,对比 java 和 go 的项目,go 内存、cpu 占用各项指标都吊打 java 。

    "为什么头条现在也在大规模的招聘 Java 的程序员?"
    ——兄弟,分析问题要学会逻辑理清晰一点。大规模招聘 java 不等于不继续用 go,完全没有证据表明字节没有继续大量用 go 。而招聘这个主要涉及几个方面,一是业务线,比如偏向电商、后台、企业级等 java 已经有成熟的社区积累的,市场上也有大量的该领域的 java 从业者,反观 go,一共才诞生了多少年,社区和从业者数量没得比,字节这种业务扩张猛的,想大量招 go 的人都招不到,然后优势 java 成熟领域,何必招 go ?反而是性能敏感的领域,比如中间件、云服务这些各种基础设施,你看有几个用 java 做的?包括比如以前 ELK,现在都变成 EFK 了,为啥?你 java 的 logstash 太渣了啊!而且这还只是 go 出生太晚,如果跟 java 同时代出生,怕是没 java 啥事了,很多 appache 顶级项目怕是会用 go 实现了

    "Java Servlet 同步性能依旧不好的原因就是因为在对象的生命周期中做了太多的事情"
    ——我不了解 java servlet,但是你们之前提到他是非异步的。如果你们确定他是同步的、阻塞 fd,那这太基础了,阻塞 fd 对线程资源的浪费肯定是影响响应的最大因素(除非你故意写对象生命周期巨大消耗的示例、让对象生命周期这些的影响超过阻塞 fd 的影响)。
    lesismal
        55
    lesismal  
       2021-07-23 18:09:43 +08:00
    @x940727

    [普通并发量 go 还是有优势,写起来简单,不容易写出瓶颈,java 如果不异步性能太差,netty 了又 callback hell]
    ——java 我肯定是不熟,但是 netty 异步 callback 应该没问题吧?我前面这句话有什么问题的话,请你指正。
    lesismal
        56
    lesismal  
       2021-07-23 18:15:00 +08:00
    @x940727

    "不然并不会比那些很快的同步 Web 框架差多少的"
    ——这里提到的 "那些很快的同步 Web 框架" 是指哪些框架?恕我见识少,还没听说过有高性能的同步框架(跟之前提到的一样,这里的同步是指同步 IO,不是指 erlang/golang 这种语言级同步)
    lesismal
        57
    lesismal  
       2021-07-23 18:24:43 +08:00
    @x940727 同步 io 与异步 io 什么区别? select 跟 epoll 什么区别? java 同步库跟 NIO/Netty 什么区别?互联网最早期时进程池模型线程池模型就是同步 io,为什么后来又要搞异步框架?异步 io 你有深入了解过吗?读相关的书或者看优秀项目源码甚至自己手撸过吗? CSAPP,APUE,UNP,内核相关的书,java 高并发网络相关的书,或者其他的优秀的系统层网络层优秀的书有真正认真读过并且理解了吗?
    至少从前面几楼你的一些措辞,确实是基础认知的欠缺,如果没有花很多时间在我提问的这些上,建议多研究下,可以等先去确认下自己搞懂了再说
    BBCCBB
        58
    BBCCBB  
       2021-07-23 19:32:03 +08:00
    java 里查数据库不一定要用 jdbc, 比如 vertx 里就是自己封装的. 还有 r2dbc 这种, 数据库官方都在支持..

    不过没有语言级别的支持.

    只是用 reactor 或者回调的化还是不够友好.
    x940727
        59
    x940727  
       2021-07-23 20:11:35 +08:00   ❤️ 2
    @MakHoCheung 招的 2B 业务很多很多都是 Java 的,我认识一个人就是在字节写 Java 。
    @lesismal 不了解就是 Go 的 GC 整体比 Java 强? OMG,什么时候 Go 的 GC 能打得过 JVM 的 GC 了,我一个 Java 菜鸡瑟瑟发抖……不谈了,Go 宇宙第一好么。还有 ELK 这个事情,你知道 ElasticSearch 就是 Java 写的吗?还有为什么变成 EFK 了,人家 elastic 推荐的是 ELKF,filebeat 就专门拿来读取文件,logstash 提供 grok 啥的,在你眼里反正 Java 就是不行,我也没办法劝服你,Java 就是弱鸡好吧……
    x940727
        60
    x940727  
       2021-07-23 20:32:45 +08:00
    @lesismal #57 我不知道你这个蜜汁自信从哪里来的,你问的这些问题,你是面试八股文背多了吗?
    这些基础的东西有什么好在这里逼叨叨的,就你看过 CSAPP ?还有你自己说不清楚 Java,然后在这里指点 Java 的江山?不好意思,我看过 CSAPP,也看过 MIT 6.824,CMU 15-213,还有现代操作系统这些的,至于 Java 的并发书籍,我看过火车头,JVM 的我看过 Oracle Java 虚拟机规范的和周志明写的深入理解 Java 虚拟机,二三版都看过。
    我建议你少在这里背八股文,你自己说的 [java 如果不异步性能太差] 事实就是 Java 的语言性能已经是目前来说所有语言里面排得上前列的了,不然那么多顶级工程师优化 JVM 不是白优化了吗?
    还有你说的 [netty 了又 callback hell] 这个大把最佳实践教你消除回调地狱,你自己不了解就别说 netty 就一定会有这种模式。
    还有 Web 框架的性能排名 https://www.techempower.com/benchmarks/#section=data-r20&hw=ph&test=composite 你可以在这个页面里面自己看,Java 的异步框架的排名是非常靠前的,我说 Servlet 慢就是因为。
    x940727
        61
    x940727  
       2021-07-23 20:36:23 +08:00
    @lesismal #56 我不知道你这个里面说的是什么玩意,Netty 就是同步非阻塞 IO 模型,你的意思是 netty 的性能很差?麻烦你搞明白 AIO 和 NIO 的区别。
    lesismal
        62
    lesismal  
       2021-07-23 20:36:35 +08:00
    @x940727

    "不了解就是 Go 的 GC 整体比 Java 强"
    —— 我不了解 java,但是了解 go,从 java 的 jvm gc 表现看,go 确实强。比如 stw,比如高阶需要手动去调 java gc 策略然而即使调还是 stw 时间比较长,比如 java 吃内存,号称宇宙第一 gc 算法,然而同时也是宇宙第一吃内存怪兽。go 的 gc 自 1.8 以后已经非常强,而且除非代码本身 cpu 打满之类的否则基本不会有所有线程 stw 的情况,并且单个线程 gc 时长也比 java 小,如果这都不算强,那 java 这种号称 gc 算法最强但实际效果拉垮的 gc 赢了我 go 辩不过

    "你知道 ElasticSearch 就是 Java 写的吗?"
    —— 我上面已经说过了,这是历史原因,go 出生的晚,如果 go 早生,java 就不会如今这么繁荣了。但是话说回来,go 也是站在以前很多语言肩膀商取其精华去其糟粕,比如像 java 的臃肿、嘴炮 gc,go 就当成糟粕而没有采用,而是在 c lisp 之类的简洁基础上更加简洁、借鉴 erlang 之类的并发模型但又不限于 actor 所以让编程姿势更加通用,编译上除了动态链接库那是没办法、静态链接库直接打包到二进制内方便部署。优点很多,不一个一个说了。
    ES 是 java 社区较早的积累,而且这种重量的基础设施不只是简单的语言实现、还涉及到整个社区的培养,所以不可能像 F 那样相对轻量的项目可以迅速替换掉 L 那种。
    用这个举例,跟你之前用头条招聘举例子是一样的,没有辩论的逻辑性。比如我之前说用 F 替换 L,是能证明 java 有的项目不行所以用 go 取代。但你举出 ES 的例子却不能说明是 go 没能力去取代,而是 go 没有适合的时机去替代,尤其是 ES 这种社区、商业都发展的比较成熟的项目。
    单就网络库、框架,我上面问你的那些,我都搞过多年了,java 虽然不熟悉,但是也手撸过 java 的网络库,c/c++/go 的都撸过,进程池、线程池各种框架层的东西、性能优化都是我日常工作内容
    x940727
        63
    x940727  
       2021-07-23 20:49:29 +08:00   ❤️ 1
    @lesismal #54 Go1.6 中的 gc pause 已经完全超越 JVM 了吗? - RednaxelaFX 的回答 - 知乎
    https://www.zhihu.com/question/42353634/answer/94460053 这是 R 大的回答,这个人应该算是虚拟机界中最顶流的人了,你自己看看人家咋说的。你如果只是拿内存和 CPU 占用率来谈论,那咱们确实是没啥好说的,占用低就是好?是个正常写代码的人就不会这么认为吧? Java 不是没有低暂停的 GC 实现,也不是没有超大吞吐的 GC 实现,但是这两者很难兼得。Go 只是取了一个比较均衡的点而已,你说吊打 Java,那真的是没啥好谈的。Go 宇宙第一……
    wanguorui123
        64
    wanguorui123  
       2021-07-23 20:51:43 +08:00
    Go 是语法层面默认支持异步调用,Java 需要第三方库实现,方便程度和可读性不如 Go,其实都可以做一样的事情。
    x940727
        65
    x940727  
       2021-07-23 20:56:59 +08:00   ❤️ 2
    @lesisma 算了,没啥好谈的,Go 的 GC 吊打 Java 的 JVM GC,JVM 的 GC 团队都是嘴炮侠……从你的回答我就明白你为什么会喜欢 Go,看不上别的语言了,万物都手搓,难怪和个狂信徒一样。麻烦多看看世界,不是只有 Go 在进步。
    x940727
        66
    x940727  
       2021-07-23 21:03:20 +08:00
    @lesismal #62 还有如果你真的懂 GC 的话,你就知道 Go 的 GC 压根没想和 JVM 去竞争,都不是一个方向的实现,难道 Java 就没有 low pause gc?
    lesismal
        67
    lesismal  
       2021-07-23 21:14:26 +08:00
    @x940727 我铜币不够了,你先看下你引用帖子日期吧,其他的我明天说
    x940727
        68
    x940727  
       2021-07-23 21:26:18 +08:00
    @lesismal 不想和你争论了,Go 无敌,JVM 垃圾!
    lesismal
        69
    lesismal  
       2021-07-23 21:35:04 +08:00
    @x940727 go 不无敌,但 jvm 确实垃圾。
    ahmcsxcc
        70
    ahmcsxcc  
       2021-07-23 21:35:42 +08:00
    @x940727 #63
    这个是 go 1.6 的回答,现在 go 已经到 1.17 了。这期间 go gc 的优化挺大的。
    x940727
        71
    x940727  
       2021-07-23 21:53:24 +08:00   ❤️ 1
    @lesismal 不想和你争论了,Go 无敌,JVM 垃圾!
    @ahmcsxcc Java 也到了 16 马上就 17 了,也引入了 ZGC,Shenandoah 这些更加现代的 GC,为什么非要盯着十几年前的 CMS 和七八年前的 G1 呢?我都说了,这个世界又不是只有 Go 在进步。
    hpeng
        72
    hpeng  
       2021-07-23 21:59:21 +08:00 via iPhone
    go 用起来简单,但是真要大规模用,不太好用。
    beidounanxizi
        73
    beidounanxizi  
       2021-07-23 22:08:25 +08:00
    go runtime gmp 用户态的 goroutine
    iyaozhen
        74
    iyaozhen  
       2021-07-24 00:11:37 +08:00
    @x940727 为什么头条现在也在大规模的招聘 Java 的程序员?你听谁说的
    内部没看见这个风向,而且内部那一堆库很多压根都没 java 版本。当然我可能盲人摸象,但没看出要用 java 的理由。注:这里并不是 java 不好,只是字节在特定历史时期选择了 go
    iyaozhen
        75
    iyaozhen  
       2021-07-24 00:25:36 +08:00
    恰好 java 和 go 的团队都待过

    除去特殊的高并发场景(比如 IM ),两者工程上写起来差不多。个人看到的差异:
    1. 因为 go 新一点,所以很多理念新一点,比如全面面向云原生。我们之前 java 还在 spring mvc+jdk1.6 折腾(不是说 java 不行,是因为有历史包袱)
    2. java 启动确实比 go 慢,实际看到的项目 go 10s 左右,java 2-3 分钟。java 就打出的 jar 包有 1G 多(不含静态资源)
    3. java 确实占内存一点
    4. go 项目大了也确实很“百花齐放”,到处都是 interface{},缺少 spring 这样大统一的框架,有时候还要解决框架问题,只能找内部资料
    iseki
        76
    iseki  
       2021-07-24 00:29:50 +08:00
    Java 传统的那一套 J2EE 体系里的东西(包括 JDBC 和 serverlet 等等)都是基于会占用线程的阻塞 io 的,高并发下不可避免占用大量线程资源,而线程又比较贵所以就······
    如果你不使用这套传统生态的话, 新的库基本上都是基于非阻塞 io 的,如果你可以切换到 Kotlin,使用 Kotlin Coroutine 等方案也能享受到协程。
    Go 原生就有协程,没历史包袱,生态的东西自然对协程都是一级支持。

    至少在 Web 开发上个人比起 Go 更倾向选择 Kotlin,在语言特性上,Go 还是太缺乏了,就算说是“大道至简”,这也太简陋了。而使用 Kotlin,有并发需求的场景,完全可以选择规避 Java 传统的那一套生态。当然这个问题见仁见智了。
    x940727
        77
    x940727  
       2021-07-24 01:39:04 +08:00 via iPhone
    @iyaozhen 你可以自己去头条的招聘网站看到底招不招 Java,还有我认识就有在头条写 Java 的,2B 业务……哪个业务线没问太具体,而且 thrift 又没限制语言,Java 人更多,大规模数据,流量下都有更成熟的解决方案,为啥不选 Java ?
    x940727
        78
    x940727  
       2021-07-24 01:55:51 +08:00 via iPhone
    @iyaozhen 难道 Java 做不了抖音的后台?就像上面那个狂信徒一样,他带队从头做电商会选 Java 做?肯定还是用 Go 去做,说白了,用什么技术,什么语言并不是一定非要用这个,可能就是因为带头大哥熟悉这个,或者 KPI 任务(重构)而已……
    BBCCBB
        79
    BBCCBB  
       2021-07-24 11:40:06 +08:00
    @lesismal 事情不是非 1 即 0, 非黑即白, 不要太偏执,

    java 里 gc 可选择的很多, 低暂停的 gc 做的比 go 的 gc 好的也有很多. zgc, Shenandoah, 商业的 zing 等.

    java 内存这块其实和 go 相差无几, 只是现在开发比如套上一层 spring. 框架占的内存是大头, 但是这类框架抽象成都高, 很好用, 能简化开发.. 所以大家依然愿意用. java 和 go 裸写内存占用区别真的不大..
    BBCCBB
        80
    BBCCBB  
       2021-07-24 11:42:53 +08:00
    @lesismal 虽然 go 的 内置协程支持, native 编译这些的确是优点, 但缺点依然不少. 我工作用 java, 不过也是在不断的学 go..
    iyaozhen
        81
    iyaozhen  
       2021-07-24 15:22:50 +08:00
    @x940727 注意你的定语,大规模。一个公司不可能就一个语言,招 JAVA 、Python 、C++都有,这不是很正常,有招聘信息并不代表大规模呀。就像阿里里面难道就没用 go 的?但阿里说到底还是一个 java 为主的。在内部并没有看到有大规模转 java 的迹象。java 的讨论群几天没人说话,go 的群都满了。

    我前面也说了,并不是字节选择了 Go,go 就天下第一,也不是阿里选择了 java java 就第一,很多时候选择只是特定时期、特定的背景(就像你说的带头大哥)。事实上整个互联网还是 java 多(成熟)一点,大而全,不用考虑选 JAVA 没错,web 、大数据啥都能做。

    go 的优势就是新,没有历史包袱。我为了高并发用 netty,那为什么不直接用 go 呢?学习成本都差不多,反而还能抛开 servlet 的束缚。
    x940727
        82
    x940727  
       2021-07-24 16:00:56 +08:00 via iPhone
    @iyaozhen 你是头条的你就可以看看你们自己的社招网站的 hc 啊…昨天还在脉脉看到有人问头条 Java 是不是边缘部门,你们头条自己人说 Java 越来越多了,招聘网站也是 Java 比 go 多,这不叫大规模我也不知道什么叫大规模了……非要把旧的 go 项目用 Java 写才叫?那不太可能的
    iyaozhen
        83
    iyaozhen  
       2021-07-25 01:39:16 +08:00 via Android
    @x940727 那是你不知道,搞 go 的有好多,字节有多大,现在 JAVA 真的太少了。
    https://mp.weixin.qq.com/s/1dgCQXpeufgMTMq_32YKuQ
    字节跳动的技术体系以 Golang 语言为主。根据最新的调查统计,公司里有超过 55 % 的服务是采用 Golang 的,排名第二的语言是前端的 NodeJS,之后是 Python 、JAVA 、C++,Rust 也有一些使用。
    lysS
        84
    lysS  
       2021-07-25 10:35:59 +08:00
    lesismal
        85
    lesismal  
       2021-08-08 12:08:05 +08:00
    @BBCCBB

    没有非黑即白,也不狂热。

    我从来没说 go gc 完胜 java 。我楼上原文是:
    "go 的 gc 整体表现还真是比 java 强,而且特定业务或者框架用 pool 优化,内存、gc 更优"
    ——注意,是整体表现。

    比如引用的知乎帖子,是 2016 年 go1.6,现在已经是 2021 、go1.16 了,10 个大版本了并且中间 1.8 的小对象 gc 更进步了一些。
    即使算上 java gc 有很多选择,或者 zgc,golang 还有 pool 一样可以优化更多,我上面有提到自己项目里就用了大量的 pool,内存占用、gc 开销的节约也是非常巨大的,比如压测能省几倍的内存分配,相应的对象少了非常多、gc 当然也省非常多。

    "java 内存这块其实和 go 相差无几, 只是现在开发比如套上一层 spring. 框架占的内存是大头, 但是这类框架抽象成都高, 很好用, 能简化开发.. 所以大家依然愿意用. java 和 go 裸写内存占用区别真的不大.."
    —— 这又是另一个层次的问题,java 最成功的的地方在于社区强,社区框架、方案一把梭,让小白也能写高级的东西,所以绝大多数项目也是离不开那些占用高的框架,而 go 即使框架,也不占用那么多。如果只用裸 java,还有人用它吗?或者用裸 java 还能让绝大多数人写出稳定高性能的业务代码吗?最好结合线上实际表现来谈语言,而不是都裸语言、理论上对比,毕竟外头很多性能而是对比框架性能,所以既然社区框架方案是绝大多数人的必需品,那就尽量别抛开框架单聊裸 java 了吧,否则咱再把开发效率等其他指标也带上一块对比

    另外,java gc 理论上确实最强,我也没否认。但是如果实际业务中表现很差但是按照为了对比 gc 而专门写的例子来对比,那 java 应该是大概率赢了。但是对于 gc 和内存,还有一点请注意:实际业务的服务,java 太吃内存了,同样的功能占用的内存比 go 多太多,响应速度也慢一些,实际效果就是这样。如果同等业务 go 只需要 100M,java 需要 200M,然后对比 gc 非要让 go 也占用到 200M 来跟 java 对比 gc ?那可能确实没什么优势,然而实际的表现就是我说的那样,绝大多数同类业务未对内存占用和 gc 优化过的服务对比,cpu 、内存各项指标 go 都优于 java 很多

    我之前的回复,有的地方同步、异步、io 的概念没说的太细,因为直接网页上回复,而且无法编辑修改了。

    @x940727

    补充一点:
    "不是 java 不异步性能差"
    ——这是最基础的错误认知
    我说的同步异步不只是 fd io syscall,而是框架 handler 函数内的,如果同步,比如你操作数据库,线程数量有限,肯定没法快的

    另外:
    "如果单论语言性能,Java 是要强于 Go 的。"
    由于这句,所以我之前的回复可能比较激烈,如果有冒犯,那抱歉了,请见谅,咱们就技术聊技术

    还有关于所谓的 "八股文" 也需要解释下:

    "Servlet 同步性能依旧不好的原因就是因为在对象的生命周期中做了太多的事情"
    "java 如果不异步性能太差,netty 了又 callback hell"
    —— 这两点,不管 Servlet 还是 netty,你都考虑下在 handler 里操作数据库的业务,java 的线程数量少、并发量的 CPU 利用率就不够用了,很多 request 要等待,所以还是需要异步。
    java 好像也有一些协程库,但是好像还没人敢大规模使用吧?

    出于之前的这些探讨,所以才会问你所谓的 "八股文",并且,不是每个人都只是为了面试而背八股文,做底层框架或者分析底层框架的性能影响的方方面面,确实就是需要用这些 "八股文",再并且,即使读够这些 "八股文",纸上得来终觉浅啊兄弟,你如果没去实际玩过这些基础设施的设计、实现、优化,说真的,很多痛点你 get 不到。

    基础设施的设计和优化,涉及大量的系统知识,我自己的项目,就是整天要考虑这些 "八股文",有兴趣可以来看下,欢迎交流、指正:
    https://github.com/lesismal/nbio
    lesismal
        86
    lesismal  
       2021-08-08 12:09:58 +08:00
    攒币不容易,上次没币了才发现,原来发帖、回复这么费币,得每天签到才行了
    kksco
        87
    kksco  
       2021-08-09 12:01:41 +08:00   ❤️ 1
    lesismal
        88
    lesismal  
       2021-08-09 16:25:52 +08:00
    @kksco thanks!
    kksco
        89
    kksco  
       2021-08-09 21:02:21 +08:00
    @lesismal 大佬,方便加个微信吗
    kksco
        90
    kksco  
       2021-08-09 21:03:20 +08:00
    @lesismal MTMxNzM2ODAwMTg=
    lesismal
        91
    lesismal  
       2021-08-10 10:09:29 +08:00
    @kksco 社交太费时间了,前几天有几位加微信的,聊起技术就刷屏了,导致我微信还被鹅厂 AI 判定骚扰 /恶意营销了。v 站、github 、邮件交流吧:joy:
    zavierx
        92
    zavierx  
       2021-08-22 14:45:05 +08:00
    @btnokami 怎么可能,阻塞式 IO,当前线程肯定会陷入系统调用等待返回。这种情况,golang 是将 P 与当前阻塞的线程 M 解绑,创建一个新的线程 M 与 P 绑定,继续处理 P 中的 G 。所以 golang 多个线程,除了与 P 绑定的 M 外,其它都在陷入系统调用
    btnokami
        93
    btnokami  
       2021-08-22 15:59:47 +08:00 via iPhone
    @zavierx 是,我之前对这个理解有误,你说得对
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4025 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 05:13 · PVG 13:13 · LAX 21:13 · JFK 00:13
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.