设置堆内存 x m x 应该考虑哪些因素?设置堆内存应该考虑哪些因素呢?在 g 二中,我们通常设置堆内存大小,我们通常是使用这个 x max 参数, 那设置堆的内存大小他是一个重要的性能调优决策,需要考虑多个因素,以确保应用程序在合适的内存限制下能够运行顺畅,避免内存不足或内存浪费的问题。 以下呢,是考虑设置堆内存大小参数时需要考虑的一些因素。第一个是应用程序的内存需求。 首先呢,我们要了解应用程序的内存需求,这包括了应用程序的数据量啊,并发用户数呀,以及对向创建频率等。不同的应用程序呢, 可能需要不同大小的堆内存。第二个呢是应用程序的性能需求,性能目标对内存大小有很大的影响,如果需要更高的吞吐量和更低的延迟,可能需要分配更多的内存, 但是要小心不要分配过多,以避免内存的一个浪费。第三个呢是可用物理内存,要考虑服务器或计算机上面的可用物理内存量。将 x max 参数设置为超过物理内存容量的值, 可能会导致操作系统频繁的进行内存交换,降低性能。第四个呢是垃圾回收的开销,堆内存越大,垃圾回收的开销通常也会同步的增加,大内存可能需要更长的 垃圾回收暂停时间,因此呢,需要权衡内存大小和垃圾回收的一个开销。第五个呢是堆内存分代结构。 gr 堆内存通常分为年轻代、老年代和永久代, 那不同代的分配比例和大小会影响 x max 参数的一个设置,根据应用程序的特性,可以考虑调整不同代的一个大小。那第六个呢,是监控和调整, 监控应用程序的内存使用情况,使用工具,比如说 g visual vm, g ctrl 等来观察对内存的一个使用情况,根据监控数据进行动态的调整这个 x mams 参数。第七个呢是应用程序的设计,合理的应用程序设 设计也可以影响对内存的需求,避免内存线路和不必要的对象创建,可以降低内存的需求。第八个呢是并发性的需求, 多线程应用程序需要更多的堆内存,因为每个线程都需要一定的内存空间来存储战争和局部面料。 第九个呢是 gbm 版本和垃圾回收器。不同的 gbm 版本和垃圾回收器可能对内存的需求有不同的影响。 那某些垃圾回收器呢,可能更适合大堆内存,而某些更适合小堆内存。那通过这些因素呢,需要进行实际的性能测试和监控来确定合适的 x max 参数 值。通常呢,建议开始设置一个合理的初始值。然后呢,通过性能测试和监控来逐渐的调整,以满足应用程序的需求,避免不必要的内存浪费。 不同的应用程序可能需要不同的内存配置,因此没有一种大小适合所有情况下的通用规则。
粉丝3996获赞2.3万

