Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >没错,老板让我写个 BUG!

没错,老板让我写个 BUG!

作者头像
crossoverJie
发布于 2022-10-27 02:54:26
发布于 2022-10-27 02:54:26
19300
代码可运行
举报
文章被收录于专栏:crossoverJiecrossoverJie
运行总次数:0
代码可运行

前言

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

刚接到这个需求时我内心没有丝毫波澜,甚至还有点激动。这可是我特长啊;终于可以光明正大的写 bug 了🙄。

先来看看具体是要干啥吧,其实主要就是要让一些负载很低的服务器额外消耗一些内存、CPU 等资源(至于背景就不多说了),让它的负载可以提高一些。

JVM 内存分配回顾

于是我刷刷一把梭的就把代码写好了,大概如下:

写完之后我就在想一个问题,代码中的 mem 对象在方法执行完之后会不会被立即回收呢?我想肯定会有一部分人认为就是在方法执行完之后回收。

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

那事实情况如何呢?我做了一个试验。

我用以下的启动参数将刚才这个应用启动起来。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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 对象就相当于图中的 Object5,所以在 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 的过程确实也加深了印象,如果对你有所帮助请不要吝啬你的点赞与分享。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-12-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 crossoverJie 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
jvm最全详解-05-JVM调优工具详解及调优实战[通俗易懂]
事先启动一个web应用程序,用jps查看其进程id,接着用各种jdk自带命令优化应用
全栈程序员站长
2022/09/02
4960
jvm最全详解-05-JVM调优工具详解及调优实战[通俗易懂]
【干货】JVM 优化、内存泄露排查、gc.log 分析方法等
本文讲解了 JVM 的内存划分和分配策略,并以截图和脚本展示常用可视化和命令行工具的使用方法,完整演示了 JVM 优化、内存泄露排查、gc.log 分析方法等。
IT技术小咖
2019/06/26
5.8K0
【干货】JVM 优化、内存泄露排查、gc.log 分析方法等
JVM实战—3.JVM垃圾回收的算法和全流程
如果新生代里的对象越来越多,当新生代快满的时候就会触发垃圾回收。把新生代里没有被引用的对象给回收掉,释放内存空间,而这就是新生代的垃圾回收触发时机。如下图示:
东阳马生架构
2025/03/13
1960
GC和垃圾回收器其二
CMS是并行标记回收器,使用标记-清除算法进行收集。适用于对时延要求较高的在线服务,不接受长时间停顿的那种。但是如果服务运行较长时间,会造成严重的内存碎片。
春哥大魔王
2019/07/31
5590
GC和垃圾回收器其二
JVM简介—2.垃圾回收器和内存分配策略
给对象添加一个引用计数器,每当一个地方引用它时就将计数器加1,当引用失效时就将计数器减1,任何时刻计数器为0的对象都不再被使用。
东阳马生架构
2025/03/10
1280
JVM实战—6.频繁YGC和频繁FGC的后果
在堆内存中会放业务系统创建出来的各种对象,而且通常都会将堆内存划分为新生代和老年代两个内存区域。对象一般都会优先放在新生代中,如下图示:
东阳马生架构
2025/03/17
1800
JVM学习第二天(垃圾回收器和内存分配策略)大章
说道垃圾回收器大家应该都会有所了解,GC白,当然说道具体的可能就不是很清楚了,今天我们就来玩一玩;
彼岸舞
2020/09/30
4630
jvm 调优命令_java jvm调优工具
所谓的标准参数,就是不会随着我们JDK 变化而变化版本的参数 这种参数可以通过Java -help查看(和Java -version使用方式一样)
全栈程序员站长
2022/11/17
1.1K0
jvm 调优命令_java jvm调优工具
JVM实战—4.JVM垃圾回收器的原理和调优
一般在之前多年里,如果没有最新的G1垃圾回收器的话,通常线上系统都是用ParNew垃圾回收器作为新生代的垃圾回收器。当然后来即使有了G1,很多线上系统还是用ParNew。
东阳马生架构
2025/03/14
880
G1原理—4.G1垃圾回收的过程之Young GC
设置新生代最小值和最大值。注意,在G1里这个最大值最小值其实是可以不设置的。G1会自动计算出一个值:从5%的Region数量开始,慢慢增加到最大为60%的Region数量。一般不用指定新生代的最大值和最小值,按照默认的5%~60%即可。
东阳马生架构
2025/03/26
1120
JVM实战—8.如何分析jstat统计来定位GC
如果平时要对运行中的系统,检查其JVM的整体运行情况。比较实用的工具之一就是jstat,它可以轻易让我们看到当前JVM内:Eden区、S区、老年代的内存情况,以及YGC和FGC的执行次数和耗时。
东阳马生架构
2025/03/18
1470
YGC问题排查,又让我涨姿势了!
在高并发下,Java程序的GC问题属于很典型的一类问题,带来的影响往往会被进一步放大。不管是「GC频率过快」还是「GC耗时太长」,由于GC期间都存在Stop The World问题,因此很容易导致服务超时,引发性能问题。
Java识堂
2020/07/15
2.1K0
YGC问题排查,又让我涨姿势了!
G1垃圾回收器在并发场景调优
目前企业级主流使用的Java版本是8,垃圾回收器支持手动修改为G1,G1垃圾回收器是Java 11的默认设置,因此G1垃圾回收器可以用很长时间,现阶段垃圾回收器优化意味着针对G1垃圾回收器优化。
赛先生和泰先生
2022/03/18
3.9K1
G1垃圾回收器在并发场景调优
GC垃圾回收——总结
JVM的垃圾回收机制,在内存充足的情况下,除非你显式调用System.gc(),否则它不会进行垃圾回收;在内存不足的情况下,垃圾回收将自动运行
用户5325874
2020/01/16
5800
GC垃圾回收——总结
Java垃圾回收(一)
自从有自动内存管理出现之时就有的一些收集算法,不同的收集器也是在不同场景下进行组合。
Yuyy
2022/09/21
3750
Java垃圾回收(一)
五位卷王 | 总结的十道 JVM 面试真题!(建议收藏)
先说下哈,这次给大家带来的大厂面试题,都来源于我的知识星球,由星球的几位嘉宾 + 我 一起完成。
悟空聊架构
2022/05/13
4980
五位卷王 | 总结的十道 JVM 面试真题!(建议收藏)
深入浅出JVM调优,看完你就懂
JVM把内存区分为堆区(heap)、栈区(stack)和方法区(method)。由于本文主要讲解JVM调优,因此我们可以简单的理解为,JVM中的堆区中存放的是实际的对象,是需要被GC的。其他的都无需GC。
全栈程序员站长
2022/09/05
5780
深入浅出JVM调优,看完你就懂
看完这篇垃圾回收,和面试官扯皮没问题了
Java 相比 C/C++ 最显著的特点便是引入了自动垃圾回收 (下文统一用 GC 指代自动垃圾回收),它解决了 C/C++ 最令人头疼的内存管理问题,让程序员专注于程序本身,不用关心内存回收这些恼人的问题,这也是 Java 能大行其道的重要原因之一,GC 真正让程序员的生产力得到了释放,但是程序员很难感知到它的存在,这就好比,我们吃完饭后在桌上放下餐盘即走,服务员会替你收拾好这些餐盘,你不会关心服务员什么时候来收,怎么收。
Java技术江湖
2020/02/17
7410
看完这篇垃圾回收,和面试官扯皮没问题了
GC算法和垃圾收集器
垃圾收集器在对堆回收之前,第一件事情就是要确定这些对象哪些还“存活”着,哪些对象已经“死去”(即不可能再被任何途径使用的对象)、
leobhao
2022/06/28
4150
GC算法和垃圾收集器
JVM实战—10.MAT的使用和JVM优化总结
2.百万级数据误处理导致频繁FGC(大数据量加载到内存处理 + String.split())
东阳马生架构
2025/03/19
1530
推荐阅读
相关推荐
jvm最全详解-05-JVM调优工具详解及调优实战[通俗易懂]
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验