目录
一、GVM内存结构
1、JVM体系概览
2、Java内存结构
3、java8以后的jvm
二、常见的垃圾回收算法
1、引用计数算法
2、标记清除算法(追踪回收算法)
3、复制回收算法——针对新生代
4、标记整理算法(压缩回收算法)——针对老年代
5、分代回收算法
三、JVM垃圾回收的时候如何确定垃圾?什么是GC Roots?
1、什么是垃圾?
2、要进行垃圾回收,如何判断一个对象是否可以被回收?
四、JVM调优和参数配置,如何盘点查看JVM系统默认值?
1、JVM的参数类型
2、查看JVM默认值
五、平时工作用过的JVM常用基本配置参数有哪些?
1、java查看虚拟机内存容量和最大内存量
2、JVM常用基本配置参数
3、实操设置JVM参数及收集详细GC日志收集信息
六、强引用/软引用/弱引用/虚引用分别是什么
1、整体架构
2、强引用(默认支持模式)
3、软引用
4、弱引用
5、虚引用
6、软引用和弱引用的适用场景
7、ReferenceQueue
8、GCRoots和四大引用的小总结
七、谈谈你对OOM的认识
1、java.lang.StackOverflowError——栈溢出
2、java.lang.OutOfMemoryError: Java heap space——堆内存不够用
3、java.lang.OutOfMemoryError: GC overhead limit exceeded——GC回收时间过长,频繁GC又没效果
4、java.lang.OutOfMemoryError: Direct buffer memory——直接内存挂了
5、java.lang.OutOfMemoryError: unable to create new native thread——创建线程数量达到上限,不能再创建更多本地线程了
6、java.lang.OutOfMemoryError:Metaspace——元空间满了
八、七大垃圾收集器及其在生产上的配置
1、Serial/Serial Coping收集器(用于新生代)
2、ParNew收集器(新生代)
3、Parallel / Parallel Scavenge收集器(“吞吐量优先”收集器)(新生代)
4、Serial Old收集器(老年代)
5、Parallel Old收集器(老年代)
6、CMS收集器(老年代)
7、G1收集器(Garbage First)
8、怎么查看默认的垃圾回收器是哪个?
9、小结:如何选择垃圾回收器
GC作用域:方法区和堆区
(1)PC寄存器/程序计数器(Program Counter Register)
严格来说是一个数据结构,用于保存当前正在执行的程序的内存地址,由于Java是支持多线程执行的,所以程序执行的轨迹不可能一直都是线性执行。当有多个线程交叉执行时,被中断的线程的程序当前执行到哪条内存地址必然要保存下来,以便用于被中断的线程恢复执行时再按照被中断时的指令地址继续执行下去。为了线程切换后能恢复到正确的执行位置,每个线程都需要有一个独立的程序计数器,各个线程之间计数器互不影响,独立存储,我们称这类内存区域为“线程私有”的内存,这在某种程度上有点类似于“ThreadLocal”,是线程安全的。
(2)Java栈(Java Stack)
Java栈总是与线程关联在一起的,每当创建一个线程,JVM就会为该线程创建对应的Java栈,在这个Java栈中又会包含多个栈帧(Stack Frame),这些栈帧是与每个方法关联起来的,每运行一个方法就创建一个栈帧,每个栈帧会含有一些局部变量、操作栈和方法返回值等信息。每当一个方法执行完成时,该栈帧就会弹出栈帧的元素作为这个方法的返回值,并且清除这个栈帧,Java栈的栈顶的栈帧就是当前正在执行的活动栈,也就是当前正在执行的方法,PC寄存器也会指向该地址。只有这个活动的栈帧的本地变量可以被操作栈使用,当在这个栈帧中调用另外一个方法时,与之对应的一个新的栈帧被创建,这个新创建的栈帧被放到Java栈的栈顶,变为当前的活动栈。同样现在只有这个栈的本地变量才能被使用,当这个栈帧中所有指令都完成时,这个栈帧被移除Java栈,刚才的那个栈帧变为活动栈帧,前面栈帧的返回值变为这个栈帧的操作栈的一个操作数。
由于Java栈是与线程对应起来的,Java栈数据不是线程共有的,所以不需要关心其数据一致性,也不会存在同步锁的问题。
在Java虚拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。在Hot Spot虚拟机中,可以使用-Xss参数来设置栈的大小。栈的大小直接决定了函数调用的可达深度。
(3)本地方法栈(Native Method Stack)
本地方法栈和Java栈所发挥的作用非常相似,区别不过是Java栈为JVM执行Java方法服务,而本地方法栈为JVM执行Native方法服务。本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。
(4)堆 (Heap)
堆是JVM所管理的内存中最大的一块,是被所有Java线程锁共享的,不是线程安全的,在JVM启动时创建。堆是存储Java对象的地方,这一点Java虚拟机规范中描述是:所有的对象实例以及数组都要在堆上分配。Java堆是GC管理的主要区域,从内存回收的角度来看,由于现在GC基本都采用分代收集算法,所以Java堆还可以细分为:新生代和老年代;新生代再细致一点有Eden空间、From Survivor空间、To Survivor空间等。
(5)方法区(Method Area)
方法区存放了要加载的类的信息(名称、修饰符等)、类中的静态常量、类中定义为final类型的常量、类中的Field信息、类中的方法信息,当在程序中通过Class对象的getName.isInterface等方法来获取信息时,这些数据都来源于方法区。方法区是被Java线程锁共享的,不像Java堆中其他部分一样会频繁被GC回收,它存储的信息相对比较稳定,在一定条件下会被GC,当方法区要使用的内存超过其允许的大小时,会抛出OutOfMemory的错误信息。方法区也是堆中的一部分,就是我们通常所说的Java堆中的永久区 Permanet Generation,大小可以通过参数来设置,可以通过-XX:PermSize指定初始值,-XX:MaxPermSize指定最大值。
(6)常量池(Constant Pool)
常量池本身是方法区中的一个数据结构。常量池中存储了如字符串、final变量值、类名和方法名常量。常量池在编译期间就被确定,并保存在已编译的.class文件中。一般分为两类:字面量和应用量。字面量就是字符串、final变量等。类名和方法名属于引用量。引用量最常见的是在调用方法的时候,根据方法名找到方法的引用,并以此定为到函数体进行函数代码的执行。引用量包含:类和接口的权限定名、字段的名称和描述符,方法的名称和描述符。
在堆中对每个对象都有一个引用计数器,当对象被引用时,引用计数器加1;当引用被置为空或者离开这个作用域时,引用计数减1,这种做法的缺点是每次对对象赋值均要维护引用计数器,并且无法解决相互引用的问题,因此JVM没有采用这个算法。
标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。一种可行的实现是,在标记阶段,利用JVM维护的对象引用图,从根节点开始遍历对象的引用图,同时标记遍历到的对象,即为可达对象。未被标记的对象就是未被引用的垃圾对象。然后,在清除阶段,清除所有未被标记的对象。
它主要由两个缺点:一个是效率问题,标记和清除过程的效率都不高;另一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致当程序在以后的运行过程中需要分配较大对象时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。
为了解决标记清除算法的效率问题,出现了复制算法,它将可用内存按容量划分为大小相等的两块,每次使用其中的一块。当这块的内存用完了,就将还存活着的对象复制到另一块上面,然后再把已使用过的内存空间一次清理掉。
优点是每次都是对其中的一块进行内存回收,内存分配时就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。缺点是将内存缩小为原来的一半,代价太高了一点。
现在的商业虚拟机都采用复制收集算法来回收新生代,有研究表明,新生代中的对象98%是朝生夕死的,所以并不需要按照1:1的比例来划分内存空间,而是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中的一块Survivor。当回收时,将Eden和Survivor中还存活着的对象一次性地拷贝到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,也就是每次新生代中可用内存空间为整个新生代容量的90%(80%+10%),只有10%的内存是会被“浪费”的。当然,并不能保证每次回收都只有10%的对象存活,当Survivor空间不够用时,需要依赖其他内存(这里指老年代)进行分配担保(Handle Promotion)。即如果另外一块Survivor空间没有足够的空间存放上一次新生代收集下来的存活对象,这些对象将直接通过分配担保机制进入老年代。
复制收集算法在对象存活率较高时就需要执行较多的复制操作,效率将会变低。更关键的是,如果不想浪费50%的空间,就需要有额外的空间进行分配担保,以应对被使用的内存中所有对象都100%存活的极端情况,所以在老年代一般不能直接选用复制收集算法。
根据老年代的特点提出了“标记-整理”算法,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是把堆中活动的对象移动到堆中一端,这样就会在堆中另外一端留出很大一片空闲区域,相当于对堆中的碎片进行处理
当前商业虚拟机的垃圾收集都采用“分代收集”算法,这种算法并无新的方法,只是根据对象的存活周期的不同将内存划分为几块,一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收
简单说就是内存中已经不再被使用的空间就是垃圾。比如说没有引用指向这个对象了
(1)引用计数法
Java中,引用和对象是有关联的。如果要操作对象则必须用引用进行。可通过引用计数来判断一个对象是否可以回收。简单说,给对象中添加一个引用计数器,每当有一个地方引用它,计数器值加1每当有一个引用失效时,计数器值减1。任何时刻计数器值为零的对象就是不可能再被使用的,那么这个对象就是可回收对象
那为什么主流的Java虚拟机里面都没有选用这种算法呢?其中最主要的原因是它很难解决对象之间相互循环引用的问题
(2)枚举根节点做可达性分析(根搜索路径)
Java可以做GCRoots的对象:
例如:
(1)标配参数
(2)X参数
(3)XX参数
说明:
jps -l 【查看java进程,定位进程编号】
jinfo -flag 某属性 进程id 【查看jvm参数】
jstack 进程id 【查看进程运行情况,可用于排错】
举例:
是否打印GC收集细节:-XX:+PrintGCDetails 或 -XX:-PrintGCDetails
是否使用串行垃圾收集器:-XX:+UseSerialGC或 -XX:-UseSerialGC
设置Metaspace大小:-XX:MetaspaceSize = 128m
年龄阈值,默认15(对象被复制的次数):-XX:MaxTenuringThreshold = 15
举例:jps -l 【获得进程编号】
I、jinfo -flag InitialHeapSize 进程编号 【初始堆内存】
II、jinfo -flags 进程编号
III、jinfo -flag MaxHeapSize 进程编号 【最大堆内存】
-Xms等价于-XX:InitialHeapSize
-Xmx等价于-XX:MaxHeapSize
(1)-XX:+PrintFlagsInitial :查看默认值
java -XX:+PrintFlagsInitial
java -XX:+PrintFlagsInitial -version
(2)-XX:+PrintFlagsFinal :主要查看修改更新
java -XX:+PrintFlagsFinal
java -XX:+PrintFlagsFinal -version
说明:【“=”是默认值,“:=是修改过后的值”】
(3)PrintFlagsFinal 举例,运行java命令的同时打印出参数
(4)-XX:+PrintCommandLineFlags -version
(1)-Xms:初始大小内存,默认为物理内存1/64,等价于-XX:InitialHeapSize
(2)-Xmx:最大分配内存,默认为物理内存1/4,等价于-XX:MaxHeapSize
(3)-Xss:设置单个线程栈的大小,一般默认为512K~1024K,等价于-XX:ThreadStackSize
(4)-Xmn:设置年轻代大小
(5)-XX:MetaspaceSize:设置元空间大小
典型设置案例:
(6)-XX:+PrintGCDetails:收集详细GC日志收集信息
(7)-XX:SurvivoRatio:设置新生代中eden和s0/s1空间的比例
默认-XX:SurvivoRatio=8,Eden:s0:s1=8:1:1,SurvivoRatio的值就是这只eden区的比例,s0/s1相同
(8)-XX:NewRatio:配置新生代和老年代在堆结构的占比
默认-XX:NewRatio=2,新生代占1,老年代占2,新生代占整个堆的1/3,NewRatio值就是这事老年代的占比
(9)-XX:MaxTenuringThreshold :设置垃圾最大年龄,就是从young到old要经过多少次的垃圾回收
(1)-Xss查看和修改单个线程栈的大小
奇怪:这里ThreadStackSize为0
设置JVM参数
再次查看
如果是0表示用的是系统出厂默认值,其他值则表示是修改过的
(2)收集详细GC日志收集信息
假设我们现在把初始大小内存和最大内存容量设置为10M,并打印GC日志信息
配置jvm参数:
正常情况下:
现在定义了一个超过10M的数据,会进行GC和Full GC,出现OOM异常
(2)GC和Full GC垃圾回收参数解读
引用类型 | 说明 | 适用场景 |
强引用(默认支持模式) | 对于强引用的对象,就算是出现了OOM也不会对该对象进行回收,死都不收。 | |
软引用 | 当系统内存充足时不会被回收,当系统内存不足时会被回收 | 高速缓存 |
弱引用 | 只要垃圾回收机制一运行,不管JVM的内存空间是否足够,都会回收该对象占用的内存 | |
虚引用 | 在任何时候都可能被垃圾回收器回收 | 当关联的引用队列中有数据的时候,意味着引用指向的堆内存中的对象被回收。通过这种方式,JVM运行我们在对象被销毁后,做一些我们自己想做的事情 |
当内存不足,JVM开始垃圾回收,对于强引用的对象,就算是出现了OOM也不会对该对象进行回收,死都不收。
强引用是我们最常见的普通对象引用,只要还有强引用指向一个对象,就能表明对象还“活着”,垃圾收集器不会碰这种对象。在java中最常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是一个强引用。当一个对象被强引用变量引用时,它处于可达状态,它是不可能被垃圾回收机制回收的,即使该对象以后永远都不会被用到,JVM也不会回收。因此强引用是造成Java内存泄漏的主要原因之一。
对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应(强)引用赋值为null,一般认为就是可以被垃垃圾收集的了(当然具体回收时机还是要看垃圾收集策略)
举例:只要还有强引用指向一个对象,就能表明对象还“活着”,垃圾收集器不会碰这种对象。
现象:obj1你回收你的,obj2是强引用,不会被回收
软引用是一种相对强引用弱化了一些的引用,需要用java.lang.ref.SoftReference类来实现,可以让对象豁免一些垃圾收集。
对于只有软引用的对象来说,当系统内存充足时不会被回收,当系统内存不足时会被回收
软引用通常用在对内存敏感的程序中,比如高速缓存就有用到软引用,内存够用的时候就保留,不够用就回收!
举例:内存足够的情况下软引用不会被回收
现象:由于内存足够,此时垃圾回收器不回收软引用
举例:内存不够用的情况下软引用会被回收
配置参数:-Xms10m -Xmx10m。JVM配置,故意产生大对象并配置大内存,让它内存不够了就导致OOM,看软引用的回收情况
现象:由于内存不够了,此时垃圾回收器回收软引用
弱引用需要用java.lang.WeakReference类来实现,它比软引用的生命期更短。对于只有弱引用的对象来说,只要垃圾回收机制一运行,不管JVM的内存空间是否足够,都会回收该对象占用的内存
举例:只要有gc弱引用就会被回收
EG:WeakHashMap
虚引用需要java.lang.ref.PhantomReference类来实现。如果一个对象仅持有虚引用,那么他就和没有任何引用一样,在任何时候都可能被垃圾回收器回收,它不能单独使用也不能通过它访问对象,虚引用必须和引用队列联合使用。
虚引用的主要作用是跟踪对象被垃圾回收的状态。仅仅是提供了一种确保对象被finalize以后,做某些事情的机制。PhantomReference的get方法总是返回null,因此无法访问对应的引用对象。其意义在于说明一个对象已经进入finalization阶段,可以被gc回收,用来实现比finalization机制更灵活的回收操作。
换句话说,设置虚引用关联的唯一目的,就是在这个对象被收集器回收的时候收到一个系统通知或者后续添加进一步的处理。java技术允许使用finalize方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。
举例:虚引用的get方总是返回null;虚引用被回收前需要被引用队列ReferenceQueue保存下
java提供了4中引用类型,在垃圾回收时,都有各自的特点。
ReferenceQueue是用来配合引用工作的,没有ReferenceQueue一样可以运行。
创建引用的时候可以指定关联的队列,当GC释放对象内存的时候,会将引用加入到引用队列,如果程序发现某个虚引用已经被加入到引用队列当中,那么就可以在所引用的对象被内存回首之前采取必要的行动,这相当于是一种通知机制
当关联的引用队列中有数据的时候,意味着引用指向的堆内存中的对象被回收。通过这种方式,JVM运行我们在对象被销毁后,做一些我们自己想做的事情
OOM类型 | 是什么 | 产生原因 |
java.lang.StackOverflowError | 栈溢出 | 如不合理的递归调用 |
java.lang.OutOfMemoryError: Java heap space | 堆内存不够用 | 如死循环、创建大的对象但是堆内存根本就不够分配 |
java.lang.OutOfMemoryError: GC overhead limit exceeded | GC回收时间过长,频繁GC又没效果 | 当GC为释放很小空间占用大量时间时抛出。一般是因为堆太小,没有足够的内存。 |
java.lang.OutOfMemoryError: Direct buffer memory | 直接内存挂了 | 元空间并不在虚拟机中,而是使用本地内存。不归GC管,所以GC不会回收,如果本地内存用完了就会抛出此异常。 |
java.lang.OutOfMemoryError: unable to create new native thread | 创建线程数量达到上限,不能再创建更多本地线程了 | 应用创建太多线程,超过系统承载能力 |
java.lang.OutOfMemoryError:Metaspace | 元空间满了 | 虚拟机加载的类信息、常量池、静态变量、即时编译后的代码不断往元数据空间灌,占据的总空间超过了Metaspace指定的元空间大小 |
产生的原因:不合理的递归调用【过深的递归调用】
产生的原因:死循环
产生原因:超过堆内存大小限制
配置参数:-Xms10m -Xmx10m
程序在垃圾回收上花费了98%的时间,却收集不回2%的空间,通常这样的异常伴随着CPU的冲高
GC回收时间过长时会抛出OutOfMemoryError。过长的定义是,超过98%的时间用来做GC并且回收了不到2%的堆内存。连续多次GC都只回收了不到2%的极端情况下才会抛出。
假如不抛出 GC overhead limit 错误会发生什么情况:那就是GC清理的这么点内存很快就会再次填满,迫使GC再次执行。这样就造成恶性循环,CPU使用率一直是100%,而GC却没有任何成果。
产生的原因:当GC为释放很小空间占用大量时间时抛出。一般是因为堆太小,没有足够的内存。虽然在做GC,但基本上没回收
配置参数:-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
解决方法:
(1)首先检查程序有没有死循环或者其他一些导致内存被大量占用的程序,如果确定程序没有问题,只是程序本身需要大内存时,通过设置增加内存。
(2)添加jvm启动参数限制使用内存:-XX:UseGCOverheadLimit
产生的原因:元空间并不在虚拟机中,而是使用本地内存。不归GC管,所以GC不会回收,如果本地内存用完了就会抛出此异常。【对象没有new在堆内存,而是在本地内存】
配置参数:-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
高并发请求服务器时,经常出现如下异常:java.lang.OutOfMemoryError: unable to create new native thread,准确地讲该native thread异常与应用的平台有关
运行结果:
非ROOT用户登录Linux系统测试->服务器级别参数调优:
java8之后使用Metaspace来代替永久代。Metaspace是在方法区HotSpot中实现的,它与持久代最大的区别在于:Metaspace并不在虚拟机内存中而是使用本地内存中。它用来存放虚拟机加载的类信息、常量池、静态变量、即时编译后的代码。MaxMetaspaceSize初始化大小是20M左右。
产生原因:不断生成类往元数据空间灌,类占据的总空间超过了Metaspace指定的元空间大小
配置参数:-XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
GC算法(引用计数/复制/标记/标整)是内存回收的方法论,垃圾收集器就是算法落地实现。
因为目前为止还没有完美的收集器出现,更加没有万能的收集器,只是针对具体应用最适合的收集器,进行分代收集
七种主要的垃圾收集器:
垃圾收集器类型 | 串行/并行/并发 | 新生代/老年代 | 详细说明 | 算法 | 目标 | 适用场景 |
串行垃圾回收器(Serial) | 串行 | 新生代 | 为单线程环境设计并且只适用一个线程进行垃圾回收,会暂停所有的用户线程 | 复制算法 | 响应速度优先 | 适用于单CPU环境,不适合服务器环境 |
Serial Old | 串行 | 老年代 | 现在已经没有了 | 标记-整理算法 | 响应速度优先 | 单CPU环境下的Client模式、CMS的后备预案 |
并行垃圾回收器(Parallel) | 并行 | 新生代 | 多个垃圾回收器并行工作,此时用户线程是暂停的 | 复制算法 | 响应速度优先 | 多CPU环境时在Server模式下与CMS配合 |
Parallel Scavenge | 并行 | 新生代 | 同上 | 复制算法 | 吞吐量优先 | 在后台运算而不需要太多交互的任务,如科学计算/大数据处理 |
Parallel Old | 并行 | 老年代 | 同上 | 标记-整理算法 | 吞吐量优先 | 在后台运算而不需要太多交互的任务,如科学计算/大数据处理 |
并发垃圾回收器(CMS) | 并发 | 老年代 | 用户线程和垃圾收集器同时执行(不一定是并行,可能交替执行),不需要停顿用户线程 | 标记-清除算法 | 响应速度优先 | 适用于对响应时间有要求的场景,集中在互联网站或B/S系统服务端上的Java应用 |
G1垃圾回收器 | 并发 | both | 将堆内存分割成不同的区域然后并发地对其进行垃圾回收 | 标记-整理+复制算法 | 响应速度优先 | 面向服务端应用,将来替换CMS |
单线程,在进行垃圾收集时必须暂停其他所有的工作线程,使用复制算法。虚拟机运行在Client模式下的默认新生代收集器。简单而高效(与其他收集器的单线程比),对于限定单个CPU的环境来说,由于没有线程交互的开销可以获得最高的单线程垃圾收集效率,因此Serial垃圾收集器依然是java虚拟机在client模式下默认的新生代垃圾收集器。
对应的JVM参数是:-XX:+UseSerialGC
开启后会使用:Serial(Young区用)+Seiral Old(Old区用)的收集器组合
表示:新生代、老年代都会使用串行回收收集器,新生代使用复制算法,老年代使用标记-整理算法
JVM参数配置:
-Xms10m -Xmx10m -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseSerialGC
ParNew收集器其实是Serial收集器的多线程版本,采用复制算法,它是许多运行在Server模式下的虚拟机中首选的新生代收集器,因为除了Serial收集器外,目前只有它能与CMS收集器配合工作。
对应的JVM参数是:-XX:+UseParNewGC
启用ParNew收集器,只影响新生代的收集,不影响老年代,开启上述参数后,会使用:ParNew(Young区用)+Seiral Old(Old区用)的收集器组合,新生代使用复制算法,老年代采用标记-整理算法。但是这样的组合java8已不再推荐使用
JVM参数配置:
-Xms10m -Xmx10m -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseParNewGC
使用复制算法,并行多线程,这些特点与ParNew一样
停顿时间越短对于需要与用户交互的程序来说越好,良好的响应速度能提升用户的体验;高吞吐量可以最高效率地利用CPU时间,尽快地完成程序的运算任务,主要适合在后台运算而不太需要太多交互的任务。
对应的JVM参数是:-XX:+UseParallelGC 或 -XX:+UseParallelOldGC(可互相激活)
启用Parallel Scavenge收集器,开启上述参数后,会使用:Parallel Scavenge(Young区用)+ Parallel Old(Old区用)的收集器组合,新生代使用复制算法,老年代采用标记-整理算法。
JVM参数配置:
-Xms10m -Xmx10m -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseParallelGC
-Xms10m -Xmx10m -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseParallelOldGC
其他参数设置:
-XX:MaxGCPauseMillis 控制最大垃圾收集停顿时间。(大于0的毫秒数)停顿时间缩短是以牺牲吞吐量和新生代空间换取的。(新生代调的小,吞吐量跟着小,垃圾收集时间就短,停顿就小)。
-XX:GCTimeRatio 直接设置吞吐量大小,0 -XX:+UseAdaptiveSizePolicy 一个开关参数,开启GC自适应调节策略(GC Ergonomics),将内存管理的调优任务(新生代大小-Xmn、Eden与Survivor区的比例-XX:SurvivorRatio、晋升老年代对象年龄-XX: PretenureSizeThreshold 、等细节参数)交给虚拟机完成。这是Parallel Scavenge收集器与ParNew收集器的一个重要区别,另一个是吞吐量。 它是Serial收集器的老年代版本,单线程,使用“标记-整理”算法。主要意义是被Client模式下的虚拟机使用。 如果在Server模式下,它还有两大用途: 它是Parallel Scavenge收集器的老年代版本,多线程,使用“标记-整理”算法。在注重吞吐量及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge+Parallel Old收集器。 它是一种以获取最短回收停顿时间为目标的收集器,尽可能缩短垃圾收集时用户线程的停顿时间。优点:并发收集,低停顿。基于“标记-清除”算法。 目前很大一部分Java应用都集中在互联网站或B/S系统的服务端上,这类应用尤其重视服务的响应速度,希望系统停顿时间最短,以给用户带来较好的体验,CMS收集器就非常适用于堆内存大、CPU核数多的服务端应用。运作过程分为4个步骤: 对应的JVM参数是:-XX:+UseConcMarkSweeoGC 启用UseConcMarkSweeoGC 收集器,开启上述参数后,会自动将-XX:+UseParNewGC打开,使用:ParNew(Young区用)+ CMS(Old区用)+ Serial Old的收集器组合,Serial Old将作为CMS出错的后备收集器,新生代使用复制算法,老年代采用标记-清除算法。 JVM参数配置: 优点:并发收集低停顿 缺点: (1)以前收集器特点 (2)G1是什么及其特点 G1可解决内存碎片问题。它是一款面向服务端应用的收集器,主要应用在多CPU和大内存服务器环境下,极大减少垃圾收集的停顿时间。主要改变是Eden,Survivor和Tenured等内存区域不再是连续的了,而是变成一个个大小一样的region,每个region从1M到32M不等。一个region可能属于Eden,Survivor或Tenured的内存区域 特点: (3)底层原理 G1区域化垃圾回收器: 做大的好处是化整为零,避免全内存扫描,只需要按照区域来进行扫描即可 回收步骤: G1收集器下的Young GC 4步过程: 常用参数配置: 与CMS相比有两个显著改进: JVM参数:java -XX:+PrintCommandLineFlags -version4、Serial Old收集器(老年代)
5、Parallel Old收集器(老年代)
6、CMS收集器(老年代)
-Xms10m -Xmx10m -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseConcMarkSweeoGC
7、G1收集器(Garbage First)
-Xms10m -Xmx10m -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseG1GC
8、怎么查看默认的垃圾回收器是哪个?
9、小结:如何选择垃圾回收器