JVM内存模型
堆内存&非堆内存
JVM内存分为堆内存(Heap Memory)和非堆内存(Non-Heap Memory)。
官方的解释为: Java虚拟机具有一个堆,堆是运行时的数据区域,所有类实例和数组的内存均从此处分配。堆是在Java虚拟机启动时创建的,JVM中堆之外的内存称为非堆内存。简单来说堆是Java代码可及的内存,是留给开发人员使用的;非堆就是JVM留给自己用的,所以方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法的代码都在非堆内存中。
年轻代&老年代&永久代&元空间
①年轻代(Young Generation): 大多数对象创建于年轻代中,其中大部分对象生命周期较短。
年轻代分三个区域,一个Eden(新生代)区,两个大小相同的Survivor区。大部分对象于Eden区中生成,当Eden区满时,仍存活的对象将被复制到两个Survivor区(中的一个)。当这个Survivor区满时,此区存活且不满足“晋升”条件的对象将被复制到另外一个Survivor区。对象每经历一次Minor GC(使用复制算法),年龄加1,达到“晋升年龄阈值”后,被放到老年代,这个过程也称为“晋升”。晋升年龄阈值”通过参数MaxTenuringThreshold设定, 不同回收器默认值不同,一般为15。
②老年代(Old Generation): 在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到老年代。老年代的垃圾回收为Major GC( 使用标记-清理算法)。
整堆包括年轻代和老年代的垃圾回收称为Full GC。
③永久代(Permanent Generation): Hotspot虚拟机特有的概念,是方法区的一种实现。用于存放Class和Method的元信息,Class在Load时被放入永久代,Class和存放Instance的Heap区不同, GC不会在主程序运行期间对永久代进行清理。
④元空间(MetaSpace): 与JVM堆不相连的本地内存。jdk1.8之后,废除了永久代,常量池从永久代迁移到了堆内存中,类的元数据如方法区从永久代迁移到了元空间中。GC时不会扫描元空间,减小了full gc的时间。
参数简介
官方文档参考
-Xms: jvm启动时分配的初始内存, 如-Xms512m, 表示初始分配512M内存。
-Xmx: jvm运行过程中分配的最大内存, 如-Xmx512m,表示jvm进程最多只能够占用512M内存。
-Xmn: 分配给年轻代的堆内存,如-Xmn192m. 表示年轻代可使用jvm的最大内存为192M。
-Xss: jvm启动的每个线程分配的内存大小, 如-Xss256k, 表示每个线程分配了256K内存。
-XX:PermSize: 永久代的初始内存,默认值依赖于平台。如-XX:PermSize=32m,表示永久代的初始内存设为32M。jdk1.8后被废除,若仍使用,不影响运行,仅启动报警。
-XX:MaxPermSize: 永久代的最大内存,若无指定,默认无上限(物理内存大小)。如-XX:MaxPermSize=68m,表示永久代最大内存为68M,jdk1.8后被废除。
-XX:MetaspaceSize: 首次触发垃圾回收的元空间大小,jdk1.8后出现。
-XX:MaxMetaspaceSize: 本地内存可分配给元空间的最大值,jdk1.8后出现,如-XX:MaxMetaspaceSize=128M。若无指定,元空间将会根据应用程序在运行时的需求动态设置大小(与可使用的本地内存相关)。
-XX:SurvivorRatio=4: 年轻代中Eden区与Survivor区的比值为4(注意Survivor区有两个)。表示Eden:Survivor=4:2,一个Survivor区占整个年轻代的1/6。
-XX:+PrintGCTimeStamps: 打印每次GC的时间戳,默认不开启。
-XX:+PrintHeapAtGC: 打印GC前后的详细堆栈信息。
-XX:+PrintGCDetails: 打印每次GC的细节信息,默认不打印。
-XX:+UseParNewGC: 支持在年轻代用多线程进行垃圾收集,默认不开启。
-XX:MaxTenuringThreshold=5: 设置垃圾回收对象的最大年龄为5。如果设置为0,则年轻代对象不经过Survivor区,直接进入老年代 。对于老年代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代的存活时间 。
-XX:ParallelGCThreads=8: 并行GC的线程数设为8,默认值是CPU数。
-XX:+UseCMSCompactAtFullCollection: 打开对年老代的压缩。可能会影响性能,但是可以消除碎片。
-XX:CMSFullGCsBeforeCompaction=3: 设置每3次GC则对内存空间进行压缩、整理。由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生碎片,使运行效率降低
-XX:+UseConcMarkSweepGC: 设置对老生代使用CMS回收。
-XX:+DisableExplicitGC: 禁止代码中显式调用GC,加上此参数,代码中调用System.gc()没有效果(附:显式调用System.gc(),只是建议JVM进行垃圾回收,但是最终会不会执行垃圾回收是不确定的)。如果应用中使用了java nio中的direct memory,那么使用-XX:+DisableExplicitGC一定要小心,存在潜在的内存泄露风险。
-XX:MaxDirectMemorySize: java nio direct buffer的最大值,若未指定MaxDirectMemorySize,此时MaxDirectMemorySize大小等同-Xmx的值, 建议显式指定大小,如-XX:MaxDirectMemorySize=128M。
-XX:NativeMemoryTracking=summary: Hotspot VM开启本地内存的统计,通过命令jcmd pid VM.native_memory summary查看。jcmd是jdk1.7新增的命令行工具, oracle官方建议使用jcmd代替jmap。
-XX:+HeapDumpOnOutOfMemoryError: 当JVM发生OOM时,自动生成DUMP文件。
-XX:HeapDumpPath=$dirName: 指定生成DUMP文件的路径(可包含文件名),如:-XX:HeapDumpPath=/root/javaheapdump.hprof ; 如果不指定文件名,默认为java
-Duser.timezone=GMT+08: 设置java运行环境时区为东8区 。
实例优化
首先确认项目的架构和代码等基本没有优化空间,然后通过GC优化,使性能提升。
方案
GC优化是系统且复杂的工作,可简单参照如下建议。
①JVM初始内存与JVM最大内存的值设置为相等,即-Xms=-Xmx
目的:减少内存自动扩容和收缩带来的性能损失
②SUN官方推荐年轻代配置为整个堆的3/8, 即Xmn为Xmx的3/8,实际根据应用情况调整。如果应用存在大量的短期对象,年轻代适量增大;如果存在相对较多的持久对象,老年代应适当增大。
③若为docker部署,Pod参数设置为:
内存Requested设置与-Xmx大小相同,内存Limited大小为Requested的4/3~3/2, 腾讯云容器(CCS)推荐为2倍。
CPU核数根据实际使用情况配置。
原因:-Xmx仅为Heap大小,除此之外,还有Non-Heap,如GC 初始及运行内存,元空间等。
实际应用
查看资源使用及堆栈信息,如通过以下命令
关于资源分配,查容器监控及Grafana_Metrics监控,资源按实际消耗的2倍配置如下