一.jvm内存结构
1.1jvm内存分配
1.栈内存分配
- 保存参数,局部变量,中间计算过程和其他数据。退出方法的时候,修改栈顶指针就可以把栈帧中的内容销毁
- 栈的优点:存取速度比堆快,仅次于寄存器,
- 栈的缺点:存在栈中的数据大小,生存期是在编译时就确定的,导致其缺乏灵活性
2.堆内存分配
- 堆的优点:动态分配内存大小,生存期不必事先告诉编译器,它是在运行期动态分配的,垃圾回收器会自动收走不再使用的空间区域,堆数据可以共享
- 堆的缺点:运行时动态分配内存,在分配和销毁时都要占用时间,因此堆的效率比较低下
堆在进程中不共享,只在线程中共享
3.jvm堆结构
老年代使用标记清除,标记整理算法
新生代使用复制算法
4.jvm堆配置参数
1.-Xms初始堆大小
默认物理内存的1/64(<1GB)
2.-Xmx最大堆大小
默认物理内存的1/4(<1GB),实际中建议不大于4GB
3.一般建议设置 -Xms=-Xmx
好处是避免每次在gc后,调整堆的大小,减小系统内存分配开销
4.整个堆大小=年轻代大小+年老代大小+持久代大小
5.jvm新生代(young generation)
1.新生代=1个eden区+2个survivor区
2.-Xmn 年轻代大小
-XX:NewSize, -XX:MaxNewSize
默认值大小为整个堆的3/8
3.-XX:NewRatio
年轻代与年老代的比值
Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置
4.-XX:SurvivorRatio
Eden区与Survivor区的大小比值,设置为8,则两个survivor区与一个eden区的比值为2:8,一个survivor区占整个年轻代的1/10
5.用来存放jvm刚分配的java对象
6.java老年代(tenured generation)
1.老年代=整个堆-年轻代大小-持久代大小
2.年轻代中经过垃圾回收没有回收掉的对象被复制到年老代
3.老年代存储对象比年轻代年龄大的多,而且不乏大对象
4.新建的对象也有可能直接进入老年代
4.1 大对象,可通过启动参数设置-XX:PretenureSizeThreshold=1024(单位为字节,默认为0)来代表超过多大时就不再新生代分配,而是直接在老年代分配
4.2大的数组对象,且数组中无引用外部对象
5.老年代大小无配置参数
1 .主内存,java栈内存,
2.栈内存分配,决定能分配多少线程,栈内存默认使用1m,总的内存如果分配了512m,最多起512个线程
栈的缺点:存在栈中的数据大小,生存期是在编译时就确定的,导致其缺乏灵活性。
3.单例 强引用,
4.调优大部分都是避免full gc
5.持久代就是方法区
二,java持久代(方法区)
1.持久代=整个堆-年轻代大小-老年代大小
没有具体的值设置,就是减出来的
2.-XX:PermSize -XX:MaxPermSize
设置持久代的大小,一般情况推荐把两者的值设置为相同的值,因为永久代大小调整也会导致堆内存需要触发fgc
3.存放class,method元信息,其大小与项目的规模,类,方法的数量有关。一般设置为128m就足够,设置原则是预留30%的空间
4.永久代的回收方式
4.1常量池中的常量,无用类信息,常量的回收很简单,没有引用就可以被回收
4.2对于无用类进行回收,必须保证3点:
类的所有实例都已经被回收
加载类的classloader已经被回收
类对象的class对象没有被引用(即没有通过反射引用该类的方法)
二.jvm内存垃圾回收
jvm垃圾收集算法
1.引用技术算法
jdk1.2之前用的,之后就没用了。
每个对象都有一个引用计数器属性,新增一个引用,计数器加一,释放一个引用,计数器减一。
无法解决循环引用的问题。
2.根搜索算法
从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,证明此对象是不可用的,不可达对象。
GC Roots包括:虚拟机中栈引用的对象
具体的垃圾回收算法
1.复制算法
一般用于新生代的内存回收,从e区回收到s0或者s1,适用于存活对象比较少的回收。
2.标记清除算法
只标记,清除,不进行对象的移动,在存活对象比较多的情况下高效,但是易造成内存碎片化。常用于老年代的回收。
3.标记整理算法
在标记清除的基础上做了对象的移动,成本更高,但解决了内存碎片化的问题。
名次解释
1.串行回收
gc单线程内存回收,会暂停所有用户线程
2.并行回收
多个gc线程并行工作,但此时用户线程是暂停的,所以,serial是穿行的,parallal收集器是并行的,而cms收集器是并发的
3.并发回收
用户线程与gc线程同时执行(不一定是并发,可能交替,但总体是同时执行的),不需要暂停用户线程(其实cms中用户线程还是需要停顿的,只是非常短,gc线程在另一个cpu上执行)
根据不通的业务场景选择不同的垃圾回收器,这个很重要。
jvm常见的垃圾回收器
上图是hotspot里的收集器,中间的横线表达分代,有连线表示可以组合使用
一.Serial回收器(串行回收器)
1.是一个单线程收集器,只能使用一个cpu或一条线程去完成垃圾收集,在进行垃圾收集时,必须暂停所有其他工作线程,直到收集完成。
2.缺点:Stop-The-World
3.优势:简单,对于单cpu,由于没有多线程交互开销,反而可以更高效,是client模式下默认的新生代收集器
新生代Serial回收器
1.-XX:+UseSerialGC来开启
Serial New + Serial Old的收集器组合进行内存回收
2.使用复制算法
3.独占式的垃圾回收
一个线程进行GC,串行。其它工作线程暂停
老年代Serial回收器
1.-XX:+UseSerialGC来开启
Serial New + Serial Old的收集器组合进行内存回收
2.使用标记-压缩算法
3.串行的,独占式的垃圾回收
因为内存比较大原因,回收比新生代慢
二,ParNew回收器(并行回收器)
并行回收器也是独占式的回收器,在收集过程中,应用程序会全部暂停。但由于使用多线程进行垃圾收集,在并发能力较强的cpu上,产生的停顿时间要短于串行回收器,而在单cpu或者并发能力较弱的系统中,并行回收器的效果不会比串行回收器好,由于多线程压力,它的实际表现很可能比串行回收器差。
新生代ParNew回收器
1.-XX:+UseParNewGC开启
新生代使用并行回收收集器,老年代使用串行收集器
2.-XX:ParallelGCThreads 指定线程数
默认最好与cpu数量相当,避免过多的线程数影响垃圾收集性能
3.使用复制算法
4.并行的,独占式垃圾回收器
新生代一般都是复制算法,老年代有标记-清除,标记整理
三,新生代Parallel Scavenge回收器
1.吞吐量优先回收器
关注cpu吞吐量,即运行用户代码的时间/总时间,比如jvm运行100分钟,其中用户代码运行99分钟,垃圾收集1分钟,则吞吐量99%,这种收集器能最高效率的利用cpu,适合运行后台运算。
2.-XX:+UseParallelGC开启
使用Parallel Scavenge+SerialOld收集器组合回收垃圾,这也是server模式下的默认值
3.-XX:GCTimeRation
来设置用户执行时间占总时间的比例,默认99,即1%的时间用来进行垃圾回收
4.-XX:MaxGCPauseMillis
设置gc的最大停顿时间
5.使用复制算法
老年代Parallel Old回收器
1.-XX:+UseParallelOldGC开启
使用Parallel Scavenge+Parallel Old组合收集器进行收集
2.使用标记整理算法
3.并行的,独占式的垃圾回收器
四,CMS(并发标记清除)回收器
1.运行过程分为4个阶段:
初始标记:只标记GC Roots能直接关联到的对象
并发标记:进行GC RootsTracing的过程
重新标记:修正并发标记期间因用户程序继续运行而导致标记发生改变的那一部分对象的标记
并发清除:
其中初始标记和重新标记两个阶段仍然需要Stop-The-World,整个过程中耗时最长的并发标记和并发清除过程中,用户线程是可以工作的。
调优针对gc log来的,看达不到的到约定的标准,比如young gc在15ms内,少full gc。调优目的是达到一个指标。
停留多长时间。
两种回收失败,会引发full gc的,理解失败原因。
失败:
1.比如老年代配置80%,触发cms full gc。假设剩余20%的的内存空间是7m,从新生代丢过来的对象是8m,触发full gc
2.老年代配置的80%,已达到81%,正在cms full gc的过程中,又丢过来一个大对象,触发full gc
1.标记-清除算法
同时它又是一个使用多线程并发回收的垃圾收集器
2.-XX:ParallelCMSThreads
手工设定cms的线程数量,cms默认启动的线程数是(ParallelCMSThreads+3)/4
3.-XX:UseConcMarkSweepGC开启
使用ParNew+CMS+Serial Old的收集器组合进行内存回收,Serial Old作为cms出现"Concurrent Mode Failure" 失败后的后备收集器使用
4.-XX:CMSInitiatingOccupancyFraction
设置cms收集器在老年代空间被使用多少后触发垃圾收集,默认值68%,仅在cms收集器时有效。一般设置为70
5.-XX:+UseCMSCompactAtFullCollection
由于cms收集器会产生碎片,此参数设置在垃圾收集器后是否需要一次内存碎片整理过程,仅在cms收集器时有效
6.-XX:+CMSFullGCBeforeCompaction
设置cms收集器在进行若干次垃圾收集后再进行一次内存碎片整理过程,通常与UseCMSCompactAtFullCollection参数一起使用
7.-XX:CMSInitiatingPermOccupancyFraction
设置Perm Gen使用达到多少比率时触发,默认92%
GC性能指标
吞吐量 应用花在非gc上的时间百分比
gc负荷 与吞吐量相反,指应用花在gc上的时间百分比
暂停时间 应用花在gc stop-the-world的时间
gc频率
反应速度 从一个对象变成垃圾到这个对象被回收的时间
一个交互式应用要求暂停时间越少越好,然而,一个非交互性的应用,当然是希望gc负荷越低越好
一个实时系统对暂停时间和gc负荷的要求,都是越低越好
内存容量配置原则
1.年轻代大小选择
响应时间优先的应用:尽可能的设大,直到接近系统的最低响应时间限制。在此种情况下,年轻代收集发生的频率也是最小的,同时,减少到达年老代的对象。
吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度,因为对响应时间没有要求,垃圾收集器可以并行进行,一般适合8cpu以上的应用。
避免设置过小,当新生代设置过小时会导致:1.ygc次数更频繁。2.可能导致ygc对象直接进入老年代,如果老年代满了,会触发fgc
2.年老代大小选择
响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可能会造成内存碎片,高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间,最优化的方案,一般需要参考以下数据获得:
并发垃圾收集信息,持久代并发收集次数,传统gc信息,花在年轻代和年老代回收上的时间比例。
吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代,原因是,这样可能尽可能回收掉大部分短期对象,减少中期的对象,而老年代尽存放长期存活对象。