性能测试连载 (16)-jvm 内存空间与 gc 机制

jvm内存空间分析

在这里插入图片描述
Heap(堆区)
  New Generation(年轻代)
  Eden 伊甸园
  Survivor From 存活区1
  Survivor To 存活区2
  Old Generation(老年代)
方法区
  Permanent Generation(持久代)
  Stack(栈区)
  Metaspace(元空间)
  Direct ByteBuffer(堆外内存)

GC机制

年轻代(young 区)

伊甸园
年轻代的内存区域分为:一个Eden区和两个Survivor区。对象被创建时,首先创建在年轻代eden区(如果年轻代空间不足,对象直接分配在老年代上)。
Eden区如果没有足够的空间时会引发一次young区的GC。从年轻代(包括Eden和Survivor 区域)回收内存被称为 Minor GC。
注意
年轻代gc使用“停止-复制”算法,停止指的是,发生GC的时候会暂停除了GC线程以外的所有线程的运行。所以年轻代频繁gc会极大影响系统吞吐量

Survivor
在经历一次eden的MinorGC之后,Eden中的存活对象就会被移动到第一块survivor0,此时Eden被清空;Eden区和s0满了之后,就再触发下一次Minor GC,Eden和S0中的存活对象会被复制送入survivor1;此时S0和Eden被清空,然后下一轮S0与S1交换角色(两个存活区采用轮回机制,总有一个是空的)。如果 Survivor的空间不足或者超过年轻代生存年龄(可以设置)的对象还能在新生代中存活,会通过分配担保机制将其送入老年代。Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生。

老年代(old 区)

对象在年轻代存活超过一定年龄(可以设置)没有被回收掉,就会被复制到老年代。老年代的空间比年轻代大,发生的GC次数也比年轻代少。但是gc时间大约是年轻代的10倍。
年轻代空间太小会导致对象直接进入old区 ,old区满了就会触发full gc。但是空间如果过大会引起回收耗时过长,导致应用阻塞。空间过小会产生old区小碎片,放不下大对象,引起频繁full gc。从老年代GC称为Major GC。

大对象会直接进入老年代(避免频繁复制)
年轻代对象年龄达到存活年龄阈值也会进入老年代
survivor 区太小,只能进入老年代

注意
老年代使用“标记-整理”算法,即将存活的对象向一边移动,以此来保证回收后,内存依然是连续的,不会出现内存碎片。每次年轻代的Eden发生Minor GC时,虚拟机都会检查每次晋级老年代的大小是否大于老年代的剩余大小,如果大于则会触发FULL GC

FullGC

执行 Minor GC(年轻代GC) 的时候,JVM 会检查老年代中最大连续可用空间是否大于了当前新生代所有对象的总大小,如果大于,则直接执行 Minor GC(年轻代GC),如果小于,JVM 会检查是否开启了空间分配担保机制,如果没有开启则直接改为执行Full GC。如果开启担保机制,则 JVM 会检查老年代中最大连续可用空间是否大于历次晋升到老年代中的平均大小,如果小于则执行改为执行Full GC,如果大于则会执行 Minor GC(年轻代GC),如果 Minor GC(年轻代GC) 执行失败则会执行 Full GC。频繁的fullgc会严重阻塞应用。

内存溢出类型

在年轻代gc后还存活的对象会被复制到老年代中。当老年代空间不足时,JVM会对老年代和年轻代进行完全的垃圾回收(full GC)。如果fullGC后还是无法存放从 Survivor复制过来的对象,就会出现 OOM(Out of Memory)

内存溢出也区分为好几种类型,常见的有下面几种

java.lang.OutOfMemoryError: Java heap space
原因:java堆内存不够或者程序中有死循环;
解决:扩大堆内存空间

java.lang.OutOfMemoryError: GC overhead limit exceeded
原因:内存不足,GC为了释放很小空间而占用大量时间时抛出异常
解决:
1、查看系统是否有使用大内存的代码或死循环;
2、通过添加JVM配置,来限制使用内存:
  
java.lang.OutOfMemoryError: PermGen space
原因:持久代内存不够

解决:调整JVM的配置:
< jvm-arg>-XX:MaxPermSize=128m< /jvm-arg>
< jvm-arg>-XXermSize=128m< /jvm-arg>

java.lang.OutOfMemoryError: Direct buffer memory
原因:栈溢出,方法调用层次过多或者线程栈太小。
解决:优化程序设计,减少方法调用层次;调整-Xss参数增加线程栈大小。
调整-XX:MaxDirectMemorySize= 参数
< jvm-arg>-XX:MaxDirectMemorySize=128m< /jvm-arg>

JVM常用参数

-server: tomcat以server模式运行时将拥有更大更高的并发处理能力,更快更强捷的JVM垃圾回收机制,可以获得更多的负载与吞吐量
-Xms–Xmx: 把Xms与Xmx两个值设成一样是最优的做法
-Xss: 设定线程的堆栈大小。一般不易设置超过1M,否则容易出现out ofmemory
**-XX:+AggressiveOpts:**启用这个参数,则每当JDK版本升级时,你的JVM都会使用最新加入的优化技术
**-XX:+UseBiasedLocking:**启用一个优化了的线程锁。使得appserver内对线程处理自动进行最优调配
**-XX:PermSize=128M-XX:MaxPermSize=256M:**设置非堆内存初始值
**-XX:+DisableExplicitGC:**在程序代码中不允许调用System.gc()
**-XX:+UseParNewGC:**对年轻代采用多线程并行回收
**-XX:+UseConcMarkSweepGC:**使用cms GC回收器
**-XX:MaxTenuringThreshold:**设置年轻代gc最大年龄
**-XX:+CMSParallelRemarkEnabled:**在使用UseParNewGC的情况下, 尽量减少mark的时间(暂时不理解)
**-XX:+UseCMSCompactAtFullCollection:**使用concurrent_gc的情况下,防止memoryfragmention,对live _object进行整理,减少memory碎片
**-XX:LargePageSizeInBytes:**指定Java heap的分页页面大小
-XX:+UseFastAccessorMethods: get,set方法转成本地代码
-XX:MaxGCPauseMillis=500设置年轻代回收的最长时间
-XX:ParallelGCThreads=4 设置并行回收的线程数
-XX:+UseParallelOldGC:对老年代采用多线程并行回收
-XX:+UseAdaptiveSizePolicy:并行收集器会自动选择年轻代区大小和相应的Survivor区比例
-XX:NativeMemoryTracking=detail追踪JVM的内部内存使用,开启之后会增加5%-10%的性能消耗

jvm参数设置
export JAVA_OPTS="-server -Xms1400M -Xmx1400M -Xss512k -XX:+AggressiveOpts -XX:+UseBiasedLocking -XX:PermSize=128M -XX:MaxPermSize=256M -XX:+DisableExplicitGC -XX:MaxTenuringThreshold=31 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC  -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m  -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -Djava.awt.headless=true "
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值