一招轻松定位线上 jvm 内存易出问题。那首先看到这个界面,这是我们 grandfa 的一个内存监控界面,我们可以看到这是我们的一个内存使用情况,以及我们的 cpu 的一个使用情况。 ok, 那么接下来我们模拟一下我们的内存飙升。好,关于这个问题的详细文字版,我已经整理了一份八十万字的专网秘书大全笔记,放在视频的最后面,坚持看完一定对你有帮助。那首先设置我们一个启动参数,将我们的堆的最小值和堆的最大值设成一百兆,还去设置这个参数,那这个参数他当我们 om 发生时,他会自动当谱一个堆内存信息,那下面他可以改变我们一个 地址。 ok, 好,那这里我们有三个 u r l。 好,我们去代码里面看一下这三个 u r l。 首先看到我们这里,我们这里有 test up 一和我们的 test up 二,以及我们的 test auto memory。 好,那我们点进去看一下,我们看到这里我们的 test up 一和我们的 test up 二,它是不是对我们的一个 catch map 做了修改。好,我们点进去 看一下它是不是循环给它铺的值,对不对?好,那再看到我们一个 test of memory。 好,这里它是不是对我们那个 memory map 是不是对它进行一个循环添加,是不是?好,那么现在我们看到我们这里是不是有 两个 map, 对不对? ok, 那这时候我们模拟一下我们的内存飙升。好,那首先是我们的一个第一个 ul, 好,我们访问一下我们的第一个 url。 好,请求成功,我们看一下他现在是不是已经执行完了,对不对?好,那这个时候看下我们内存的一个使用情况,我们先是百分之二十九了,是不是?是不是在升高,对不对?好,我们再刷新一下,我们稍微等待一下。 好,我们可以看到我们这个 dna 层是不是已经使用了百分之五十几了,是不是? ok, 那现在我们继续让他再继续飙升。好,访问我们的第二个 url。 好,我们看一下我们后台, 我们这里是不是也值钱包呢?是不是我们那个 test r 牌啊?是不是?好,那现在我们再去看它内存的一个使用情况。 好,我们现在可以看到它现在是百分之七十八点几,并且它颜色都变了,对不对?它现在是不是已经在告警了?好,那这个时候我们去分析一下我们一个内存的一个使用情况。好,那么下面看,那么我们可以通过我们那个 gps 查看一个进程,通过我们的降谱,我们去 下载他的一个对应内存信息,那么我们因为我们这边已经安装好我们的一个 xweter, 所以说我们可以通过他来下载我们的帐篷文件。好,那么现在我们用这个来去下载我们的帐篷文件。好,这是我们的 ipip 改成幺二七点零点零点幺,那么是我们的 pod, 是我们的一个八零八零端口,是不是? 好,那么现在我去下载这个 dump 文件。好,他现在是不是已经下载完了?那么现在这个时候我们用我们的一个工具去分析下他这个 内存一个使用情况。好,那这里有我们的一个 m a t 以及我们还可以使用的一个 j profer, 还有我们的一个微锁 v m。 ok, 那么现在我们去打开我们的刚刚下载的这个档谱文件,那么这里选择我们的一个 warfare。 ok, 我们可以看到我们一个 type 档谱,是不是?好,那么等它加载完成。 ok, 好,那么现在我们看到这里,我们这里是不是有很多的选项?好,那首先点击我们这个地方,我们可以看一下 他这里是不是将我们那个内存的一个使用情况和一个占比是不是都显示出来了?并且他是按照从大到小的一个顺序排列的,是不是?好,那么现在我们去看一下他这里面 是什么样子的? ok, 这里是不是我们看占比最高的,是不是?好,那我们再看这个好像都看不出什么信息出来,那么接着我们往下面看。 ok, 好像也看不出什么信息出来,那我们再往下面看。好,我们看到这里,他这里是不是说,哎,我们这是不是有三个属性?我们有一个, 还有两个哈星 map, 是不是?好,再往里看,我们可以看到这里是不是有两个哈星 map, 这两个哈星 map, 我们有一个哈星 map, 他的一个占比的内存是不是比较高?哎,那这个时候我现在到底是哪个哈星 map? 我再点进去看一下。 好,那我们是这样看,是不是看不出太多信息?我不知道他是哪一个哈士 map, 对不对?好,那这时候怎么办? ok, 那现在在这个页面我们看一下,我们点击这个地方。好,我们点进去看一下,我们看到这里 他是不是分析出我们这里有一个问题,是不是?好,我们看一下我们在这里是不是看不出太多信息?但是我们在这里可以看到他一个内存占比是不是占了一个百分之八十六点三五,对不对?这是我们的一个内存占比,这是我们一个内存的一个这样的一个使用情况。好, 那接下来我们可以看到它是不是提示我们一个哈西 map, 是不是?那是哪个哈西 map? 我现在是不是还是没有分析出来,对不对?好,那这时候点击我们一个 detail, 我们在音乐开上,哎,在这里我们 看到这边是他一个内存的一个什么情况?是不是?好,那我们再看到这里,我们这里是不是他一个内存占用是不是也比较高?这是谁?这是我们的是不是开启 map, 对不对?好,那么我们看到我们的开启 map, 它在我们哪个类里面?它是不是在我们一个 test service 的一个实现类里面?好,那我们找到我们的 test service 实现类,我们是不是这里?那找到我们一个 cash map, 我们可以看到它现在是不是有三个地方,第一是我们一个初始化,然后是我们的 test app 一和我们的 test app 二,我们这两个地方是不是又使用到了我们的这个 cash map, 对不对?好,我们点进去看一下它这里是不是在便利 去给他铺的值。 ok, 那说明他这个时候他铺的一个占内内存比较高,说明他铺的值比较多,是不是?好? 王大萌,现在是不是找到这个内存升高的一个原因呢?是不是说明是我们的一个开启 map, 它占用内存比较多?好,那继续我们让我们的一个内存一出, 我们再看一下,让我们内存溢出, 哎,他现在是不是还在执行?我们可以看到之前我们执行我们的 test up 一和我们 test up 二,他是不是很快执行完的?那在这里为什么他执行这么慢?那是因为我们现在内存是不是占用已经很高了?所以说这时候他会影响我们一个执行速度,对不对?甚至我们内存溢出还可能导致我们一个程序崩溃。好,那么现在我们等他执行完 好,我们这里可以看到它是不是帮我们生成了一个 p i d 三二九六的,而且 profit 文件,对不对?并且这时候它是不是这里报错了?我往下面看一下它是报什么错误? 它这里是不是提示我们一个 out of memory 异常,是不是?好,那这时候我们去分析下我们这个 p i d 文件。好,那我们刷一下我们的项目。 好,我们可以看到我们这项目这里是不是也有对应的一个 p i d 文件,对不对?好,那这个时候我们去分析我们的 p i d 文件。好,找到我们这个项目所在的文件夹。 ok, 我们可以看到这是不是这个三二九六,是不是?我们打开它。好,我们等待加载完成。 ok, 那么接下来我们分析一下我们这个三二九六这个文件。好,我们点到这里,首先还是看他这个内存的一个使用情况,他的占比是什么样子的, 我们可以看到他这里有一个是不是占用了一个百分之五十九点零三,还有一个是百分之二十八点七八,是不是?那我们之前是不是在这里没分析出什么原因出来,是不是啊?这个时候,那么我们直接在这里来分析,好,我们看一下他这里是不是帮我们分析出有两个问题,对不对?那我们看第一个问题,哎,这里是不是 有我们一个 test seven 的一个实现类,对不对?他这里说,哎,我们这里是不是有个哈西 map, 他这个占比是百分之五十九点零三,是不是?好,那这里我们再往下看,往这里看,是不是也不知道是哪个哈西 map, 对不对?那我们再往里面看,好,我们看到这里,这里是不是说我们这个开心 map, 对不对? 我们开启 map, 它是占比也比较高,它告诉我们这是我们的一个 test seven 的一个实现内,我们这里是不是导致占用了我们比较多的内存?好,那再看我们第二个问题, 好,我们看第二个问题,他的说也是说一个哈士 map, 他的占比是百分之二十八点七八,是不是?那再往里面看, 它也是指向我们的 test service 实现内,好,那再往里面看,我们这里是不是也不知道是哪个哈士卖部,对不对?因为我们有几个哈西卖部?好,我们再往里面看,这里它会提示,哎,我们看到它一个内存占比,是不是?这里是不是占得比较高?它内,其实我们有个 memory map 是不是?好,它在哪里?是不是在我们的 test service 的一个实现内,对不对?好,那找到我们 test service 的一个实现内, 那么我们再去看我们那个 memory map, 好,我们找到它,哎,那在这里是不是有使用到?还有一处在哪里?这是它一个初始化,对不对?好,那我们看这里说明我们这个 memory map 它占用一个内存比较高,是不是?那,那这时候我们分析出来我们占用内存比较高的原因,那像我们这些缓存是不是? 那我们是不是应该保存到内存里面呢?那我们可不可以换另外一种方案?比如说保持到不对 reds 里面是不是好?那这样可以防止我们在档期的情况下,哎,我们的数据丢失,是不是?好?那以上就是我们线上如何排查我们这边内存易出问题。

tvm 内存过高排查步骤,如果你是加瓦成全,请一定要掌握。第一步,通过 top 查出加瓦进程 id。 第二步,通过兼 stat 杠 gcuq 加进程 id, 加刷新时间,加刷新次数,监控内存信息。重点关注下老年代的内存使用率及 fogc 次数。如果过高,我们可以通过接麦谱,但不出接 vim 内存信息, 注意此过程会触发一次放弃 c。 我们也可以通过 jvm 启动参数设置,当出现 om 时自动弹出内存文件,导出文件后 在观察下内存信息,如果老年代的内存使用率有明显下降,说明没有内存泄漏,否则我们需要下载刚导出的文件做进一步分析。第三步,使用接 profi 或者 mvt 分析内存文件,我们选择这个在线网站分析下,用法都差不多。 第一,文件已经帮我们标记出来了,也可以看到每个类型的内存占用比例,可以逐个排查。这里定位到的是 ccsonographys 的 ivo 变量,查看这里的代码,确实使用了大量内 需,要优化代码或者加大接 vm 内存需要特殊说明一下,如果是对外内存, om 需要在接 vm 启动时配置这个参数为非关闭状态,会导致百分之五到百分之十的性能损耗。如果你还没有实际操作过,记得点赞关注收藏。

