正文
JVM内存配置的再次思考
摘要
最近研究过不少内存分配相关的处理
今天晚上突然感觉还不是非常系统.
还是想能够细致的在学习一下.
希望能够慢慢的拾遗,提高自己
操作系统内存的使用情况
本文主要想思考linux相关的.
暂时不考虑Windows相关的机器配置.
也不考虑混用的情况 仅考虑专用的应用服务器.
Linux 尤其是不带GUI界面的纯CLI界面的服务器.
初始内存使用是挺小的.
但是如果机器上面文件比较多,目录比较复杂.
linux会缓存很多inode和dentry信息到内存中
会导致系统内存的大量使用.
除此之外. 操作系统还会缓存大量最近是用过的文件或者是其他变量
除非是内存遇到压力.一般是不会进行释放.
最简单的清理这部分文件的方法是:
echo 3 > /proc/sys/vm/drop_caches
JVM内存分配的原则
在给操作系统足够辗转腾挪的空间之后尽可能多的给予JVM使用
在给JVM的内存需要分析不同的业务场景再进行内存分配.
JVM内存的设置 主要考虑如下几个方面:
1. 能够分配多少内存给JVM使用
2. 能够分配多少内存给JVM的堆区
3. 堆区里面老年代,青年代的比率.
4. 青年代里面 From区域 to区域 以及伊甸区的比率.
5. 其他区域的内存配置, 比如方法区,元数据区,以及native memory是否需要进行限制.
不同业务需要的JVM配置是不一样的.
1. 如果是经常有大对象出现的环境. 可能要增加青年代. 避免对象过大过早升级到老年代触发GC影响性能
2. 如果对象有很多必须持久化存在的,也就是经理至少15次GC还能够存活的, 需要加大老年代. 避免空间不够,过早出现OOM.
3. 不同架构的元数据区域的类或者是方法的元数据区, 编译方法的codecache可能需要进行定制化分析.
关于堆区占比的分析
通过nmt的跟踪发现.
在默认情况下一个线程占用大于 1MB的内存, 如下面所示.
Thread (reserved=242MB, committed=242MB)
(thread #241)
(stack: reserved=241MB, committed=241MB)
(malloc=1MB #1210)
所以这时候要考虑线程数量与内存大小的情况
如果内存过小. 不建议堆区占用太多, 至少要留给操作系统1-2G的内存进行文件缓存等
留给进程数*1MB的用于堆栈区域.
codecache 一般默认值是 256m
GC进程也需要单独的内存进行存放信息. 一般也是需要跟不小的空间
类的元数据区跟程序的复杂度密切相关, 复杂的可能会有上G的空间占用.
如果是虚拟机或者是物理机, 感觉排除堆区, 至少留给3.5G左右的空间.
如果是容器化, 可以减少 1.5G 预留2G左右的空间给非堆区.
容器或者是虚拟机在限制之外的部分可以留给堆区使用.
注意这里考虑比较复杂的应用, 如果应用比较小巧,可以稍留.
如果应用非常复杂, 建议要增加预留. 避免被系统OOM.
关于MaxRamPercentage
如果容器限制内存是4G左右,系统又比较复杂, 这种情况不建议设置太高的比率
避免超过容器的限制,导致被kill
如果容器的限制内存到了16G或者是更高, 这种情况下. 可以设置为 70%左右的内存给堆区.
如果容器的限制内存超过了32G等, 可以超过 75%的比率给容器使用.
如果更高的内存限制, 可以增加更搞一点的比率. 来最大化性能使用.
关于堆区大小与性能
性能主要是考虑TPS以及RT
工作线程数/RT=TPS
所以折中情况下. RT是比较关键的.
如果GC时间较久导致RT时间变成性能衰退.
所以如果设置较大值的内存,会降低GC的次数, 但是可能会导致GC时间的增长
所以选择一个合适的设置和GC算法是很关键的.
比如G1GC 可以设置 停顿时间来保证响应.
综上 其实没有银弹可以解决所有问题 需要不断的学习与时间反馈来提高配置.