JVM启动命令垃圾回收器指定GC,回收算法和常见调优参数解析

回收算法

在这里插入图片描述

MarkSweep标记清除

首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象

它的主要不足有两个:

一个是效率问题,标记和清除两个过程的效率都不高;
另一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作

Copy二分拷贝

为了解决效率问题,一种称为“复制”(Copying)的收集算法出现了,它将可用内存按量划分为大小相等的两块,每次只使用其中的一块
当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这样使得每次都是对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效

MarkCompact标记整理

标记-整理(Mark- Compact)算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存

分代收集

分代模型的新生代是复制算法,下面的垃圾回收器工作在新生代的也是复制算法

一般把 Java 堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法
新生代中的对象 98%是“朝生夕死”的,所以并不需要按照 1:1 的比例来划分内存空间,
而是将内存分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 和其中一块 SurvivorSurvivor from 和Survivor to ,内存比例 811
在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。
而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或者“标记一整理”算法来进行回收

垃圾回收器种类

Serial 和 SerialOld

“Stop The World”,它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束。在用户不可见的情况下把用户正常工作的线程全部停掉

Serial:是一个单线程的垃圾收集器,copy算法,工作在新生代

SerialOld:标记整理算法,工作在老生代

在这里插入图片描述

parallelScavenge 和 parallelOld

parallelScavenge吞吐量优先的垃圾回收器
吞吐量=运行用户代码的时间/CPU总时间

parallelScavenge:多线程的Serial,工作在新生代,copy算法
虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量,这种调节方式称为 GC自适应调节策略
-XX:MaxGCPauseMillis参数GC停顿时间,500MB ——>300MB,这个参数配置太小的话会发生频繁GC
-XX:GCTimeRatio吞吐量参数,99%
parallelOld:多线程的SerialOld,标记整理算法,工作在老生代

在这里插入图片描述

ParNew 和 CMS

