JAVA系列之JVM内存调优

一、前提JVM性能调优牵扯到各方面的取舍与平衡,往往是牵一发而动全身,需要全盘考虑各方面的影响 。在优化时候,切勿凭感觉或经验主义进行调整,而是需要通过系统运行的客观数据指标,不断找到最优解 。同时,在进行性能调优前 , 您需要理解并掌握以下的相关基础理论知识:

1、JVM垃圾收集器和垃圾回收算法2、JVM性能监控常用工具和命令3、JVM运行时数据区域4、能够读懂gc日志5、内存分配与回收策略
二、JVM内存结构
JAVA系列之JVM内存调优

文章插图
从上图可以看出 , 整个JVM内存是由栈内存、堆内存和永久代构成 。
年轻代(New generation) = eden + s0 + s1堆内存 = 年轻代 + 老年代(Old generation)JDK1.8以前: JVM内存 = 栈内存 + 堆内存 + 永久代JDK1.8以后: 由元空间取代了永久代,元空间并不在JVM中,而是使用本地内存 。因此JVM内存 = 栈内存 + 堆内存
1、栈内存栈内存归属于单个线程,也就是每创建一个线程都会分配一块栈内存,而栈中存储的东西只有本线程可见,属于线程私有 。栈的生命周期与线程一致,一旦线程结束,栈内存也就被回收 。栈中存放的内容主要包括:8大基本类型 + 对象的引用 + 实例的方法
JAVA系列之JVM内存调优

文章插图
2、堆内存堆内存是由年轻代和老年代构成,JDK1.8以后,永久代被元空间取代,使用直接内存 , 不占用堆内存 。堆内存是Jvm中空间最大的区域,所有线程共享堆 , 所有的数组以及内存对象的实例都在此区域分配 。我们常说的垃圾回收就是作用于堆内存 。
JAVA系列之JVM内存调优

文章插图
Eden区占大容量,Survivor两个区占小容量 , 默认比例是8:1:1
3、永久代(元空间)这个区域是常驻内存的 。用来存放JDK自身携带的Class对象 。Interface元数据,存储的是Java运行时的一些环境 。这个区域不存在垃圾回收!关闭虚拟机就会释放这个区域的内存 。当发现系统中元空间占用内存比较大时,排查方向是否加载了大量的第三方jar包,Tomcat部署了太多应用,大量动态生成的反射类等 。
三、JVM常用参数首先JVM内存限制于实际的最大物理内存 , 假设物理内存无限大的话,JVM内存的最大值跟操作系统有很大的关系 。简单的说就32位处理器虽然可控内存空间有4GB,但是具体的操作系统会给一个限制,这个限制一般是2GB-3GB(一般来说Windows系统下为1.5G-2G , Linux系统下为2G-3G),而64bit以上的处理器就不会有限制 。
1、堆大小设置java -server -Xmx4g -Xms4g -Xmn2g –Xss128k
-Xmx4g:设置JVM最大可用内存为4g 。-Xms4g:设置JVM最小可用内存为4g 。一般配置为与-Xmx相同,避免每次垃圾回收完成后JVM重新分配内存 。-Xmn2g:设置年轻代大小为2G 。整个堆大小=年轻代大小 + 年老代大?。栽龃竽昵岽螅?将会减小年老代大小 。-Xss128k:设置每个线程的堆栈大小 。JDK5.0以后每个线程默认大小为1M,以前每个线程大小为256K 。根据应用的线程所需内存大小进行调整 。在相同物理内存下,减小这个值能生成更多的线程 。
java -server -Xmx4g -Xms4g -Xmn2g –Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxMetaspaceSize=16m -XX:MaxTenuringThreshold=0
-XX:NewRatio=4: 设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代) 。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5-XX:SurvivorRatio=4: 设置年轻代中Eden区与Survivor区的大小比值 。设置为4 , 则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6-XX:MaxMetaspaceSize=16m: 设置元空间最大可分配大小为16m 。-XX:MaxTenuringThreshold=0: 设置垃圾最大年龄 。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代 。对于年老代比较多的应用,可以提高效率 。如果将此值设置为一个较大值 , 则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概率 。
2、垃圾回收器选择JVM给了三种选择:串行收集器、并行收集器、并发收集器,但是串行收集器只适用于小数据量的情况,所以这里的选择主要针对并行收集器和并发收集器 。默认情况下,JDK5.0以前都是使用串行收集器,如果想使用其他收集器需要在启动时加入相应参数 。JDK5.0以后 , JVM会根据当前系统配置进行判断 。

推荐阅读