大家好,我是爱讲 java 的 king, 今天我们继续解读大厂高频发系统设计 gvm 调优之如何排查内存占用过高, 这个牌差不多了,相对来说还是比较简单的啊,一般来说,如果你的程序没有发生 om, 你可以通过 g map 杠 history 这样的一个参数去进行查看,也就是说你的程序如果说他没有发生 om, 并且呢你的程序还可以正常的运行, 那么这样的话呢,我们可以直接在线上,比如说李尼克斯服务区上面,我们通过迪迈普钢黑色瑞进行对应的查看,那么现在我们呢进入李尼克斯界面, 那么我们进入这个界面之后,你会发现啊,就像我使用的一个 top 命令, top 命令的话呢,可以查看内存,查看 cpu, 你会发现这一个 pid 就是这一个进程号三四四幺,这个进程 他占用的内存比例非常高啊,甚至达到了差不多接近两个 g 的水平,所以现在的话呢,我们就需要对这一个进程进行对应的查询。 ok, 那么首先呢,我们把命令撤销一下, 我们通过一个 g map gun history, 然后这样的猜出了查三四四幺,好, 通过 g map 了,这个时候你会发现他导出很多很多的这样的一些类啊,然后还有相关的信息啊,我们查看一下啊, 其实 gmipro history 这个东西导出的是什么?导出的是你的这一个进程里面,按照这种最大的容量进行排名啊,一个岛区排名就是谁多谁占容量占的多,他的一个实力数, 然后的话一个排名,然后呢占用的字结数以及这个对象他属于哪个类?所以通过这样的一段代码,你会发现,其实占据前七名的 就是可以找到对应的类啊,比如说像我这里面呢,是一个飞车 task, 然后还有一个 big intig, 还有一个 big decame, 还有了一个英特类型的数组,然后我可以定位到具体的代码 啊,我这段具体代码呢,是一个 use them in for 一个累不累?然后呢,同时里面还有一个啊 congruent 的 execute 啊,一个类似于现成池的,以及呢,里面还有很多的拉马的表达式,他们呢占据了大小都不小,同时的话,他们的数量都有差不多是 两百万个,也就是说有两百万个这样的对象长期的占用了对空间,所以的话呢,我们就可以根据对应的这样的一些代 代码定位到具体的问题。当然啊,很多情况下面我们可能直接做这个 g map 和 history 对应的记账号是不行的,因为你的程序有可能发生了荡机,或者是有有可能发生的 om, 你的程序已经死了,那么这个时候该怎么办呢? 如果你的程序访问不了,这个时候呢,可以导出当捕日制啊,比如你像 om 啊, om, 其实机员们当中有一个参数啊,待会我给你们看一下,这个参数的话呢,你把它设置完之后,那么这样的话呢,就是当你的程序如果发生了 om, 那么他会自动导出一份当捕日制, 我可以在 linix 服务器上面输入这样的一个命令,扎瓦杠叉叉,然后冒号加 printing flag fanna govishan。 这是什么呢?这是打印所有之与人们相关的参数,那么我们可以找到对应的这个参数, 一个叫做 hip dump h, 好,这个参数就是这个参数呢,是叫做 hip dump on art of memory arrow。 一般情况下面你的程序他的这个 hip dump on art of memory arrow 这个参数,他是 first, 这个是什么意思呢?也就是说当你的程序啊,如果说发生了 out of memor arrow, 发生了 om, 这个时候呢,你的程序会自动导出一份当谱日志,但是默认情况下面 gdk 是把它关闭的,所以呢,你可以通过修改置于我们的参数,把这个东西 开启变为 two。 同时的话呢,默认情况下面这一个黑不当铺的这个 pass, 就是导出这个当不日制的 pass 啊,这个路径啊,他还是在默认的项目路径,同样你也可以进行对应的设置啊,默认情况下面不设置的话呢,就是你项目的跟路径 ok, 好,如果你导出了当铺日制的话,我们可以用对应的工具进行分析,那么工具有哪些呢?我们来看一下。第一个工具比较原始的叫做 v 撒 v m, 这个可以在你安装的 g d k 的这一个并目的下面找到啊, v 撒 v m 呢,使用的话比较简单,它也是支持界面化的, 这个时候呢,只需要把你打出的这个当不日制啊,把它打开,然后呢点到这一个类,那么他的这个显示的效果就会跟刚才 ken 老师在李尼克上面用这个命令显示的效果是一样的,他也会显示类名实力数, 然后实力的一个大小,然后占用了比例啊,跟我们刚才的这个李宁夫见面是一模一样的, ok, 除此之外的话呢,还有一个工具也比较好用, mat, mat 的这个工具的话呢,其实导打开日制啊,打开了一个当铺文件,其实也非常简单,打开当铺文件以后呢,点这个 符号啊,点这个相当于是有一个柱状的符号,点这个符号之后的话,你会发现啊,底下他也是跟命令一样的,他也会显示占用最高的内名,占用最高的这一些数据,对不对?他的 磁力术,然后呢,他的一些占用的对空间的大小,你也可以根据这个去分析问题的所在啊。不过 mit 比较好的一点,他不单纯只是一个监控,他还带了一个内存泄漏的猜想工具,所以借助这个工具呢,我们可以大概的知道为什么这些内存长期的占用我们的对空间, 我们的垃圾回收机还并没有把它回收掉, ok, 回来啊,这就是我们刚才所讲的很多情况。下面呢,我们可以通过对应的排插步骤定位到底是哪一些对象长期的占用我们的内存, 然后方便我们进行分析,这就是我们今天要讲的 g y 们调油之如何排查内存占用过高。

java 线上故障排查程序发生内存泄露如何排查? hello, 大家好,我是架构师奶爸。我们都知道 java 和 c 加加的最大区别是前者会自动收回不再使用的内存,后者需要程序员手动释放。在 c 加加中, 如果我们忘记释放内存,就会发生内存泄露。但是不要以为 jvm 帮我们回收了内存就不会出现内存泄露,程序发生内存泄露后,进程的可用内存会慢慢变少。 最后的结果就是抛出问错误。发生问错误后,可能会想到是内存不够大,于是把 xmx 参数调大,然后重启应用。这么做的结果就是过了一段时间后,问依然会出现,最后无法再调大最大堆内存了。 结果就是只能每隔一段时间重启一下应用。内存泄露的另一个可能的表现是请求的响应时间变长了,这是因为频繁发生的 gc 会暂停其他所有现成 stop the world 造成的。为了模拟这个场景, 使用了以上的程序运行参数是, x m s 二十 m x m x 二十米 x x 加 pre g c 把可用内存调小一点,并且在发生 g c 时输出信息, 运行结果如上图所示。可以看到,虽然一直在 gc, 占用的内存却越来越多,说明程序有的对象无法被回收。但是上面的程序对象都是定义在方法内的, 属于局部变量。局部变量在方法运行结果后所引用的对象在 g c 时应该被回收啊,但是 这里明显没有。为了找出到底是哪些对象没能被回收,我们加上运行参数, x x h e a p dump on out of memory error x x h e a p dump half he been 意思是发生用时把堆内存信息 dump 出来 am 运行程序直至异常, 于是得到 hip down 文件。然后我们借助 equips the mat 插件来分析,如果没有安装,需要先安装,然后 file open hip down, 然后选择刚才 dump 出来的文件,选择 lick suspects met 会列出所有可能发生内存泄露的对象。 可以看到居然有两万一千二百六十个 threat 对象,三千三百八十六个 threat poor executor 对象。如果你去看一下 java util concurrent threat poor executor 的原码,可以发现县城池为了附用 线程会不断的等待新的任务,线程也不会回收,需要调用其 shut down 方法才能让线程池执行完任务后停止。其实线程池定义成局部变量,好的做法是设置成单立,上面只是其中一种处理方法。在线上的应用, 内存往往会设置的很大,这样发声用再把内存快照当出来的文件就会很大,可能大到在本地的电脑中已经无法分析了, 因为内存不足够打开这个 down 文件。这里介绍另一种处理办法,一、用 j p s 定位到晋城号,因为已经知道了是哪个应用发生了 on, 这样可以直接用 j p s 找到进程号。二、用 dot 分析 g c 活动情况 dot 是一个统计 java 进程内存使用情况和 g c 活动的工具,参数可以有很多 多,可以通过 dot help 查看所有参数以及含义。上面的命令意思是输出 g c 的情况,输出时间,每八行输出一个行透信息,统计的进程号是二四八三六,每一千毫秒 输出一次信息。输出信息是 time, step 是距离 j v m。 启动的时间。 s 零 s e e 是新生代的两个 survivor 和 eden, o 是老年代区, m 是 mates space c c s 使用压缩比例。 y g c 和 y g c t 分别是新生态 g c 的次数和时间。 f g c 和 f g c t 分别是老年代 g c 的次数和时间。 g c t 是 g c 的总时间。虽然发生了 g c, 但是老年代内存占用率根本没下降,说明有的对象没法被回收,当 也不排除这些对象真的是有用。三、用 j m a p 工具 dump 出内存快照 j m a p 可以把指定 java 进程的内存快照 dump 出来,效果和第一种处理办法一样,不同的是他不用 dom one 就可以做到,而且 dump 出来的快照也会小很多, 这时会得到 even 的内存快照文件,然后就可以用 eclipse 来分析了。想了解更多 java 架构师岗位知识,请关注我,架构师奶爸共同筑基 java 架构师。

