老板让我写个 BUG!

前语

标题没有看错,真的是让我写个 bug

刚接到这个需求时我心里没有一点点波涛,乃至还有点激动。这可是我专长啊;总算能够光明磊落的写 bug 了。

先来看看详细是要干啥吧,其实首要便是要让一些负载很低的服务器额定耗费一些内存、CPU 等资源(至于布景就不多说了),让它的负载能够进步一些。

JVM 内存分配回忆

所以我刷刷一把梭的就把代码写好了,大约如下:

写完之后我就在想一个问题,代码中的 mem 目标在办法履行完之后会不会被当即收回呢?我想必定会有一部分人以为便是在办法履行完之后收回。

我也正儿八经的去调研了下,问了一些朋友;果不其然的确有一部分以为是在办法履行完毕之后收回。

那现实状况怎么呢?我做了一个试验。

我用以下的发动参数将方才这个运用发动起来。

java -Djava.rmi.server.hostname=10.xx.xx.xx 
-Djava.security.policy=jstatd.all.policy 
-Dcom.sun.management.jmxremote.authenticate=false 
-Dcom.sun.management.jmxremote.ssl=false 
-Dcom.sun.management.jmxremote.port=8888  
-Xms4g -Xmx4g  -jar bug-0.0.1-SNAPSHOT.jar

这样我就能够经过 JMX 端口长途连接到这个运用调查内存、GC 状况了。

假如是办法履行完毕就收回 mem 目标,当我分配 250M 内存时;内存就会有一个显着的曲线,一起 GC 也会履行。

这时调查内存曲线。

会发现的确有显着的涨幅,可是之后并没有当即收回,而是一向坚持在这个水位。一起左面的 GC 也没有任何的反响。

用 jstat 检查内存布局也是相同的状况。

不论是 YGC,FGC 都没有,仅仅 Eden 区的运用占比有所添加,究竟分配了 250M 内存嘛。

那怎样才会收回呢?

我再次分配了两个 250M 之后调查内存曲线。

发现第三个 250M 的时分 Eden 区达到了 98.83% 所以再次分配时就需求收回 Eden 区发作了 YGC

一起内存曲线也得到了下降。

整个的换算进程如图:

因为初始化的堆内存为 4G,所以算出来的 Eden 区大约为 1092M 内存。

加上运用发动 Spring 之类耗费的大约 20% 内存,所以分配 3 次 250M 内存就会导致 YGC

再来回忆下方才的问题:

mem 目标既然在办法履行完毕后不会收回,那什么时分收回呢。

其实只需记住一点即可:目标都需求废物收回器发作 GC 时才干收回;不论这个目标是局部变量仍是全局变量。

经过方才的试验也发现了,当 Eden 区空间缺乏发作 YGC 时才会收回掉咱们创立的 mem 目标。

但这儿其实还有一个躲藏条件:那便是这个目标是局部变量。假如该目标是全局变量那仍然不能被收回。

也便是咱们常说的目标不可达,这样不可达的目标在 GC 发作时就会被以为是需求收回的目标然后进行收回。

在多考虑下,为什么有些人会以为办法履行完毕后局部变量会被收回呢?

我想这应当是记混了,其实办法履行完毕后收回的是栈帧

它最直接的成果便是导致 mem 这个目标没有被引证了。但没有引证并不代表会被立刻收回,也便是上面说到的需求发作 GC 才会收回。

所以运用的是上面说到的目标不可达所选用的可达性剖析算法来标明哪些目标需求被收回。

当目标没有被引证后也就以为不可达了。

这儿有一张动图比较明晰:

当办法履行完之后其间的 mem 目标就相当于图中的 Object 5,所以在 GC 时分就会收回掉。

优先在 Eden 区别配目标

其实从上面的比如中能够看出目标是优先分配在新生代中 Eden 区的,但有个条件便是目标不能太大。

曾经也写过相关的内容:

大目标直接进入老时代

而大目标则是直接分配到老时代中(至于多大算大,能够经过参数装备)。

当我直接分配 1000M 内存时,因为 Eden 区不能直接装下,所以改为分配在老时代中。

能够看到 Eden 区几乎没有变化,可是老时代却涨了 37% ,依据之前核算的老时代内存 2730M 算出来也差不多是 1000M 的内存。

Linux 内存检查

回到这次我需求完结的需求:添加服务器内存和 CPU 的耗费。

CPU 还好,自身就有必定的运用,一起每创立一个目标也会耗费一些 CPU。

首要是内存,先来看下没发动这个运用之前的内存状况。

大约只运用了 3G 的内存。

发动运用之后大约只耗费了 600M 左右的内存。

为了满意需求我需求分配一些内存,但这儿有点需求考究。

不能一向分配内存,这样会导致 CPU 负载太高了,一起内存也会因为 GC 收回导致占用也不是特别多。

所以我需求少数的分配,让大多数目标在新生代中,为了不被收回需求坚持在百分之八九十。

一起也需求分配一些大目标到老时代中,也要坚持老时代的运用在百分之八九十。

这样才干最大极限的运用这 4G 的堆内存。

所以我做了以下操作:

  • 先分配一些小目标在新生代中(800M)坚持新生代在90%
  • 接着又分配了老时代内 *(100%-已运用的28%);也便是 2730*60%=1638M 让老时代也在 90% 左右。

作用如上。

最首要的是一次 GC 都没有发作这样也就达到了我的意图。

终究内存耗费了 3.5G 左右。

总结

虽然这次的需求是比较奇葩,但想要准确的操控 JVM 的内存分配仍是没那么简单。

需求对它的内存布局,收回都要有必定的了解,写这个 Bug 的进程的确也加深了形象,假如对你有所协助请不要小气你的点赞与共享。

vwin娱乐场

宣布我的谈论

撤销谈论
表情 插代码

Hi,您需求填写昵称和邮箱!

  • 必填项
  • 必填项