ParNew:工作在新生代
ParNew 收集器其实就是 Serial 收集器的多线程版本,特殊的更适合CMS的parallelScavenge,除了使用多条线程进行垃圾收集之外,
其余行为包括 Serial 收集器可用的所有控制参数(例如:-XX: SurvivorRatio-XX: PretenureSize' Threshold-XX: HandlePromotionFailure 等)、
收集算法、Stop The World、对象分配规则、回收策略等都与 Serial 收集器完全一样,在实现上,这两种收集器也共用了相当多的代码
使用-XX: ParallelGCThreads 参数来限制垃圾收集的线程数

CMS:工作在老生代,标记清除,初始标记会stop-the-world,并发标记和用户线程一起运行,并发清除也是和用户线程一起运行

CMS垃圾收集器缺点:
对CPU资源非常敏感,无法处理浮动垃圾,程序在进行并发清除阶段用户线程所产生的新垃圾
标记清除算法产生空间碎片

在这里插入图片描述

在这里插入图片描述

G1

G1 中每个 Region 都有一个与之对应的 Remembered Set,当进行内存回收时,在 GC 根节点的枚举范围中加入 Remembered Set 即可保证不对全堆扫描也不会有遗漏 检查Reference引用的对象是否处于不同的Region

G1:标记整理算法,
初始标记(Initial Marking--标记一下 GC Roots 能直接关联到的对象
并发标记(Concurrent Marking---GC Root 开始对堆中对象进行可达性分析,找出存活的对象,这阶段耗时较长,但可与用户程序并发执行
最终标记(Final Marking) ---为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录。虚拟机将这段时间对象变化记录在线程 Remembered Set Logs 里面,最终标记阶段需要把 Remembered Set Logs的数据合并到 Remembered Set 中
筛选回收(Live Data Counting and Evacuation)

在这里插入图片描述

空间整合:基于“标记一整理”算法实现为主和Region之间采用复制算法实现的垃圾收集
可预测的停顿:这是 G1 相对于 CMS 的另一大优势,降低停顿时间是 G1CMS 共同的关注点,但 G1 除了追求低停顿外,还能建立可预测的停顿时间模型
在 G1 之前的其他收集器进行收集的范围都是整个新生代或者老年代,而 G1 不再是这样。使用 G1 收集器时,Java 堆的内存布局就与其他收集器有很大差别,它将整个 Java 雄划分为多个大小相等的独立区域(Region),
虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔髙的了,它们都是一部分 Region(不需要连续)的集合。
G1 收集器之所以能建立可预测的停顿时间模型,是因为它可以有计划地避免在整个 Java 堆中进行全区域的垃圾收集。
G1 跟踪各个 Regions 里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,
优先回收价值最大的 Region(这也就是 Garbage- Firsti 名称的来由)。这种使用 Region 划分内存空间以及有优先级的区域回收方式,保证了 G1 收集器在有限的时间内可以获取尽可能高

命令和调优

指定使用垃圾回收器命令

以下无空格相邻的是常用新生代老年代组合

-XX:+UseSerialGC         指定Serial/Serial Old

-XX:+UseParallelGC       新生代指定parallelScavenge
-XX:+UseParallelOldGC    指定老年代parallelOld

-XX:+UseParNewGC         指定ParNew收集器
-XX:+UseConcMarkSweepGC  指定Concurrent Mark SweepCMS)垃圾收集器

-XX:+UseG1GC             指定G1

垃圾回收器可选组合
在这里插入图片描述

打印GC日志命令

-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps

gc日志输出文件
current=`date "+%Y-%m-%d-%H%M%S"`
-Xloggc:/xx/xx/gc-$current.log

出错dump快照命令

-XX:+HeapDumpOnOutOfMemoryErrorJVM发生OOM时,自动生成DUMP文件
-XX:HeapDumpPath=/home/xx/dump 参数表示生成DUMP文件的路径,也可以指定文件名称

内存大小命令

默认 Eden:Survivor1:Survivor2:Old = 8:1:1:20
默认 新生代:老年代 = 1:2
-Xms20M 设置JVM初始内存为20M
-Xmx20M 设置JVM最大内存为20M
-Xmn10M 设置年轻代内存大小为10M,剩余的老年代的大小就是总大小 20-10=10M,增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
-XX:SurvivorRatio=8 (对方Eden 8,我两个幸存区 2 就是2:8)决定了新生代中 Eden 区与两个 Survivor 区的空间比例是 8:1:1
-XX:NewRatio=4 (对方老年代4,1,就是1:4)设置年轻代(包括Eden和两个Survivor)与年老代的比值(除去持久代).设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
-Xss256k 设置每个线程的堆栈大小.决定函数调用的深度,JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K.更具应用的线程所需内存大小进行 调整.在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右

-XX:MaxnewSize=1G 新生代可被分配的内存的最大上限(注意:该值需要小于-Xmx的值)

-XX:MaxMetaspaceSize=512m 无论-XX:MetaspaceSize-XX:MaxMetaspaceSize两个参数如何设置,对于64JVM来讲都会从20.8M开始,随着类加载越来越多不断扩容调整,上限是-XX:MaxMetaspaceSize,默认是几乎无穷大,
这个参数替代jdk1.7及之前的-XX:PermSize,-XX:MaxPermSize,
因为调整元空间的大小须要Full GC,想要启动项目启动快速减少Full GC,通常建议在JVM参数中将MetaspaceSizeMaxMetaspaceSize设置成同样的值,并设置得比初始值要大,元空间溢出异常可以增大该最大配置,或者取消配置让其可以无限大
使用的是机器的内存不是启动设置的-Xmx,-Xms指定出来的内存,所以有时候top命令可以看到占用的内存比启动-Xmx还大
Class.forName动态动态加载类信息,实际上是在占用元数据区的空间,并发较大加载过多动态类信息可能导致元空间outOfMemoryError MetaSpace溢出

-XX:PretenureSizeThreshold=3145728   设置大对象的大小(3M) 1024*1024*3=3145728,
虚拟机提供了一个-XX: PretenureSizeThreshold 参数,令大于这个设置值的对象直接在老年代分配。这样做的目的是避免在 Eden 区及两个 Survivor 区之间发生大量的内存复制

-XX:MaxTenuringThreshold=0:设置垃圾最大年龄.如果设置为0的话,则年轻代对象不经过Survivor,直接进入年老代. 对于年老代比较多的应用,可以提高效率

吞吐量优先

吞吐量是指应⽤程序线程⽤时占程序总⽤时的⽐例。 例如,吞吐量99/100意味着100秒的程序执⾏时间应⽤程序线程运⾏了99秒, ⽽在这⼀时间段内GC线程只运⾏了1秒,吞吐量越大,程序执行所占时间越大

推荐优化命令

-XX:+UseParallelGC 年轻代并行垃圾收集器

-XX:MaxGCPauseMillis=100 毫秒为单位,设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值,
在运⾏时,吞吐量收集器计算在暂停期间观察到的统计数据(加权平均和标准偏差)。 如果统计表明正在经历的暂停其时间存在超过⽬标值的风险时,JVM会修改堆和GC设置以降低它们。 
需要注意的是,年轻代和年⽼代垃圾收集的统计数据是分开计算的,还要注意,默认情况下,最⼤暂停时间没有被设置
当设置最⼤暂停时间⽬标时,我们应注意不要选择太⼩的值。为了保持低暂停时间,JVM需要增加GC次数,那样可能会严重影响可达到的吞吐量

-XX:GCTimeRatio=99 -XX:GCTimeRatio=N指定⽬标应⽤程序线程的执⾏时间(与总的程序执⾏时间)达到N/(N+1)的⽬标⽐值。 
例如,通过-XX:GCTimeRatio=9我们要求应⽤程序线程在整个执⾏时间中⾄少9/10是活动的 (9/9+1)
XX:GCTimeRatio的默认值是99,也就是说,应⽤程序线程应该运⾏⾄少99%的总执⾏时间。(99/99+1)

-XX:ParallelGCThreads=20  配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等,
如果4个以服务器⽅式运⾏的JVM同时跑在在⼀个具有16核处理器的机器上,设置-XX:ParallelGCThreads=4是明智的,它能使不同JVM的垃圾收集器不会相互⼲扰

-XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集

-XX:+UseAdaptiveSizePolicy 默认激活,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,
垃圾收集器能将堆⼤⼩动态变动像GC设置⼀样应⽤到不同的堆区域,只要有证据表明这些变动将能提⾼GC性能

响应时间(暂停时间减少)优先

尽可能让单次stop the world时间减小
parNew+CMS

-XX:+UseParNewGC 设置年轻代为并行收集
-XX:+UseConcMarkSweepGC 设置年老代为并发收集
-XX:CMSFullGCsBeforeCompaction=5 由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理

-XX:+UseCMSCompactAtFullCollection:打开对年老代的压缩。可能会影响性能,但是可以消除碎片

逃逸分析和栈上分配命令

栈上分配效率更高

逃逸分析

逃逸分析的基本行为就是分析对象动态作用域:当一个对象在方法中被定义后,它可能被外部方法所引用,称为方法逃逸。甚至还有可能被外部线程访问到,譬如赋值给类变量或可以在其他线程中访问的实例变量,称为线程逃逸
栈上分配

栈上分配就是把方法中的变量和对象分配到栈上,方法执行完后自动销毁,而不需要垃圾回收的介入,从而提高系统性能

-XX:+DoEscapeAnalysis开启逃逸分析可以执行栈上分配(jdk1.8默认开启,其它版本未测试)
-XX:-DoEscapeAnalysis 关闭逃逸分析

启动参数调优案例

#dump位置和gc位置的目录一定要手动创建好才行
projectName=$1
DspringProfilesActive=$2
port=$3
current=`date "+%Y-%m-%d-%H%M%S"`

JAVA_OPTS="$JAVA_OPTS -Xms4096m -Xmx4096m -Xss256k -Xmn512m -XX:MaxMetaspaceSize=640m -XX:SurvivorRatio=8"
JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/home/logs/gc-$current.log"
JAVA_OPTS="$JAVA_OPTS -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/logs/dump"
JAVA_OPTS="$JAVA_OPTS -Dspring.profiles.active=$DspringProfilesActive -Dserver.port=$port"

nohup java -jar $JAVA_OPTS $projectName*.jar > /dev/null &

测试环境可加远程jvisualvm监控参数

-Djava.rmi.server.hostname=当前机器ip -Dcom.sun.management.jmxremote.port=30002 -Dcom.sun.management.jmxremote=true
-Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false

启动脚本

sh go.sh projectName DspringProfilesActive port

检查启动日志脚本

ps -ef |grep projectName
logname=`ls -lt /xx/xx*/*.log|head -n1|awk '{print $9}'`
echo $logname
tail -20f $logname

关闭脚本

projectName=projectName
port=port
#进程不存在,直接exit
pid=`ps -ef |grep 'java' |grep $projectName |grep $port |awk '{print $2}'`
if [ ! -n "$pid" ]; then
	echo "${projectName} is not running."
	exit 0
fi
echo "kill process use kill -15..."
kill -15 $pid
sleep 4
for ((i=0;i<20;i++))
do
	pid=`ps -ef |grep 'java' |grep $projectName |grep $port |awk '{print $2}'`
	if [ -n "$pid" ]; then
		sleep 1
	else
		break
	fi
done
pid=`ps -ef |grep 'java' |grep $projectName |grep $port |awk '{print $2}'`
if [ -n "$pid" ]; then
	echo "kill process use kill -9..."
	kill -9 $pid
	sleep 4
fi
#再次判断是否关闭,失败就提醒人工
pid=`ps -ef |grep 'java' |grep $projectName |grep $port |awk '{print $2}'`
if [ ! -n "$pid" ]; then
	echo "stop success."
else
	echo "stop fail !!!"
	exit 1
fi
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

可——叹——落叶飘零

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值