看下我们的方法区,我们方法区可以看这是一块独立于我们家瓦堆的内存空间,方法区还有一个别名叫飞堆,目的是要和我们的堆分开。再看下我们方法区的作用,它是用来存储我们内信息常量值,静态变量以及我们 git 变异后的一个代码等数据。 我们方法区是所有现成共享的内存,在加我八以前,我们是放在我们的 gvm 内存中,由我们永久待实现,是我们 gvm 内存大小参数的一个限制, 因为我们方法区在实现,在我们加瓦八中做了一次大革新,在加瓦八中我们移出了永久带的内容,方法区由我们圆空间实现,并且直接放到我们的本地内存中, 不是我们 gvm 参数的一个限制。当然,如果说我们物理内存也被占满了,那么方法区他也会爆我们的 om, 并且将原来方法区的支付算常量,还有我们的静态变量都转移到了我们的 java 堆中。我们方法区它在我们 g v m 启动时候被创建,那么我们关闭,我们 g v m 就会释放这个区域的内存。同样的,我们方法区也会抛出我们的内存溢出,那比方说我们加载了大量第三方的价包, 或者说我们 top cat 部署的工程过多,都可能导致我们的一个方法区溢出。好,这就是我们的一个方法区的介绍。

面试题, j v m 堆内存分配机制是什么?哈喽大家好,我是架构师奶爸在 java 中创建对象的时候,对象是在堆内存中创建的,但堆内存又分为新生代和老年代, 新生代又细分 eden 空间, from survivor 空间就 survivor 空间,我们创建的类到底在哪里?一、对象优先在 eden 区分配 堆内存分为新生代和老年代,新生代是用于存放使用后准备被回收的对象,老年代是用于存放生命周期比较长的对象。 大部分我们创建的对象都属于生命周期比较短的,所以会存放在新生代。新生代又细分一等空间 from survivor 空间 to survivor 空间我们 创建的对象,对象优先在 eden 分配。随着对象的创建, eden 剩余内存空间越来越少,就会触发 minor gc, 于是 eden 的存活对象会放入 from survivor 空间。 minor gc 后,新对象依然会往 eden 分配。 eden 剩余内存空间越来越少,又会触发 minor gc, 于是 eden 和 from survivor 的存活对象会放入 to survivor 空间。二、大对象直接进入老年代在上面的流程中,如果一个对象很大,一直在 survivor 空间复制来复制去,那很费性能, 所以这些大对象直接进入老年代。可以用 x x pregnant size, fresh old 来设置这些大对象的玉值。三、长期存活的对象将进入老年代在上 上面的流程中,如果一个对象 halloween 已经经历了十五次, minor g c 还存活在 survivor 空间中,那它即将转移到老年代。这个十五可以通过 x x maxtenuring threshold 来设置的,默认是十五。虚拟机为了给对象计算他到底经历了几次 minor g c, 会给每个对象定义了一个对象年龄计数器。如果对象在伊顿中经过第一次 minor g c 后仍然存活, 移动到 survivor 空间,年龄加一,在 survivor 区中美经历过 minor gc 后仍然存活年龄再加一, 年龄到了十五就到了老年代。四、动态年龄判断除了年龄达到 max dangering threshold 的值,还有另外一个方式进入老年代,那就是动态年龄判断。在 survivor 空间中,相同年龄所有对象大小的总和大于 survivor 空间的一半。年龄大于或等于该年龄的对象就可以直接进入老年代。比如 survivor 是一百兆, hello 一和 hello 二都是三岁,且总和超过了五十兆, hello 三十四岁。这个时候这三个对象都将到老年代。五、空间分配担保上面的流程提过,存活的对象都会放入另外一个 survivor 空间。如果这些存活的对象比 survivor 空间还大呢? 整个流程如下,一、 minor g c 之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果大于,则发起 minor g c。 二、如果小于,则看 handle promotion failure 有没有设置,如果没有设置,就发起 four g c。 三、如果设置了 handle promotion failure, 则看老年代最大可用的连续空间是否大于立次晋升到老年代对象的平均大小,如果小于就发起 four g c 四如果大于, 发起 minor g c minor g c 后看 survivor 空间是否足够存放存活对象,如果不够 就放入老年代,如果够放就直接存放 survivor 空间,如果老年代都不够放存活对象担保失败。 handle promotion failure 发起 four g c。 想了解更多 java 架构师岗位知识,请关注我,架构师奶爸共同筑基 java 架构师。

计算机专业毕业十年重学诈骗第十天,今天来简单聊聊诈骗。内存分配召开程序是点诈骗结尾的原代码文件并不能直接执行,需要编译成课外文件,再通过诈骗命令送到 gvm 虚拟机, 而虚拟机是在内存中。虚拟机为了更好的执行交班程序,又将他所占用的内存进行了划分。我们重点关注方法区、战堆这三块区域,下面分别看下每块区域的作用。 首先是方法区,他是用来存放自解码文件的,换句话说方法区就是用来存放要运行的代码。还有一个占内存,他的特点是先进后出,前面说过方法就是在占内存中运行的,其实变量也是在方法中定义的。 还有一块就是对内存,他是存放露出来的东西,会在这里开辟空间并产生地址,露出来的东西我们称为对象,对象有可能会非常的大,比如露个数字 对象,他里面有可能存一万个数据,就会需要很大的空间,对,这块的空间就是很大,专门用来存放照顾中的对象。这个就很像我们住的房子,贵妃卧室、厨房、卫生间一样, 每个区域有自己的功能,分门别类,更加方便管理。这个视频大家知道这三块区域的作用就行了,下个视频我们来段加号代码执行一下。 see you!


