JVM优化

JVM内存结构

JAVA堆内存是如何划分的

  1. JVM内存划分为堆内存和非堆内存,堆内存分为年轻代(Young Generation)、老年代(Old Generation),非堆内存就一个永久代(Permanent Generation)。
  2. 年轻代又分为Eden和Survivor区。Survivor区由FromSpace和ToSpace组成。Eden区占大容量,Survivor两个区占小容量,默认比例是8:1:1。
  3. 堆内存用途:存放的是对象,垃圾收集器就是收集这些对象,然后根据GC算法回收。
  4. 非堆内存用途:永久代,也称为方法区,存储程序运行时长期存活的对象,比如类的元数据、方法、常量、属性等。

在JDK1.8版本废弃了永久代,替代的是元空间(MetaSpace),元空间与永久代上类似,都是方法区的实现,他们最大区别是:元空间并不在JVM中,而是使用本地内存。
元空间有注意有两个参数:

  • MetaspaceSize :初始化元空间大小,控制发生GC阈值
  • MaxMetaspaceSize : 限制元空间大小上限,防止异常占用过多物理内存

visualvm查看内存结构

分代概念

新生成的对象首先放到年轻代Eden区,当Eden空间满了,触发Minor GC,存活下来的对象移动到Survivor0区,Survivor0区满后触发执行Minor GC,Survivor0区存活对象移动到Suvivor1区,这样保证了一段时间内总有一个survivor区为空。经过多次Minor GC仍然存活的对象移动到老年代。
老年代存储长期存活的对象,占满时会触发Major GC=Full GC,GC期间会停止所有线程等待GC完成,所以对响应要求高的应用尽量减少发生Major GC,避免响应超时。
Minor GC : 清理年轻代
Major GC : 清理老年代
Full GC : 清理整个堆空间,包括年轻代和永久代
所有GC都会停止应用所有线程。

为什么分代?

将对象根据存活概率进行分类,对存活时间长的对象,放到固定区,从而减少扫描垃圾时间及GC频率。针对分类进行不同的垃圾回收算法,对算法扬长避短。

为什么算法

  • Young区采用复制算法
  • Old区采用标记清除或者标记整理

为什么survivor分为两块相等大小的幸存空间?

主要为了解决碎片化。如果内存碎片化严重,也就是两个对象占用不连续的内存,已有的连续内存不够新对象存放,就会触发GC。

GC与内存

JVM里的GC(Garbage Collection)的算法有很多种,现在比较常用的是分代收集(generational collection,也是SUN VM使用的,J2SE1.2之后引入),即将内存分为几个区域,将不同生命周期的对象放在不同区域里:young generation,tenured generation和permanet generation。绝大部分的objec被分配在young generation(生命周期短),并且大部分的object在这里die。当young generation满了之后,将引发minor collection(YGC)。在minor collection后存活的object会被移动到tenured generation(生命周期比较长)。最后,tenured generation满之后触发major collection。major collection(Full gc)会触发整个heap的回收,包括回收young generation。permanet generation区域比较稳定,主要存放classloader信息。
young generation有eden、2个survivor 区域组成。其中一个survivor区域一直是空的,是eden区域和另一个survivor区域在下一次copy collection后活着的objecy的目的地。object在survivo区域被复制直到转移到tenured区。
我们要尽量减少 Full gc 的次数(tenured generation 一般比较大,收集的时间较长,频繁的Full gc会导致应用的性能收到严重的影响)。
堆内存GC: JVM(采用分代回收的策略),用较高的频率对年轻的对象(young generation)进行YGC,而对老对象(tenured generation)较少(tenured generation 满了后才进行)进行Full GC。这样就不需要每次GC都将内存中所有对象都检查一遍。
非堆内存不GC:GC不会在主程序运行期对PermGen Space进行清理,所以如果你的应用中有很多CLASS(特别是动态生成类,当然permgen space存放的内容不仅限于类)的话,就很可能出现PermGen Space错误。

内存申请、对象衰老过程

  • 内存申请过程

JVM会试图为相关Java对象在Eden中初始化一块内存区域;

当Eden空间足够时,内存申请结束。否则到下一步;

JVM试图释放在Eden中所有不活跃的对象(minor collection),释放后若Eden空间仍然不足以放入新对象,则试图将部分Eden中活跃对象放入Survivor区;

Survivor区被用来作为Eden及old的中间交换区域,当OLD区空间足够时,Survivor区的对象会被移到Old区,否则会被保留在Survivor区;

