java基础---JVM---调优,垃圾收集器,内存分配策略
===曾经出现哪些虚拟机内存溢出的问题呢?如何解决的呢?
pregen space out of memory的问题,内存溢出问题。
tomcat这样设置:
TOMCAT_HOME\bin\catalina.bat 中添加这样一句话:
set JAVA_OPTS= -Xmx1024M -Xms512M -XX:MaxPermSize=256m
===?Java虚拟机使用什么算法判断对象死了呢?
两种算法
1.引用计数算法,为每个对象创建一个引用计数器,有地方引用就加1,引用失效了就减一。
引用计数法很难解决循环引用的问题:
2.根搜索算法
从根GC roots根对象开始寻找路径到目的对象,如果这个对象不可达,即使说它有其他的引用,仍然是可以回收的。
===?哪些对象可以作为GC ROOTS呢?
栈:虚拟机栈里面的引用所指向的对象
本地方法栈里面引用所指向的对象
方法区:静态变量引用的对象
常量引用的对象。
3.2对象已死吗?
===?虚拟机执行回收的具体过程是什么样的?进入销毁队列的对象还有可能存活吗?
1.先判断每个对象是否要执行finalize()方法。如果对象没有覆盖finalize()方法,因为finalize是object带的方法原先为空 ,或者虚拟机已经执行过一次finalize()方法了,那么该对象是“没必要执行finalize()方法的”。
2.否则有必要执行finalize。
1)将对象放入F-Queue队列中,排队执行finalize()方法
2)如果这个时候有引用指向这个对象,就从队列中逃脱
3)否则执行finalize()进行回收。
3.3垃圾收集算法
===?有几种垃圾收集算法呢?分别有什么优缺点?
标记清除法:统一标记要回收的对象再统一清除。缺点1,标记清除本身消耗性能 2.产生内存碎片,不利于后续申请大面积的连续的内存空间
复制法:内存分两块,用完一块就复制到另一边,原先那块就空出来了。缺点:如果对象存活的比较多,那么复制的就会比较多,消耗性能。2.内存变成一半了。商业虚拟机的新生代使用的方法。新生代存活的比较小,复制算法块。hotspot新生代使用这种方法,默认的eden区域和survivor区域比例是8:1
标记压缩法:将存活的对象压缩移动到一边,清理掉边界以外的内存。老年代使用的方法。为什么老年代使用这个方法呢,因为老年代的对象存活很久都很稳定了,那么需要进行GC处理的对象比较少了,用复制算法不划算。
分代收集算法:将堆内存分成新生代和老年代。新生代容易死,所以使用复制法,老年代存活的多,所以使用标记清除算法或者标记整理算法。
===?为什么商业虚拟机喜欢使用复制法来处理新生代呢?
因为新生代容易死亡,也就是说最后复制的对象比较少,存活下来的比较少。所以分配的内存不需要进行1:1的分配。
===?什么是Minor GC和FULL GC呢?
Minor GC:新生代的GC,java对象存活时间很短,频繁死亡,所以Minor GC很频繁,回收速度较快。
FULL GC:老年代的GC,速度慢
===?为什么需要堆内存分区,分为哪些区域呢?
因为不同的对象生命周期不同,要使用不同的内存分配策略和垃圾回收策略提高jvm的效率,所以分区。
分为新生代区,老年区,永久存储区。
===?内存分配的策略有五个?
1.新创建的对象优先进入eden区
内存分为新生代区和老年区
1)eden区是新生代的区,和survivor区比例是8比1。
2)当eden区容纳不了新的对象的时候触发minor gc新生代垃圾回收,并将eden区存活的对象放入survivor。
3)survivor比较小放不下的时候,就将对象转存到老年区去。
2.大对象直接进入老年区。什么是大对象?为什么?如何进行设置
1)大对象指的是需要大量连续内存空间的对象,比如大数组大字符串
2)因为内存空间碎片很多的时候出现大对象就会触发垃圾回收,因为没有空间给它存这么一大个连续的对象。如果一直有大对象进来就会一直垃圾回收,会很消耗性能。
3)通过设置参数-XX:PretenureSizeThreshold来控制多大的对象直接存入到老年区
3.长期存活的对象进入老年区
如何判断一个对象的年龄呢?-XX:MaxTenuringThreshold=7
1)对象躲过第一次Minor GC新生代垃圾回收之后可能会移动到Survivor区域,这个时候1岁
2)在Survivor经历多次Minor GC之后,岁数一直增大,超过某个阈值的时候就会被转存到老年区了。
4.动态对象年龄判定
survivor区的对象不一定要到一定年限才转存到老年区。
因为内存可能不够,所以当》=某个年龄段的所有对象内存超过一半就直接转存到老年区。
5.空间分配担保
1)当老年区可用内存空间大于新生代对象总和的时候能够保证这次GC(垃圾回收)是安全的
2)老年区可用区域小于新生代内存总和:会算历次转移到老年区的对象平均值,平均值小于可用内存的话会进行冒险。平均值大于可用的话就直接进行FULL GC就是老年区的垃圾回收。
目的:尽量减少老年区的内存回收
JVM有哪几种垃圾收集器呢?
-
串行垃圾回收器(Serial Garbage Collector)
-
并行垃圾回收器(Parallel Garbage Collector)
-
并发标记扫描垃圾回收器(CMS Garbage Collector)
-
G1垃圾回收器(G1 Garbage Collector)
串行垃圾回收器:一个线程进行垃圾回收,回收的时候其他线程中断运行
并行垃圾回收器:多个线程同时进行,回收的时候其他线程中断运行。
CMS并发标记扫描垃圾回收器:见专题
G1垃圾回收器:见专题
虚拟机的内存调优和垃圾回收器的选择。
===为什么要进行虚拟机的垃圾回收GC优化呢?垃圾回收面临的最大问题是什么呢?如何达到实时性呢?传统的堆空间使用方式的弊端?新的算法的优势?G1垃圾回收器的优势?
因为java虚拟机在垃圾回收的时候会停止应用程序的执行,其他线程会进行等待,等待回收线程执行完。所以GC优化的目的就是减少垃圾回收的次数。
垃圾回收的最大问题就是GC停顿问题,应用停顿,请求就会堆积起来甚至请求失败,传统的方式就是让堆变小,这样垃圾回收的时间就会很短,但是吞吐量就无法保证了。
传统的使用方式将整个堆作为一个内存块使用,等到空间不足了才做GC,会产生碎片,要压缩就会要暂停。
为了达到实时性,那么需要短的暂停时间,同时需要很大的内存空间支持。这个时候使用增量收集的方式。内存分为多块,每次使用其中一部分,垃圾收集的时候把存活的放到没用的那部分空间中。
===常见的虚拟机参数设置
java -Xmx1024m -Xms1024m -Xmn512m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=64m -XX:MaxTenuringThreshold=0
堆空间的参数设置
-Xms3550m:初始化堆空间大小
-Xmx3550m:最大堆空间大小
-Xss1m:线程栈空间大小
-Xmn2g:新生代大小
堆内空间分配
-XX:NewSize=1024m:新生代空间
-XX:MaxNewSize=1024m:最大新生代空间大小
-XX:PermSize=256m:永久代大小
-XX:MaxPermSize=256m:最大永久代大小
-XX:NewRadio=4:老年代占4份
-XX:SurvivorRadio=4:新生代占4份
-XX:MaxTenuringThreshold=7:在survivor停留几次垃圾回收会进入老年代
垃圾回收器参数设置
------串行和并行垃圾收集器(垃圾回收的时候应用会暂停)
-XX:+UseSerialGC:串行垃圾回收器
-XX:+UseParallelGC:并行垃圾回收器只对新生代有效
-XX:ParallelGCThreads=20:并行垃圾回收线程数量
-XX:+UseParallelOldGC:老年代使用并行回收
GC时间控制
-XX:GCTimeRadio=99:用于垃圾回收的时间占百分之一,保证吞吐量
-XX:MaxGCPauseMilis=100:最大新生代GC停留时间毫秒。无法满足的话就会自动调整新生代大小
-XX:+UseAdaptiveSizePolicy:会自动调整新生代中eden和survivor比例来达到最低响应时间和收集频率。
-----并发收集器(垃圾回收的时候不暂停,响应时间快,因为使用标记清除法所以存在内存碎片问题)
-XX:+UseConcMarkSweepGC:
CMS并发垃圾回收器,年老代为并发收集。可以设置几次GC后压缩,开启压缩。
适用于响应时间需求大的情况,目的是减少FULL GC发生的几率。
在老年代垃圾回收的时候,会停两次,多线程进行并行收集。因为应用不停,所以会有浮动垃圾产生,一般需要使用百分二十预留空间处理浮动垃圾,因为应用不停要保证在垃圾回收的过程中有充足的内存空间使用,否则出现异常concurrent mode failure
-XX:CMSInitiatingOccupancyFraction=256m:指定堆剩余多少的时候进行并发收集。
-XX:+UseParNewGC:和CMS配合使用,设置新生代为并行收集。与-XX:ParallelGC的区别在于后者不能够和CMS一起使用。
-XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection
打开对年老代的压缩,年老代经过5次GC的时候进行压缩整理。
关于辅助信息,打印信息供调试使用。
-XX:+PrintGC 输出gc信息
-XX:+PrintGCDetails 输出gc详细信息
-XX:+PrintGCTimeStamps -XX:+PrintGC:PrintGCTimeStamps可与
上面两个混合使用
-XX:+PrintGCApplicationConcurrentTime: 打印每次垃圾回收前,程序未中断的执行时间。可与上面混合使用
-XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间。可与上面混合使用
-XX:PrintHeapAtGC: 打印GC前后的详细堆栈信息
===如何选择垃圾收集器
吞吐量优先:有个很大的年轻代和较小的老年代,对响应时间没有要求可以使用并行。
响应时间优先:年轻代尽量大,使用CMS收集老年代
-
串行处理器:--适用情况:数据量比较小(100M左右);单处理器下并且对响应时间无要求的应用。--缺点:只能用于小型应用
-
并行处理器:--适用情况:“对吞吐量有高要求”,多CPU、对应用响应时间无要求的中、大型应用。举例:后台处理、科学计算。--缺点:应用响应时间可能较长
-
并发处理器:--适用情况:“对响应时间有高要求”,多CPU、对应用响应时间有较高要求的中、大型应用。举例:Web服务器/应用服务器、电信交换、集成开发环境。
===垃圾回收器的选择