今天我们来聊一下 gvm 的性能调优,那 gvm 的性能调优呢?主要调什么呢?其实是对我们内存的垃圾回收进行调优,这就涉及到呢对垃圾回收器的一个选择。 gvm 的垃圾回收器呢,目前有七个,三个新生代垃圾收集器,三个老年代垃圾收集器,以及咖啡质 first 的 这样一个对新生代和老年代都支持的垃圾收集器。那并不是呢,我们随意的选择一个新生代的收集器,一个老年代的收集器啊,进行组合就可以了,而是要按照一定的搭配规则 来使用,才能够起到效果。而垃圾回收器呢,主要依靠的是垃圾回收的算法,那包括复制算法,标记清除算法和标记整理算法。那不同的垃圾收集器呢?采用的算法是不同的, 所起到的效果也是不同的,那无论采用什么样的算法呢,我们都是采用分代收集的一个策略,对我们的新生态进行 gc, 对老年代进行 foldgc。 那么我们调油的目的呢?其实最本质的是降低 stw 的时间, stw 就是指死到走 war 啊, word 就是在我们的垃圾收集器发生垃圾回收的时候,会导致我们 gbm 内的所有现成被占,那么我们的目的呢,就是让 stw 的次数越来越少,时间呢越来越短。那么所以我们总结一下,第一,我们希望降低新生代的一个回收次数, 那这样呢,我们就需要加大我们的年轻贷的一个内存空间。那第二呢,我们是希望加快我们对新生代的回收速度, 那这样呢,我们就需要减少新生代的一个内存空间,那他俩呢,又看似是矛盾的,所以我们要在两者之间选择一个平衡,去设置我们的相应的新生代的一个内存大小空间。那第三呢,我们是希望降低我们 fgc 的一个次数,那这个时候呢,我就希望增加我的老年代的内存空间,那在这里呢,除了有一些经验的参考以外呢,更多的是我们要通过发现我们 gvm 的一些 问题之后,然后再着手的通过一些压力测试和一些调油工具去监测他,去调整他。那在这里呢,依然向大家推荐由廖雪峰老师团队研发的三天 gm 性能调油课程。 这个课程呢,我从头到尾也已经试听了,对于我刚才所说的垃圾回收的算法,垃圾回收器的一个搭配组合, 以及调优的一个问题,发现啊,从压力测试再到解决问题,去调整参数,再去看效果,是一个相对完整的课程,那么依然是用一顿早餐的钱,让我们来学习一门专业的调优技术,十分值得推荐给大家。