当old区空间不够时,JVM会在old区进行major collection;

完全垃圾收集后,若Survivor及old区仍然无法存放从Eden复制过来的部分对象,导致JVM无法在Eden区为新对象创建内存区域,则出现"Out of memory错误";

  • 对象衰老过程

新创建的对象的内存都分配自eden。Minor collection的过程就是将eden和在用survivor space中的活对象copy到空闲survivor space中。对象在young generation里经历了一定次数(可以通过参数配置)的minor collection后,就会被移到old generation中,称为tenuring。

GC触发条件

GC类型触发条件触发时发生了什么注意查看方式
YGCeden空间不足清空Eden+from survivor中所有no ref的对象占用的内存将eden+from sur中所有存活的对象copy到to sur中 一些对象将晋升到old中: to sur放不下的 存活次数超过turning threshold中的重新计算tenuring threshold(serial parallel GC会触发此项)重新调整Eden 和from的大小(parallel GC会触发此项)全过程暂停应用 是否为多线程处理由具体的 GC决定jstat –gc 进程ID 间隔时间
FGCold空间不足 perm空间不足 显示调用System.GC, RMI等的定时触发 YGC时的悲观策略 dump live的内存信息时(jmap –dump:live)清空heap中no ref的对象 permgen中已经被卸载的classloader中加载的class信息 如配置了CollectGenOFirst,则先触发YGC(针对serial GC) 如配置了ScavengeBeforeFullGC,则先触发YGC(针对serial GC)全过程暂停应用 是否为多线程处理由具体的GC决定 是否压缩需要看配置的具体GCjstat –gc 进程ID 间隔时间

permanent generation空间不足会引发Full GC,仍然不够会引发PermGen Space错误。

了解如何通过参数来控制各区域的内存大小

JVM参数设置优化

不管是YGC还是Full GC,GC过程中都会对导致程序运行中中断,正确的选择不同的GC策略,调整JVM、GC的参数,可以极大的减少由于GC工作,而导致的程序运行中断方面的问题,进而适当的提高Java程序的工作效率。

JVM参数的含义

参数名称含义默认值备注
-Xms初始堆大小物理内存的1/64(<1GB)默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40时,JVM就会增大堆直到-Xmx的最大限制
-Xmx最大堆大小物理内存的1/4(<1GB)默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制
-Xmn年轻代大小(1.4or lator)---survivor space).与jmap -heap中显示的New gen是不同的。 整个堆大小=年轻代大小 + 年老代大小 + 持久代大小. 增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8
-XX:NewSize设置年轻代大小(for 1.3/1.4)------
-XX:MaxNewSize年轻代最大值(for 1.3/1.4)------
-XX:PermSize设置持久代(perm gen)初始值物理内存的1/64---
-XX:MaxPermSize设置持久代最大值物理内存的1/4---
-Xss每个线程的堆栈大小---JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K.更具应用的线程所需内存大小进行 调整.在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右,一般小的应用,如果栈不是很深, 应该是128k够用的 大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。
    
-XX:ThreadStackSizeThread Stack Size---(0 means use default stack size) [Sparc: 512; Solaris x86: 320 (was 256 prior in 5.0 and earlier); Sparc 64 bit: 1024; Linux amd64: 1024 (was 0 in 5.0 and earlier); all others 0.]
-XX:NewRatio年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)----XX:NewRatio=4表示年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置。   
-XX:SurvivorRatioEden区与Survivor区的大小比值---设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10
-XX:LargePageSizeInBytes内存页的大小不可设置过大, 会影响Perm的大小---=128m
-XX:+UseFastAccessorMethods原始类型的快速优化------
-XX:+PrintGC------输出形式:[GC 118250K->113543K(130112K), 0.0094143 secs] [Full GC 121376K>10414K(130112K), 0.0650971 secs]
-XX:+ PrintGCDetails------输出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs] [GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs]
-XX:PrintGCTimeStamps---------
-XX:+PrintGC:PrintGCTimeStamps------可与-XX:+PrintGC -XX:+PrintGCDetails混合使用 输出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs\
-XX:+PrintGCApplicationStoppedTime打印垃圾回收期间程序暂停的时间.可与上面混合使用---输出形式:Total time for which application threads were stopped: 0.0468229 seconds
-XX:+PrintGCApplicationConcurrentTime打印每次垃圾回收前,程序未中断的执行时间.可与上面混合使用---输出形式:Application time: 0.5291524 seconds
-XX:+PrintHeapAtGC打印GC前后的详细堆栈信息------
-Xloggc:filename把相关日志信息记录到文件以便分析. 与上面几个配合使用------

