志在指尖
用双手敲打未来

java虚拟机(Java虚拟机的原理)

java虚拟机

JVM是Java的精华部分之一。
Java最开端是怎样来的?其实是从C++上过来的,所以Java上面许多的面向对象特性都有C++的影子。
C/C++最受诟病的是什么,便是指针,常常性的内存溢出。Java说,算了,我们做个东西没有指针吧,这样程序就不会因为内存溢出而挂起退出了。得到的是常常性的空对象异常。
C/C++不仅仅有个编译的进程,还有一个进程叫做衔接,这个也是常常出问题的当地,常常衔接不上去。
不同计算机体系结构也不一样,大学应该有一门课程叫做《计算机体系结构》,主要涉及到CPU,内存,存储,以及对字符和数字的处理上。
Java就说,那么这样的话衔接也出问题,编译好的代码也没法用,那我就用个虚拟机,然后让这个虚拟机在所有计算机体系结构上跑吧。
这个便是Java虚拟机的由来,JVM是Java中非常重要的概念,帮你屏蔽掉了Java针对硬件环境中的各种痛点和不适。

java

Java虚拟机的原理

JavaGC(GarbageCollection,废物搜集,废物收回)机制,是Java与C++/C的首要差异之一,作为Java开发者,一般不需求专门编写内存收回和废物收拾代码。在Java虚拟机中,存在主动内存办理和废物打扫机制。概括地说,该机制对JVM(JavaVirtualMachine)中的内存进行符号,并确认哪些内存需求收回,依据必定的收回战略,主动的收回内存,永不停息(NerverStop)的确保JVM中的内存空间,防止呈现内存泄露和溢出问题。
JDK运用的虚拟机是hotspot
JavaGC机制首要完结3件事:
确认哪些内存需求收回
确认什么时分需求履行GC
怎么履行GC
咱们将从4个方面学习JavaGC机制
1,内存是怎么分配的;
2,怎么确保内存不被错误收回(即:哪些内存需求收回);
3,在什么状况下履行GC以及履行GC的办法;
4,怎么监控和优化GC机制。
1,程序计数器(ProgramCounterRegister):程序计数器是一个比较小的内存区域,用于指示当时线程所履行的字节码履行到了第几行,能够了解为是当时线程的行号指示器。字节码解说器在作业时,会经过改动这个计数器的值来取下一条语句指令。
每个程序计数器只用来记载一个线程的行号,所以它是线程私有(一个线程就有一个程序计数器)的。
假如程序履行的是一个Java办法,则计数器记载的是正在履行的虚拟机字节码指令地址;假如正在履行的是一个本地(native,由C言语编写完结)办法,则计数器的值为Undefined,由于程序计数器只是记载当时指令地址,所以不存在内存溢出的状况,因而,程序计数器也是一切JVM内存区域中仅有一个没有界说OutOfMemoryError的区域。
2,虚拟机栈(JVMStack):一个线程的每个办法在履行的同时,都会创立一个栈帧(StatckFrame),栈帧中存储的有局部变量表、操作站、动态链接、办法出口等,当办法被调用时,栈帧在JVM栈中入栈,当办法履行完结时,栈帧出栈。
局部变量表中存储着办法的相关局部变量,包含各种根本数据类型,目标的引证,回来地址等。在局部变量表中,只有long和double类型会占用2个局部变量空间(Slot,关于32位机器,一个Slot便是32个bit),其它都是1个Slot。需求留意的是,局部变量表是在编译时就现已确认好的,办法运转所需求分配的空间在栈帧中是完全确认的,在办法的生命周期内都不会改动。
虚拟机栈中界说了两种反常,假如线程调用的栈深度大于虚拟机答应的最大深度,则抛出StatckOverFlowError(栈溢出);不过大都Java虚拟机都答应动态扩展虚拟机栈的巨细(有少部分是固定长度的),所以线程能够一向申请栈,直到内存缺乏,此刻,会抛出OutOfMemoryError(内存溢出)。
每个线程对应着一个虚拟机栈,因而虚拟机栈也是线程私有的。
3,本地办法栈(NativeMethodStatck):本地办法栈在效果,运转机制,反常类型等方面都与虚拟机栈相同,仅有的差异是:虚拟机栈是履行Java办法的,而本地办法栈是用来履行native办法的,在许多虚拟机中(如Sun的JDK默许的HotSpot虚拟机),会将本地办法栈与虚拟机栈放在一起运用。
本地办法栈也是线程私有的
4,堆区(Heap):堆区是了解JavaGC机制最重要的区域,没有之一。在JVM所办理的内存中,堆区是最大的一块,堆区也是JavaGC机制所办理的首要内存区域,堆区由一切线程同享,在虚拟机发动时创立。堆区的存在是为了存储目标实例,原则上讲,一切的目标都在堆区上分配内存(不过现代技能里,也不是这么绝对的,也有栈上直接分配的)。
一般的,依据Java虚拟机规范规则,堆内存需求在逻辑上是接连的(在物理上不需求),在完结时,能够是固定巨细的,也能够是可扩展的,现在主流的虚拟机都是可扩展的。假如在履行废物收回之后,仍没有满足的内存分配,也不能再扩展,将会抛出OutOfMemoryError:Javaheapspace反常。
关于堆区的内容还有许多,将在下节“Java内存分配机制”中详细介绍。
5,办法区(MethodArea):在Java虚拟机规范中,将办法区作为堆的一个逻辑部分来对待,但事实上,办法区并不是堆(Non-Heap);另外,不少人的博客中,将JavaGC的分代搜集机制分为3个代:青时代,老时代,永久代,这些作者将办法区界说为“永久代”,这是由于,关于之前的HotSpotJava虚拟机的完结办法中,将分代搜集的思维扩展到了办法区,并将办法区设计成了永久代。不过,除HotSpot之外的大都虚拟机,并不将办法区当做永久代,HotSpot本身,也计划撤销永久代。本文中,由于笔者首要运用OracleJDK6.0,因而仍将运用永久代一词。
办法区是各个线程同享的区域,用于存储现已被虚拟机加载的类信息(即加载类时需求加载的信息,包含版别、field、办法、接口等信息)、final常量、静态变量、编译器即时编译的代码等。
办法区在物理上也不需求是接连的,能够挑选固定巨细或可扩展巨细,并且办法区比堆还多了一个约束:能够挑选是否履行废物搜集。一般的,办法区上履行的废物搜集是很少的,这也是办法区被称为永久代的原因之一(HotSpot),但这也不代表着在办法区上完全没有废物搜集,其上的废物搜集首要是针对常量池的内存收回和对已加载类的卸载。
在办法区上进行废物搜集,条件苛刻并且相当困难,效果也不令人满意,所以一般不做太多考虑,能够留作今后进一步深化研究时运用。
在办法区上界说了OutOfMemoryError:PermGenspace反常,在内存缺乏时抛出。
运转时常量池(RuntimeConstantPool)是办法区的一部分,用于存储编译期就生成的字面常量、符号引证、翻译出来的直接引证(符号引证便是编码是用字符串标明某个变量、接口的位置,直接引证便是依据符号引证翻译出来的地址,将在类链接阶段完结翻译);运转时常量池除了存储编译期常量外,也能够存储在运转时刻发生的常量(比方String类的intern()办法,效果是String维护了一个常量池,假如调用的字符“abc”现已在常量池中,则回来池中的字符串地址,否则,新建一个常量参加池中,并回来地址)。
6,直接内存(DirectMemory):直接内存并不是JVM办理的内存,能够这样了解,直接内存,便是JVM以外的机器内存,比方,你有4G的内存,JVM占用了1G,则其余的3G便是直接内存,JDK中有一种基于通道(Channel)和缓冲区(Buffer)的内存分配办法,将由C言语完结的native函数库分配在直接内存中,用存储在JVM堆中的DirectByteBuffer来引证。由于直接内存收到本机器内存的约束,所以也或许呈现OutOfMemoryError的反常。
Java目标的访问办法
一般来说,一个Java的引证访问涉及到3个内存区域:JVM栈,堆,办法区。
以最简略的本地变量引证:Objectobj=newObject()为例:
Objectobj标明一个本地引证,存储在JVM栈的本地变量表中,标明一个reference类型数据;
newObject()作为实例目标数据存储在堆中;
堆中还记载了Object类的类型信息(接口、办法、field、目标类型等)的地址,这些地址所履行的数据存储在办法区中;
在Java虚拟机规范中,关于经过reference类型引证访问详细目标的办法并未做规则,现在主流的完结办法首要有两种:
1,经过句柄访问(图来自于《深化了解Java虚拟机:JVM高档特效与最佳完结》):
经过句柄访问的完结办法中,JVM堆中会专门有一块区域用来作为句柄池,存储相关句柄所履行的实例数据地址(包含在堆中地址和在办法区中的地址)。这种完结办法由于用句柄标明地址,因而非常安稳。
2,经过直接指针访问:(图来自于《深化了解Java虚拟机:JVM高档特效与最佳完结》)
经过直接指针访问的办法中,reference中存储的便是目标在堆中的实践地址,在堆中存储的目标信息中包含了在办法区中的相应类型数据。这种办法最大的优势是速度快,在HotSpot虚拟机顶用的便是这种办法。
Java内存分配机制
这儿所说的内存分配,首要指的是在堆上的分配,一般的,目标的内存分配都是在堆上进行,但现代技能也支撑将目标拆成标量类型(标量类型即原子类型,标明单个值,能够是根本类型或String等),然后在栈上分配,在栈上分配的很少见,咱们这儿不考虑。
Java内存分配和收回的机制概括的说,便是:分代分配,分代收回。目标将依据存活的时刻被分为:年青代(YoungGeneration)、年迈代(OldGeneration)、永久代(PermanentGeneration,也便是办法区)。如下图(来源于《成为JavaGC专家partI》,http://www.importnew.com/1993.html):
年青代(YoungGeneration):目标被创立时,内存的分配首要发生在年青代(大目标能够直接被创立在年迈代),大部分的目标在创立后很快就不再运用,因而很快变得不可达,所以被年青代的GC机制收拾掉(IBM的研究标明,98%的目标都是很快消亡的),这个GC机制被称为MinorGC或叫YoungGC。留意,MinorGC并不代表年青代内存缺乏,它事实上只标明在Eden区上的GC。
年青代上的内存分配是这样的,年青代能够分为3个区域:Eden区(伊甸园,亚当和夏娃偷吃禁果生娃娃的地方,用来标明内存初次分配的区域,再贴切不过)和两个存活区(Survivor0、Survivor1)。内存分配进程为(来源于《成为JavaGC专家partI》,http://www.importnew.com/1993.html):
1.绝大大都刚创立的目标会被分配在Eden区,其间的大大都目标很快就会消亡。Eden区是接连的内存空间,因而在其上分配内存极快;
2.最初一次,当Eden区满的时分,履行MinorGC,将消亡的目标收拾掉,并将剩下的目标仿制到一个存活区Survivor0(此刻,Survivor1是空白的,两个Survivor总有一个是空白的);
3.下次Eden区满了,再履行一次MinorGC,将消亡的目标收拾掉,将存活的目标仿制到Survivor1中,然后清空Eden区;
4.将Survivor0中消亡的目标收拾掉,将其间能够晋级的目标晋级到Old区,将存活的目标也仿制到Survivor1区,然后清空Survivor0区;
5.这只是个最大值,并不代表必定是这个值)之后,依然存活的目标(其实只有一小部分,比方,咱们自己界说的目标),将被仿制到老时代。
从上面的进程能够看出,Eden区是接连的空间,且Survivor总有一个为空。经过一次GC和仿制,一个Survivor中保存着当时还活着的目标,而Eden区和另一个Survivor区的内容都不再需求了,能够直接清空,到下一次GC时,两个Survivor的人物再交换。因而,这种办法分配内存和收拾内存的功率都极高,这种废物收回的办法便是闻名的“中止-仿制(Stop-and-copy)”收拾法(将Eden区和一个Survivor中依然存活的目标拷贝到另一个Survivor中),这不代表着中止仿制收拾法很高效,其实,它也只在这种状况下高效,假如在老时代选用中止仿制,则挺悲剧的。
在Eden区,HotSpot虚拟机运用了两种技能来加速内存分配。分别是bump-the-pointer和TLAB(Thread-LocalAllocationBuffers),这两种技能的做法分别是:由于Eden区是接连的,因而bump-the-pointer技能的中心便是盯梢最终创立的一个目标,在目标创立时,只需求查看最终一个目标后边是否有满足的内存即可,然后大大加速内存分配速度;而关于TLAB技能是关于多线程而言的,将Eden区分为若干段,每个线程运用独立的一段,避免相互影响。TLAB结合bump-the-pointer技能,将确保每个线程都运用Eden区的一段,并快速的分配内存。
年迈代(OldGeneration):目标假如在年青代存活了满足长的时刻而没有被收拾掉(即在几回YoungGC后存活了下来),则会被仿制到年迈代,年迈代的空间一般比年青代大,能存放更多的目标,在年迈代上发生的GC次数也比年青代少。当年迈代内存缺乏时,将履行MajorGC,也叫FullGC。
能够运用-XX:+UseAdaptiveSizePolicy开关来操控是否选用动态操控战略,假如动态操控,则动态调整Java堆中各个区域的巨细以及进入老时代的年纪。
假如目标比较大(比方长字符串或大数组),Young空间缺乏,则大目标会直接分配到老时代上(大目标或许触发提前GC,应少用,更应避免运用短寿的大目标)。用-XX:PretenureSizeThreshold来操控直接升入老时代的目标巨细,大于这个值的目标会直接分配在老时代上。
或许存在年迈代目标引证新生代目标的状况,假如需求履行YoungGC,则或许需求查询整个老时代以确认是否能够收拾收回,这显然是低效的。处理的办法是,年迈代中维护一个512byte的块——”cardtable“,一切老时代目标引证新生代目标的记载都记载在这儿。YoungGC时,只要查这儿即可,不必再去查全部老时代,因而功能大大提高。
JavaGC机制
GC机制的根本算法是:分代搜集,这个不必赘述。下面阐述每个分代的搜集办法。
年青代:
事实上,在上一节,现已介绍了新生代的首要废物收回办法,在新生代中,运用“中止-仿制”算法进行收拾,将新生代内存分为2部分,1部分Eden区较大,1部分Survivor比较小,并被划分为两个等量的部分。每次进行收拾时,将Eden区和一个Survivor中依然存活的目标拷贝到另一个Survivor中,然后收拾掉Eden和方才的Survivor。
这儿也能够发现,中止仿制算法中,用来仿制的两部分并不总是持平的(传统的中止仿制算法两部分内存持平,但新生代中运用1个大的Eden区和2个小的Survivor区来避免这个问题)
由于绝大部分的目标都是短寿的,乃至存活不到Survivor中,所以,Eden区与Survivor的份额较大,HotSpot默许是8:1,即分别占新生代的80%,10%,10%。假如一次收回中,Survivor+Eden中存活下来的内存超过了10%,则需求将一部分目标分配到老时代。用-XX:SurvivorRatio参数来配置Eden区域Survivor区的容量比值,默许是8,代表Eden:Survivor1:Survivor2=8:1:1.
老时代:
老时代存储的目标比年青代多得多,并且不乏大目标,对老时代进行内存收拾时,假如运用中止-仿制算法,则相当低效。一般,老时代用的算法是符号-收拾算法,即:符号出依然存活的目标(存在引证的),将一切存活的目标向一端移动,以确保内存的接连。
在发生MinorGC时,虚拟时机查看每次提升进入老时代的巨细是否大于老时代的剩下空间巨细,假如大于,则直接触发一次FullGC,否则,就查看是否设置了-XX:+HandlePromotionFailure(答应担保失败),假如答应,则只会进行MinorGC,此刻能够容忍内存分配失败;假如不答应,则依然进行FullGC(这代表着假如设置-XX:+HandlePromotionFailure,则触发MinorGC就会同时触发FullGC,哪怕老时代还有许多内存,所以,最好不要这样做)。
办法区(永久代):
永久代的收回有两种:常量池中的常量,无用的类信息,常量的收回很简略,没有引证了就能够被收回。关于无用的类进行收回,必须确保3点:
类的一切实例都现已被收回
加载类的ClassLoader现已被收回
类目标的Class目标没有被引证(即没有经过反射引证该类的地方)
永久代的收回并不是必须的,能够经过参数来设置是否对类进行收回。HotSpot供给-Xnoclassgc进行操控
运用-verbose,-XX:+TraceClassLoading、-XX:+TraceClassUnLoading能够查看类加载和卸载信息
-verbose、-XX:+TraceClassLoading能够在Product版HotSpot中运用;
-XX:+TraceClassUnLoading需求fastdebug版HotSpot支撑
废物搜集器
在GC机制中,起重要效果的是废物搜集器,废物搜集器是GC的详细完结,Java虚拟机规范中关于废物搜集器没有任何规则,所以不同厂商完结的废物搜集器各不相同,HotSpot1.6版运用的废物搜集器如下图(图来源于《深化了解Java虚拟机:JVM高档特效与最佳完结》,图中两个搜集器之间有连线,阐明它们能够合作运用):
在介绍废物搜集器之前,需求明确一点,便是在新生代选用的中止仿制算法中,“停止(Stop-the-world)”的含义是在收回内存时,需求暂停其他所有线程的履行。这个是很低效的,现在的各种新生代搜集器越来越优化这一点,但依然只是将中止的时刻变短,并未完全撤销中止。
Serial搜集器:新生代搜集器,运用中止仿制算法,运用一个线程进行GC,串行,其它作业线程暂停。运用-XX:+UseSerialGC能够运用Serial+SerialOld形式运转进行内存收回(这也是虚拟机在Client形式下运转的默许值)
ParNew搜集器:新生代搜集器,运用中止仿制算法,Serial搜集器的多线程版,用多个线程进行GC,并行,其它作业线程暂停,重视缩短废物搜集时刻。运用-XX:+UseParNewGC开关来操控运用ParNew+SerialOld搜集器组合搜集内存;运用-XX:ParallelGCThreads来设置履行内存收回的线程数。
ParallelScavenge搜集器:新生代搜集器,运用中止仿制算法,重视CPU吞吐量,即运转用户代码的时刻/总时刻,比方:JVM运转100分钟,其间运转用户代码99分钟,垃圾搜集1分钟,则吞吐量是99%,这种搜集器能最高功率的利用CPU,适合运转后台运算(重视缩短废物搜集时刻的搜集器,如CMS,等待时刻很少,所以适合用户交互,提高用户体会)。运用-XX:+UseParallelGC开关操控运用ParallelScavenge+SerialOld搜集器组合收回废物(这也是在Server形式下的默许值);运用-XX:GCTimeRatio来设置用户履行时刻占总时刻的份额,默许99,即1%的时刻用来进行废物收回。运用-XX:MaxGCPauseMillis设置GC的最大中止时刻(这个参数只对ParallelScavenge有用),用开关参数-XX:+UseAdaptiveSizePolicy能够进行动态操控,如主动调整Eden/Survivor份额,老时代目标年纪,新生代巨细等,这个参数在ParNew下没有。
SerialOld搜集器:老时代搜集器,单线程搜集器,串行,运用符号收拾(收拾的办法是Sweep(收拾)和Compact(紧缩),收拾是将废弃的目标干掉,只留幸存的目标,紧缩是将移动目标,将空间填满确保内存分为2块,一块全是目标,一块空闲)算法,运用单线程进行GC,其它作业线程暂停(留意,在老时代中进行符号收拾算法收拾,也需求暂停其它线程),在JDK1.5之前,SerialOld搜集器与ParallelScavenge调配运用。
ParallelOld搜集器:老时代搜集器,多线程,并行,多线程机制与ParallelScavenge差不错,运用符号收拾(与SerialOld不同,这儿的收拾是Summary(汇总)和Compact(紧缩),汇总的意思便是将幸存的目标仿制到预先准备好的区域,而不是像Sweep(收拾)那样收拾废弃的目标)算法,在ParallelOld履行时,依然需求暂停其它线程。ParallelOld在多核核算中很有用。ParallelOld呈现后(JDK1.6),与ParallelScavenge合作有很好的效果,充分体现ParallelScavenge搜集器吞吐量优先的效果。运用-XX:+UseParallelOldGC开关操控运用ParallelScavenge+ParallelOld组合搜集器进行搜集。
CMS(ConcurrentMarkSweep)搜集器:老时代搜集器,致力于获取最短收回中止时刻(即缩短废物收回的时刻),运用符号铲除算法,多线程,优点是并发搜集(用户线程能够和GC线程同时作业),中止小。运用-XX:+UseConcMarkSweepGC进行ParNew+CMS+SerialOld进行内存收回,优先运用ParNew+CMS(原因见后边),当用户线程内存缺乏时,选用备用计划SerialOld搜集。
CMS搜集的履行进程是:初始符号(CMS-initial-mark)->并发符号(CMS-concurrent-mark)–>预收拾(CMS-concurrent-preclean)–>可控预收拾(CMS-concurrent-abortable-preclean)->重新符号(CMS-remark)->并发铲除(CMS-concurrent-sweep)->并发重设状况等待下次CMS的触发(CMS-concurrent-reset)
详细的说,先2次符号,1次预收拾,1次重新符号,再1次铲除。
1,首要jvm依据-XX:CMSInitiatingOccupancyFraction,-XX:+UseCMSInitiatingOccupancyOnly来决议什么时刻开始废物搜集;
2,假如设置了-XX:+UseCMSInitiatingOccupancyOnly,那么只有当old代占用的确到达了-XX:CMSInitiatingOccupancyFraction参数所设定的份额时才会触发cmsgc;
3,假如没有设置-XX:+UseCMSInitiatingOccupancyOnly,那么体系会依据统计数据自行决议什么时分触发cmsgc;因而有时会遇到设置了80%份额才cmsgc,可是50%时就现已触发了,便是由于这个参数没有设置的原因;
4,当cmsgc开始时,首要的阶段是初始符号(CMS-initial-mark),是stoptheworld阶段,因而此阶段符号的目标只是从root集最直接可达的目标;
CMS-initial-mark:961330K(1572864K),指符号时,old代的已用空间和总空间
5,下一个阶段是并发符号(CMS-concurrent-mark),此阶段是和运用线程并发履行的,所谓并发搜集器指的便是这个,首要效果是符号可达的目标,此阶段不需求用户中止。
此阶段会打印2条日志:CMS-concurrent-mark-start,CMS-concurrent-mark
6,下一个阶段是CMS-concurrent-preclean,此阶段首要是进行一些预收拾,由于符号和运用线程是并发履行的,因而会有些目标的状况在符号后会改动,此阶段正是处理这个问题由于之后的Rescan阶段也会stoptheworld,为了使暂停的时刻尽或许的小,也需求preclean阶段先做一部分作业以节省时刻
此阶段会打印2条日志:CMS-concurrent-preclean-start,CMS-concurrent-preclean
7,下一阶段是CMS-concurrent-abortable-preclean阶段,参加此阶段的意图是使cmsgc更加可控一些,效果也是履行一些预收拾,以减少Rescan阶段形成运用暂停的时刻
此阶段涉及几个参数:
-XX:CMSMaxAbortablePrecleanTime:当abortable-preclean阶段履行到达这个时刻时才会结束
-XX:CMSScheduleRemarkEdenSizeThreshold(默许2m):操控abortable-preclean阶段什么时分开始履行,
即当eden运用到达此值时,才会开始abortable-preclean阶段
-XX:CMSScheduleRemarkEdenPenetratio(默许50%):操控abortable-preclean阶段什么时分结束履行
此阶段会打印一些日志如下:
CMS-concurrent-abortable-preclean-start,CMS-concurrent-abortable-preclean,
CMS:abortprecleanduetotimeXXX
8,再下一个阶段是第二个stoptheworld阶段了,即Rescan阶段,此阶段暂停运用线程,中止时刻比并发符号小得多,但比初始符号稍长。对目标进行重新扫描并符号;
YGoccupancy:964861K(2403008K),指履行时young代的状况
CMSremark:961330K(1572864K),指履行时old代的状况
此外,还打印出了弱引证处理、类卸载等进程的耗时
9,再下一个阶段是CMS-concurrent-sweep,进行并发的废物收拾
10,最终是CMS-concurrent-reset,为下一次cmsgc重置相关数据结构
有2种状况会触发CMS的失望fullgc,在失望fullgc时,整个运用会暂停
A,concurrent-mode-failure:预收拾阶段或许呈现,当cmsgc正进行时,此刻有新的目标要进行old代,可是old代空间缺乏形成的。其或许性有:1,O区空间缺乏以让新生代晋级,2,O区空间用完之前,无法完结对无引证的目标的收拾。这标明,当时有大量数据进入内存且无法开释。
B,promotion-failed:新生代younggc或许呈现,当进行younggc时,有部分young代目标依然可用,可是S1或S2放不下,因而需求放到old代,但此刻old代空间无法容纳此。
影响cmsgc时长及触发的参数是以下2个:
-XX:CMSMaxAbortablePrecleanTime=5000
-XX:CMSInitiatingOccupancyFraction=80
处理也是针对这两个参数来的,根本的原因是每次请求耗费的内存量过大
处理办法:
A,针对cmsgc的触发阶段,调整-XX:CMSInitiatingOccupancyFraction=50,提早触发cmsgc,就能够缓解当old代到达80%,cmsgc处理不完,然后形成concurrentmodefailure引发fullgc
B,修改-XX:CMSMaxAbortablePrecleanTime=500,缩小CMS-concurrent-abortable-preclean阶段的时刻
C,考虑到cmsgc时不会进行compact,因而参加-XX:+UseCMSCompactAtFullCollection
(cmsgc后会进行内存的compact)和-XX:CMSFullGCsBeforeCompaction=4(在fullgc4次后会进行compact)参数
在CMS收拾进程中,只有初始符号和重新符号需求时间短中止,并发符号和并发铲除都不需求暂停用户线程,因而功率很高,很适合高交互的场合。
CMS也有缺陷,它需求耗费额外的CPU和内存资源,在CPU和内存资源紧张,CPU较少时,会加重体系负担(CMS默许发动线程数为(CPU数量+3)/4)。
另外,在并发搜集进程中,用户线程依然在运转,依然发生内存废物,所以或许发生“起浮废物”,本次无法收拾,只能下一次FullGC才收拾,因而在GC期间,需求预留满足的内存给用户线程运用。所以运用CMS的搜集器并不是老时代满了才触发FullGC,而是在运用了一大半(默许68%,即2/3,运用-XX:CMSInitiatingOccupancyFraction来设置)的时分就要进行FullGC,假如用户线程耗费内存不是特别大,能够适当调高-XX:CMSInitiatingOccupancyFraction以下降GC次数,提高功能,假如预留的用户线程内存不够,则会触发ConcurrentModeFailure,此刻,将触发备用计划:运用SerialOld搜集器进行搜集,但这样中止时刻就长了,因而-XX:CMSInitiatingOccupancyFraction不宜设的过大。
还有,CMS选用的是符号铲除算法,会导致内存碎片的发生,能够运用-XX:+UseCMSCompactAtFullCollection来设置是否在FullGC之后进行碎片收拾,用-XX:CMSFullGCsBeforeCompaction来设置在履行多少次不紧缩的FullGC之后,来一次带紧缩的FullGC。
G1搜集器:在JDK1.7中正式发布,与现状的新生代、老时代概念有很大不同,现在运用较少,不做介绍。

未经允许不得转载:IT技术网站 » java虚拟机(Java虚拟机的原理)
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

志在指尖 用双手敲打未来

登录/注册IT技术大全

热门IT技术

C#基础入门   SQL server数据库   系统SEO学习教程   WordPress小技巧   WordPress插件   脚本与源码下载