哈喽,大家好,我是安石。那么今天我们来学习内存模型,本期视频的内容呢,将会按照下面这张脑图来进行组织,下面我们直接进入正题。在很久以前呢,当人们还在使用单处理器计算机的时候,程序员只需要编写面向单线程的程序, 如何让程序跑得更快?上层程序员几乎是无能为力的,因为他们只能依赖于更加先进的硬件与变异器,这可以理解为是一种纵向的优化。 可是呢,硬件的发展速度渐渐趋于平缓,为了让程序能够运行的更快,于是工程师们开始追求横向的拓展,就是在单台计算机中使用更多的处理器。 那么众所周知,目前 cpu 的处理速度与内存的读写速度是不在一个数量级的,所以需要在 cpu 和内存之间加上缓存来进行提速。这样呢,就呈现了一种 cpu 寄存器缓存主 的访问结构,这种结构在单 cpu 时期运行的很好,但是当一台计算机中引入了多个 cpu 时呢,出现了一个棘手的问题,假如 cpu a 将数据 d 从主存独取到独占的缓存内,通过计算之后呢,修改了数据 d 变为第一, 但是还没有刷新回主存。此时 cpub 将数据地从主存读取到独占的缓存内,也对地进行了计算,变为第二。显而易见,这时候呢,数据产生了不同步,那到底是以第一为准还是以第二为准?这就是一个问题。 对这个问题呢,科学家们设计了缓存一致性协议,主要就是为了解决多个 cpu 缓存之间的同步问题。 cpu 缓存一致性协议有很多,大致可以分为两类,窥探型和基于目录性。当 cpu 缓存想要访问主存时呢,需要经过一致性协议这种软件层面的措施来保证数据的一致性,那么协议本身的实现系 细节,本文就不再发散的展开去了。但是可以猜想的是呢,其中的内容一定是一些和数据同步相关的操作,那么既然要进行数据同步,就很可能出现等待唤醒这样的措施,这将可能导致性能问题,尤其是对于 cpu 这种运算速度极快的组件来说呢, 丝毫的等待都是极大的浪费。比如 cpu b 想要读取数据 d 的时候,还需要等待 cpu a 将数据 d 写回储存,那么这种行为是难以忍受的,因此计算机科学家们做了一些优化。 怎么优化呢?整体思想上就是将同步改成异步,比如 cpub 想要读取数据地的时候,发现地正在被其他的 cpu 修改,那么此时 cpub 可以注册一个读取地的消息,自己呢回头去做其他事情。其他 cpu 写回数据地后呢,响应了这个注册消息,此时 cpub 发现消息被响应 后呢,再去读读取的,这样呢就能够有效的提升效率。但是对于 cpub 来说,程序看上去就不是顺序执行的了,可能会出现先运行后面的指令,再返回头去运行前面的指令这种行, 这就体现出了一种指令重排序,虽然指令被重排了,但 cpu 依然需要保证程序执行结果的正确性,就是说无论他怎么指令重排, 他最后的执行结果一定要和顺序执行的结果是一样的,这具体是如何实现的不是本文的重点,有兴趣的同学呢可以自己去查阅资料。以下是几个相关的关键词,硬件内存模型的目标呢,是为了让汇编代码能够运行在一个具有一致性的内存试图上。 随着高级语言的流行呢,工程师们开始设计编程语言级别的内存模型,这是为了能够使用该语言进行编程的时候呢,也能拥有一个一致性的内存视图。于是,在硬件内存模型 之上,还存在着为编程语言设计的内存模型。比如加瓦内存模型呢,就屏蔽掉了各种硬件和操作系统的内存访问差异, 实现了让加法程序能够在各种硬件平台下都能够按照预期的方式来运行。他的抽象呢,如图所示。概括来说,每个工作县城都拥有独占的本地内存。 本地内存中呢,存储的是私有变量以及共享变量的副本,并且使用一定机制来控制本地内存和储存之间读写数据时的同步。问题更加具体一点呢,我们将工作现成和本地内存具象为 siri 的 主存,巨象为 happle。 在 strastex 中呢,有两种类型的变量,其中原始类型的变量,比如说印子券等呢,总是存储在县城站上的对象类型的变量。那么引用或者说指针本身是存储在现成站上的,而引用纸箱的对象呢,是存储在堆上的。在 happy 中呢,存储 处对象本身持有对象,引用的现成呢,就能够访问该对象。那么 hip 本身他不关心哪个现成正在访问对象。我们可以这么理解,加把现成模型中的 style、 sty 和 shift 都是对物理内存的一种抽象, 这样开发者只需要关心自己写的程序使用到了 siritty 和黑粉,而不需要关心更下层的计存器啊, cpu 缓存、储存等。可以猜测,现成在工作时呢,大部分情况下都在读写 siristet 中的本地内存, 也就是说本地内存对速度的要求更高,那么他可能大部分都是使用计算器和 cpu 缓存来实现的。而黑分子中呢,需要存储大量的对象, 需要更大的容量,那么他可能大部分都是使用储存来实现的。这么一讲呢,你大概就能够理解夹板内存模型与硬件内存模型之间的模糊的这种映射关系了。上面呢,我们提到了夹板内存模型需要设计一些机制来实现主 储存与工作内存之间的数据传输与同步,这种数据的传递呢,正是现成之间的通信方式。主层和工作内存之间通过这八个指令来实现数据的读写与同步。我按照作用呢,分别分为两类, 一类是作用于主层的,一类是作用于工作内存的。那这八个指令不需要完全记住,只需要知道有这么个概念。下面我用一张图来演示一下一个通信的例子,比如说现成 a, 现在调用 rap 指令 将 x 变量标记为独占状态。接下来他调用 x 和 stel 两个指令来对 x 变量进行复制,使他变为二,再继续现成为他调用 rat 指令将 x 这个变量写入组成。此 此时他的操作已经完成了,可以对 x 变量进行解锁。于是他调用了按 note 指令释放 x 的锁定状态,这时候呢,切成 b 需要读取变量 x, 于是他调用锐的指令来读取了 x。 这个变量 去了之后呢,要用漏的将这个变量加载到自己的本地内存中。最后呢,他再调用柚子指令来让计算资源对这个变量进行操作。这一套下来呢,就实现了现成 a 和现成 b 之间的这个通信。不过这张图上演示的是一种比较理想的状态。而在实际的现成通信中呢,还存在着一些问题需要解决。第一个问题, 假如本地内存 a 和本地内存 b 中存在 x 副本,且值都是一,当县城 a 将 x 修改为二,并且写入储存后,此时县城 b 想要读取变量 x, 默认的将会从本地内存 b 中读取,而本地内存 b 中的 x 依然是等于一的。换言之,现成 a 刷新了储存中的 x, 现成 b 如何才能读取到最新的值?那么这个问题被称为一种可见性的问题。第二个问题,假如现成 a 和 b 都从储存中读取了变量 x, 此时 x 是等于一的, 分别在各自的本地内存中自增了一, x 变为了二,然后再刷新回主存。这里就有一个问题,实际上总共自增了两次, x 应该是变为三的,但是主存中的 x 却为二。那么这种问题呢,被称为是一种原始性的问题。上面所说的两个问题,其实就是反映了县城通信之间的同步问题, 当多个县城在并发操作共享数据时呢,可能会引发各种各样的问题。这些问题呢,被总结为三个要素,第一可见性,第二原则性,第三有序性。 上面所说的两个问题呢,分别对应了可见性和原子性,这三个要素呢,事实上并不是完全割裂的,尤其是可见性和有序性,我们下面详细来讲,可见性指的是呢,当 一个县城修改共享变量的值,其他县城需要能够立刻得知这个修改。这句话其实有两层含义,不过网上的博客总结呢,往往只写到了其中的一层,可能会造 造成大家在理解上的困难。那么我们来翻译一下第一种解读,县城 a 修改了数据 d, 县城 b 需要读到修改后的最新的 d。 这个问题怎么理解呢?对应到加班内存模型中,当一个县城在自己的工作内存中修改了某个变量,应该把该变量立即刷新到主存中,并让其他县城知道。比如说我们在上网提到的案例, 在这里将它转化为代码。当我们执行慢方法时呢,首先县城一会启动,由于 a 的值为一,县城一将会执行死循环, 一秒钟后现成二,启动现成二,将 a 的值改为二。此时如果现成一能够读到 a 的值被修改为二的话呢,将会跳出死循环,但是你会发现事实上并没有跳出, 会发现死循环将一直执行下去,说明变量 a 的修改并没有被现成一堵道,那么说明他此时不满足可见性。针对这种现象如何解决呢?当某个现 程修改了变量,其他县城如何才能立刻获取到最新值?这里主要有两种解决方法。第一种,使用摩洛塔有关键字, 摩洛塔尔关键字呢?下层的实现保证了,若一个被沃勒塔修饰的变量被修改,那么总是会主动的写入内存。若要读取一个被沃勒塔修饰的变量,那么总是从储存中读取,这样的话呢,相当于操作被沃勒塔修饰的变量时呢,都是直接去读写主存,这样就能够解决上面的可见性问题。 我们不妨将上书例子中的变量 a 加上 wlta 修饰运行一下,我们可以发现,那么现成一运行了大概一秒钟之后呢,会按照我们的预期退出。第二种方法, surnerx 关键字, 金库瑞纳斯关键字呢,我们在并发专题中其实已经详细的讲过了,这里需要介绍他实现的另一个特性,在头部代码块中,在莫丽特的基础上,当读写变量时呢,会影视的 执行上文提到的内存 rap 指令,并清空工作内存中该变量的值。需要使用该变量时呢,必须从储存中获取, 同理也会隐私的执行内存,按 note 指令将修改过的变量刷新回储存,这样呢就也能够解决可见性问题。我们不妨修改一下上面的代码, 在现成一的外有块中加上对变量 a 的读取操作,可以见到现成一运行的一秒也将会如我们的预期一样退出。因此总结下来,我们可以使用波勒塔和 surnx 来解决这种可见性问题。第二种可见性问题,现成 b 需要读到被修改的变量 d, 那么现成 a 应该修改,但是因为重排序的问题,导致了现成 a 没有及时的去修改变量 d。 这句话呢,读上去可能有点抽象,有点难懂,我们来举一个例子,针对这个例子,我来提出一个问题,如果代码执行到注释四这行时,变量 i 是否 一定等于一?这时候你不妨暂停来思考一下,答案呢?是否定的。我们在最上面提到过,硬件内存模型中存在着指定重排序的机制。在硬件内存模型的上层夹板内存模型中呢,也存在着指令重排序,他们的作用和约束都是一样的。 第一呢,是为了更高的执行效率。第二个呢,在单线程中,指令重牌后呢,能够保证程序执行结果的正确性,就是说和顺序执行的结果是一样的。所以线程一中的代码一和代码二完全有可能在变异后备重排 出现了下面这样的执行顺序。在这种情况下呢,变量 i 还是等于零。但是从程序顺序执行的逻辑上看,似乎只要执行到代码四,变量 i 就一定是一。这里就出现了可见性问题,说明变量 a 此时不满足可见性。同样的,我们也可以通过 worti 和 surner 的两个关键字来解决这种 可见性问题。第一种,使用摩洛特尔关键字,摩洛特尔关键字禁止当前变量与之前的代码语句进行重排序。可以这么理解, 当程序执行到沃尔塔变量的图写时呢,之前的代码语句的执行结果是满足可见性的。当执行沃尔塔变量的图写时呢,上文讲过,变量将会与储存进行同步,所以沃尔塔变量本身也能保证可见性。在这个例子中,我们只要给 flak 变量加上沃尔塔修饰, 那么就能够禁止代码一和代码二的重排,因为代码二中的变量是被沃勒塔修饰的,根据上一段所讲,就能够保证代码一的可见性,现成二中的代码四就能够成功的读到 a 的值为一,辛苦那是关键字。我们再来想一下 上面这个例子,导致可见性问题的根源就是代码一和代码二被重排了,并且在执行期间,县城二读取到了县城一的中间状态,那么如果代码一和代码二 变成了一个不可分割的代码块,这时无论其内部如何进行重排,外部都只能读到最终结果, 所以也就避免了可见性的问题。我们可以看这张图来辅助理解,转化成代码呢,就是变成这个样子。这样呢,我们也能够解决由于指令重拍而导致的可见性问题。根据上面两个不同角度的例子呢, 可以看出,在并发编绳的时候,可见性真的是一个很麻烦的问题,好像稍一疏忽就会产生可见性问题。但是很多人可能觉得在日常开发中好像也没有怎么出现可见性问题啊, 那是因为我们站在了巨人的肩膀上射击内存模型的前辈们呢,早早的为我们解决了这件事。他们定义了一组原则,被称为 happy 或原则。 这组原则规定了对于两个操作, a 和 b 这两个操作可以在不同的县城中执行。如果 a 开贝斯比夫和 b, 那么可以保证当 a a 操作执行完后, a 操作的执行结果对 b 操作是可见的。事实上,之所以很多人在日常开发中对可见性问题没有太多的感知,那是因为在不知不觉中就已经满足了 happy store 的原则,那么 happy style 原则有下面八条,八条规则看上去有点多,也不用死记硬背, 我们就来介绍前面比较重要的三条,后面几条呢,有兴趣的同学自己可以去了解。程序顺序原则,在一个线绳的内部,按照程序代码的书写顺序书写在前面的代码操作, happy fer 与书写在后面的代码操作。 因为在单个县城中,程序员编写的代码在语意上呢,是需要串行顺序的执行的,即使在编译后的代码可能会进被进行重排,但是内存模型会保证程序执行结果的正确性。也就是说,无论他的内部怎么重排,他最终的执行结果和顺序 执行的结果是一致的。这条原则呢,其实也就是大部分程序员在执行自己所写的代码时呢,没有出现可见性问题的主要原因了。那么我们继续来看其他规则。第二条,锁定规则,对于一个锁的解锁总是 happy phone 与随后对这个锁的枷锁,那么这么听上去好像有点难理解,我们来看, 可以来看这个例子。上面这段代码呢, sety 六和 gety 六两个方法是共享同一个锁的。假设 sety 六方法在现成 a 中执行, gety 六方法在现成 b 中执行。 sity l 方法会先对 y 六变量进行复制,然后再释放所。 get y 六方法会首先获取所,然后再去读取 y 六。之 所以根据锁定原则呢,县城 a 中对 w 变量的修改可以立刻被县城 b 所感知到。如果这两个方法上没有 surndax 的声明,那么在县城 a 中执行 sety w 方法,对 w 复制之后,县城 b 中 get y 六方法反 为的 y 六值并不能保证是最新值。 siris 关键字能够保证可见性的主要原理呢,就是刷新储存和原子画多个操作。我们在上面介绍了两个可见性的案例时呢,已经详细的讲解过了。第三个规则,沃勒塔的规则,对一个沃勒塔变量的写,总是 happy biothone 与后续对这个俄罗塔变量的读, 其中的原理呢,主要就是刷新储存和禁止重拍序。我们在上面介绍可见性的两个案例的时候呢,已经详细的讲解过了。可见性呢,我们就讲到这里,我认为可见性在三要素里呢,算是最难理解的一个部分, 希望大家能够把我上面讲的两个案例以及相关的解读能够吃透,那样就能够超过很多人讲完了。可见性呢,我们再来讲原始性,原始性指的是一个操作是不可中断的,要么全部执行成功,要么全部执行失败。那么原始操作我按照自己的理解呢,分为两种,第一种是单指令,原 操作,单指令原子操作指的是呢,当你执行单个指令,要么成功,要么失败。比如我们上面讲到的工作内存和储存之间进行读写呢,有八个指令,这些指令都是不可再分的,每个指令都是原子操作。再比如我们在并发专题中讲到的 cmp 指令,他也是原子的。 举个例子,当我们在代码中写下印特 a 等于十,那么这就是一个单指令的原子操作,他对应了上述所说到的上一指令。第二种,利用锁的组合指令原子操作。有时候呢,开发者想让一组操作要么执行成功,要么执行失败,也就是说想保证一组指令的原子弹,这时候呢,就需要用到锁。 比如说我们刚才说到的八个内存指令中,就有 note 和昂 note 这两个和所相关的指令,利用他们就能够支持一组指令的原则性。反映到上层呢,就是 surpras 的关键字和 surprrax 的相关的呆萌呢,我们以 已经演示过很多个了。三要素之中第三个特性,有序性。上文呢,其实我们已经详细讲解到了,无论是从硬件内存模型还是加把内存模型来看呢,都支持指定重排这种优化操作。 不过在单线程中,虽然指令可能会被重排,但是在单线程中,内存模型能够保证执行结果的准确。 也就是说,在单线程中,无论指令如何重排,他最终的执行结果和顺序执行的结果是一样的,但是在多线程环境下,就可能因为指令重排而导致一些问题。有序性呢,其实和可见性是不能完全分开讲的, 指令重排引起的乱序,最有可能导致的就是可见性问题。我们在可见性一节中呢,讲到了加把内存模型通过 happy 或原则来解决这些由于重排而导致的可见性问题,并且针对沃勒塔原则和锁原则呢,我们讲到了他们为什么能够实 线可见性的内部原理。最后呢,我们再来总结一下。首先我们介绍了硬件的内存模型,他想要解决的问题是为了让能让汇编代码能够运行在一个具有一致性的内存试图上,那 随着高级语言的流行呢,人们开始设计编程语言级别的内存模型,他的目的呢,是为了能够在使用该语言进行编程时,开发者能够拥有一个一致性的内存视图。在介绍加班内存模型时呢,我们首先介绍了他的这种抽象方式, 接下来呢,我们介绍了两个抽象概念,一个是现成站,一个是堆。接下来就是本视频的重点,现成间之间的通信 性,诊间之间的通信需要依赖于一些同步原则,否则呢,就可能会出现一些可见性问题,或者说原则性问题,或者说有序性问题。针对并发三要素呢,我们其中要详细讲解了可见性问题,因为可见性问题是最容易被大家忽视。也是啊,大部分人都没有完全掌握的一个问题, 而解决可见性的主要就是依赖于前辈设计的 happy stop 原则, happys beat 原则共有八条,我们通过两种可见性的案例讲到了锁原则和沃勒塔原则。 这期视频呢,整体上来说还是有一定的理解难度的,如果你看一遍没有能够完全理解的话,建议你多看几遍,或者添加我的微信,我拉你进入我们的并发学习的交流群,有问题大家一起来讨论和解决。好了,本期视频就到这里,我是韩石,我们下期再见。