垃圾收集器

  • 串行收集器(Serial)
    比较老的收集器,单线程。收集时,必须暂停应用的工作线程,直到收集结束。

  • 并行收集器(Parallel)
    多条垃圾收集线程并行工作,在多核CPU下效率更高,应用线程仍然处于等待状态。

  • CMS收集器(Concurrent Mark Sweep)
    CMS收集器是缩短暂停应用时间为目标而设计的,是基于标记-清除算法实现,整个过程分为4个步骤,包括:

    • 初始标记(Initial Mark)
    • 并发标记(Concurrent Mark)
    • 重新标记(Remark)
    • 并发清除(Concurrent Sweep)

    其中,初始标记、重新标记这两个步骤仍然需要暂停应用线程。初始标记只是标记一下GC Roots能直接关联到的对象,速度很快,并发标记阶段是标记可回收对象,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作导致标记产生变动的那一部分对象的标记记录,这个阶段暂停时间比初始标记阶段稍长一点,但远比并发标记时间段。
    由于整个过程中消耗最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,所以,CMS收集器内存回收与用户一起并发执行的,大大减少了暂停时间。

  • G1收集器(Garbage First)
    G1收集器将堆内存划分多个大小相等的独立区域(Region),并且能预测暂停时间,能预测原因它能避免对整个堆进行全区收集。G1跟踪各个Region里的垃圾堆积价值大小(所获得空间大小以及回收所需时间),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region,从而保证了再有限时间内获得更高的收集效率。
    G1收集器工作工程分为4个步骤,包括:

    • 初始标记(Initial Mark)
    • 并发标记(Concurrent Mark)
    • 最终标记(Final Mark)
    • 筛选回收(Live Data Counting and Evacuation)

    初始标记与CMS一样,标记一下GC Roots能直接关联到的对象。并发标记从GC Root开始标记存活对象,这个阶段耗时比较长,但也可以与应用线程并发执行。而最终标记也是为了修正在并发标记期间因用户程序继续运作而导致标记产生变化的那一部分标记记录。最后在筛选回收阶段对各个Region回收价值和成本进行排序,根据用户所期望的GC暂停时间来执行回收。

衡量收集器好坏因素

垃圾收集器参数

参数描述
-XX:+UseSerialGC串行收集器
-XX:+UseParallelGC并行收集器
-XX:+UseParallelGCThreads=8并行收集器线程数,同时有多少个线程进行垃圾回收,一般与CPU数量相等
-XX:+UseParallelOldGC指定老年代为并行收集
-XX:+UseConcMarkSweepGCCMS收集器(并发收集器)
-XX:+UseCMSCompactAtFullCollection开启内存空间压缩和整理,防止过多内存碎片
-XX:CMSFullGCsBeforeCompaction=0表示多少次Full GC后开始压缩和整理,0表示每次Full GC后立即执行压缩和整理
-XX:CMSInitiatingOccupancyFraction=80%表示老年代内存空间使用80%时开始执行CMS收集,防止过多的Full GC
-XX:+UseG1GCG1收集器
-XX:MaxTenuringThreshold=0在年轻代经过几次GC后还存活,就进入老年代,0表示直接进入老年代

垃圾收集器搭配

JDK1.8-JVM参数设置例子

4核8G服务器的jvm参数1配置

-Xmx5440M -Xms5440M 
-XX:MaxMetaspaceSize=512M -XX:MetaspaceSize=512M 
-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+ParallelRefProcEnabled 
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=dump.hprof 
-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:gc.log

通过gceasy分析gc日志

http://gceasy.io/

  • 吞吐量,停顿时间
  • 内存情况
  • gc情况
  • gc原因

实验1

xms=xmx 避免更多full gc

分配1M内存

-Xms20m -Xmx20m -XX:+PrintGCDetails -XX:+PrintTenuringDistribution


分配4M内存

-Xms20m -Xmx20m -XX:+PrintGCDetails -XX:+PrintTenuringDistribution


触发gc

-Xms20m -Xmx20m -XX:+PrintGCDetails -XX:+PrintTenuringDistribution


触发full gc

-Xms12m -Xmx12m -XX:+PrintGCDetails -XX:+PrintTenuringDistribution

发生full gc时,系统卡顿,所以需要合理分配空间

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值