我们今天继续上一节的街卖铺,继续接着讲我们上一节简单聊了一下街卖铺怎么去分析当前续集类的一些原数据情况,比如说类的数量啊,对象的数量,以及哪个类他尿出来的对象多 啊。其实 gmap 更常用的一个地方是来分析堆内的内存,就是来观察内存的使用情况,还是这一个简单的程序,这个程序呢,它就是扭了相当多的速度啊,然后着重强调一下第四旧啊,这个地方, 这个地方实际上是尿了一个一百兆的速度,我们先暂时嗯把它屏蔽掉,然后嗯启动一下,看一下当前堆内的内存是怎么使用, ok, 嗯,还是老规矩,看一下当前的镜头号啊,六八九六,然后接 map 呢,还可以看赫图,然后告诉他这个 六八九六这个镜头号,他就会告诉我们当前堆的一个使用情况,比如说堆了一些配置,嗯,他的那个大小 hope size 是多少,然后以及 mate 四百四是多少。然后这里面是一个比较重要,他是一个比较重要的地方,就是一个呃堆内的一个使用情况, 因为我们用的是 g one, 然后它的分区呢,分别是分 agent, survival 和 old, 可以看到 agent 区域呢是用了百分之十,然后呢 survival 呢和 older 呢,都是用的零, 为什么是这个样子?可以从这里简单分析一下,因为我们分啊,分配的都是一些小的对象,他对象占用 没有很多的空间,所以它就优先在 agent 区域被分配了。然后如果我们在这里分配一个大对象,我们等下就可以看到它会优先把它放到那个 out 去。 ok, 这是 gmap 的一个。呃简单用法,就是看一下当前堆内的一个内存的使用情况。呃,然后我们刚才提到了 啊,如果我们分配一个大流向在这里啊,那我们再看一下 o 的区会不会被用啊? 好, 呃,可以看到 o 的区其实已经被使用了。呃用了多少呢?用了一百八十三兆,哦不,它的大小是一百八十三兆,然后用了大概一百兆,就是我们在这里分配的。就说 o 的区域呢? 呃,目前它的大小是一百八十三兆,然后用了一百兆,然后还有八十三兆的一个空余,所以它的使用率是百分之五十四。这个一百兆呢就是我们在这里分配的一个大对象,所以说就印证了我们常聊的那个大对象啊,它优先会被分配在 out 区域,就是减少 那个内存复制带来的开销,我可以自助呃接 map 的。呃分析内存,从这里我们只能简单的看到内存的一个灾,要就说他大概每个区用了多少啊,我们没有办法看到。呃,内存里面到底是什么,那我们如果想看到内存到底是什么,我们应该怎么做呢?我们用接 map 的 dump 功能,就是我们要 dump 堆了一个快照, 大家也叫做接 double, 我们可以简单看一下怎么使用,看一下技能号,然后如果你输入 double 呢,它会有一些 help 的一些命令出来,他告你接 map 的 double 是这样用的,就是可以看到这里大概这个格式,比如说我们来看一下, 然后建成号是七零五九, 可以看到它会生成一个呃 dump 文件下来,而这个文件呢,我们默认给它一个叫做呃 pro hip hip prof 的一个结尾。然后这个文件呢,我们可以采用很多工具来分析,呃给,比如说我们常用的呃那个 gippo fighter, 以及那个观察讯息堆内的情况。然后这时候呢,我可以给大家推荐一个线上的有有相关的工具,我们可以把这个堆上传到线上去啊,有一个网站叫做 hop 档 nice, 这个好像 hook 到到这里 cup dump 呢,它提供了很多工具啊,比如说有虚拟机参数现成 dump 以及内存 dump, 我们可以看一下内存 dump 啊,我之前有,我之前有一些测试啊,比如说我们采用本地上传,然后呢把呃文件上传上去, 找一下 d p r, ok, d p r 还是挺大的,有一百多兆,因为我们里面有个超级大的束缚在里。 好,现在上传成功了,可以点一点分析啊, 他这个分析其实不是很完全,嗯,就说他只分析了一些占比比较大的,比如说这里他分析到了这个玩意这个东西他有占比百分之九九。比如说我们看一下我们前面的那个, 前面的这一个,比如说第一不带那个 big object 的那个,可以看到他有呃大量的那个呃分析的一些信息在里面,比如说对象有哪些地址呀,有那个大小是多少呀?以及占比多少呀,这就可以非常清晰的看到堆里面现在呃是一个什么情况。 ok, 这就是呃我们接麦谱的剩下两种常用的用法,第一种是分析内存的一个简单的使用情况,第二种是啊,当铺数据下。

你是不是还没有完全搞懂 jbm 的内存模型?欢迎来到新一期的小欧说编程,今天来给大家分享一下 jbm 的内存模型。在日常的面试中, jbm 的内存模型是我们绕不开的一个话题。 jbm 的内存模型一共分为五大块, 分别是虚拟机上程序计数器,本地方法站、堆还有方法区。这个方法区有两种实现,在 jdk 七之前, 它的实线是叫永久带,在 jdk 一点八之后,它的实线则换成了圆空间。我们先来解释一下虚拟基站,虚拟基站存储的是方法的一些局部变量,程序计数器存储的则是我们的程序执行到了哪一步。本地方法站和虚拟基站是类似的, 只是这里面存储的是一些本地方法的信息,存储的是一些西家家编写的本地方法的一些信息,而这里的堆存储的就是我们扭出来的一些对象的一些相关信息。 方法区存储的则是一些静态变量,还有一些层量,还有一些静态方法,以及我们的一些类信息。我们的 class 文件在进行被类加载之后,会产生一些类的信息,就是存储在咱们的这个方法区里面。眼睛比较尖的同学可以发现,这个图里面把这五个区分别标了两种颜色,红色代表的是现成私有的, 每个县城都会开辟单独的一块内存,而我们蓝色是县城共享的,也就是所有的县城都能访问到这个堆里面的信息,还有这个方法区里面的一些相关信息。下面我们用一串简单的代码来详细的给大家讲一下 这些存储的过程,我们这里面可以看到这一串代码,这串代码很简单,就是这里面有两个变量,我们来我们来通过这两个变量去调用这个创建的方法来创建出一个人类的类实力,然后再将其打印,下面我们来看一下它的解析过程。 我们在执行卖方法的时候,这个 id 是这个卖方法的一个局部变量,此时代码执行到这一行的时候,我们就会将 id 加载到虚拟基站里面。在执行到第二行的时候,因为我们的实菌类型它不是一种基础类型,这里面就会进行类的创建,我们会先创建一个实菌的类,创建在咱们的堆内存里面, 然后这个是类的地址,这不算,在创建的时候,他是会存储在我们的这不算常见词里面,然后这个是他的类地址, 这个是吃不穿的信息,然后这里面我们就会加载这个内幕的变量,然后这个变量的值就是我们的类地址,下面我们就去调用了这个创建方法,我们再去调用那个创建方法的时候,这里面的 id 和我们的 mate 发里面的 id 不是同一个,所以它这里面也会 往这个站里面塞入一个 id, 再到下一步,这里面的这个内幕和之前的那个内幕也是不一样的,所以我们这里面也会在站里面 塞入一个内幕的变量,但是他们的指向的类地址都是同一个,都是咱们这个常见词里面的这个对象代码在执行到这一行的时候就会去扭对象,扭对象的时候他就会在堆里面去,他就会在堆里面去开辟一块空间,用来存储这个类的信息,此时我们的账里面也会填充这个变量的信息, 这变量的值指向的就是这个类地址,这里面他通过类地址找到了堆里面的空间,然后再进行给他复制,分别复上 id 还有内幕, 最后返回这个变量。当我们的代码在执行完这个方法的时候,属于这个方法的变量就会依次从这个站里面进行弹出, 随后代码执行到这一行,我们的内方法也获得了一个人的变量,此时这个变量也会进账。 最后我们在结束内分法的执行之后,这些属于内分法的变量也会进行出证, 这个就是代码的一些完整的过程。这里面我们可以看到我们的账里面存储的一些变量信息,在执行完方法之后就会依次出账,但我们堆里面存储的一些对象信息则不会被销毁,那他们什么时候会被回收呢? 当我们在堆里面存储的这些对象信息没有指针指向的时候,在下一次垃圾回收之后就会进行回收,就会将我们堆里面的这些对象信息进行销毁。最后再用代码来给大家看一下我们的方法区域里面存储的到底是哪些信息。我们可以进入我们的代码里面,我们可以通过反变异这个代码就可以看到 一个 class 文件在进行类的加载的时候,会产生一些类的信息,这些信息就是存储在我们的方法区里面, 现在我们执行一下反变异这里面我们通过执行这个反变异的命令,我们可以看到 一个 class 文件在被类解析之后会产生这些类的信息,我们的方法区里面除了存储一些类的信息之外,还会存储一些产量,还有一些静态方法的一些信息,这个就是本期的分享视频,我们下期再见, nice。