1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > JVM GC策略

JVM GC策略

时间:2020-10-07 05:55:23

相关推荐

JVM GC策略

静态内存

在Java中静态内存分配是指在Java被编译时就已经能够确定需要的内存空间,当程序被加载时系统把内存一次性分配给它。这些内存不会在程序执行时发生变化,直到程序执行结束时内存才会被回收。在Java的类和方法中的局部变量包括原生数据类型和对象的引用都是静态分配内存的。静态内存空间实在Java栈上分配的,当方法运行结束时,对应的栈帧也就撤销了,所以分配的静态内存空间也就被回收了。

// 形参 arg 为基础数据类型int 4个字节public void test(int arg) {// 对象引用 4个字节(对象引用一律为4个字节表示内存地址)String s = "helloword";// 基础数据类型 long 8个字节long l1 = 1;// 对象引用 4个字节Long l2 = 1L;}

动态内存

除了Java基础数据类型,其他都是对象类型,它们存储在Java堆中,可以被共享,不一定随着方法执行结束而消失。对于对象类型,在Java栈中会分配一个4字节的地址指针空间(引用),这个地址指针指向该对象在堆中的地址。

对象的内存空间是动态分配的,即只有在程序执行时才知道要分配的存储空间大小,这个对象什么时候被回收也是不确定的,只有等到这个对象不再使用时才会被回收。动态内存的分配和回收是一个值得讨论的问题,下面就看看垃圾收集器(下面简称GC)是怎样来解决堆内存回收的。

检测垃圾

GC的第一个使命就是正确地检测出垃圾对象,这也是关键点。先给出垃圾对象的定义:不能够被一个根对象集合到达的对象。根对象集合和JVM的具体实现有关系,但大都包含如下元素:

1)静态属性引用的对象

2)虚拟机栈中的引用对象

3)在本地方法中持有的对象引用:有些对象被传入本地方法中,但是这些对象还没有被释放

对于引用,补充说明一点。上面说的引用指的是强引用,在Java中有四种引用类型,如下:

1)强引用:是指创建一个对象并把这个对象赋给一个引用变量, 强引用有引用变量指向时永远不会被垃圾回收,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。

2)软引用:如果一个对象具有软引用,内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。软引用可用来实现内存敏感的高速缓存,比如网页缓存、图片缓存等。使用软引用能防止内存泄露,增强程序的健壮性。

3)弱引用:弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。

4)虚引用:虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收。在java中用java.lang.ref.PhantomReference类表示。

基于分代的垃圾收集算法

目前使用最广泛垃圾收集算法是hotspot VM(sun JDK和open JDK使用的虚拟机)中使用的基于分代的垃圾收集算法。该算法思路是这样的:把对象按照寿命长短来分组,分为年轻代和老年代,新创建的对象被分在年轻带中,如果对象经过几次回收后仍然存活,就把这个对象划分到老年代,老年代的收集操作不像年轻代那么频繁,这样就减少了每次垃圾收集时所要扫描的对象的数量,从而提高了垃圾回收效率。

![这里写图片描述](https://img-/0417141533230?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTI0ODQxNzI=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)

如上图所示,JVM将整个堆划分为Young区、Old区和Perm区,分别存放不同年龄的对象,这三个区存放的对象有如下区别:

Young:分为Eden区和两个Survivor区,其中所有新创建的对象都在Eden区,当Eden区满后会出发minor GC将Eden区仍然存好的对象复制到其中一个Survivor区中,另外一个Survivor区中的存活对象也复制到这个Survivor中,以保证始终有一个Survivor区是空的。一般情况下,Young区的大小为整个堆区的1/4,Survivor区为整个Young区的1/8Old区:存放Young区的Survivor满后出发minor GC后仍然存活的对象,当Eden区满后会将对象存放到Survivor区中,如果Survivor区仍然存不下这些对象,GC收集器会将这些对象直接存放到Old区。如果在Survivor区中的对象足够老,也直接存放到Old区。如果Old区也满了,将会触发Full GC,回收整个堆内存Perm区:存放的主要是类的Class对象,如果一个类被频繁的加载,也可能会导致Perm区满,Perm区的垃圾回收也是由Full GC触发的

下面看Hotspot提供的三类具体的垃圾收集算法

Serial Collector

JVM在client模式下默认的GC方式。可以通过-XX:+UseSerialGC来指定使用该收集算法,该算法是串行的,GC动作是单线程完成,因此GC时其他应用程序会全部停止(所谓的“stop-the-world”)。

当Minor GC时,除了将Eden区的非活动对象回收以外,还会把一些老对象也复制到Old区中。这个老对象的定义是通过配置参数MaxTenuringThreshold来控制的,如-XX:MaxTenuringThreshold=5,则如果这个对象已经被Minor GC回收过5次后仍然存活,那么这个对象在这次Minor GC后会直接被放入Old区。还有一种情况,当这次Minor GC时Survivor区中的To Space放不下这些对象时,这些对象也将直接放入Old区。如果Old区或者Perm区空间不足,将会触发Full GC,Full GC会检查堆中的所有对象,清除所有垃圾对象,如果是Perm区,会清除已经被卸载的classloader中加载的类的信息。

Parallel Collector

根据Minor GC 和 Full GC的不同分为三种。

1)ParNewGC

回收策略和Serial Collector类似,只是回收操作是多线程并行回收,但同样会导致“stop-the-world”。通过-XX:+UseParNewGC来指定,通过UseAdaptiveSizePolicy来配置Eden、From Space和To Space的TenuringThreshold大小。

2)ParallelGC

在Server下默认的GC方式,通过-XX:+UseParallelGC参数来指定,并行回收的线程数由-XX:ParallelGCThreads指定(如果CPU的核数小于8,线程数可以和核数一样,如果大于8,值为3 + core * 5 / 8)。Eden、From Space和To Space的大小通过SurvivorRatio来控制(-XX:SurvivorRatio=8表示Eden区和FromSpace的大小为8:1,默认情况就是这个值)。

当在Eden区中申请内存空间时,如果Eden区不够,那么看当前申请的空间是否大于等于Eden的一半,如果大于则这次申请的空间直接在Old中分配,如果小于则触发Minor GC。在触发GC之前首先会检查每次晋升到Old区的平均大小是否大于Old区的剩余空间,如大于则再触发Full GC。这次触发GC后会按照这个规则再重新检查一次,也就是Full GC会执行两次。Full GC时清空堆中所有的垃圾对象和Perm区中已经被卸载的类信息,并进行压缩。这里有几个配置项了解一下

AlwaysTenure:默认为flase,表示只要Minor GC时存活就晋升到Old区

NeverTenure:默认为false,表示永远晋升到Old区

UseAdaptiveSizePolicy:上面的两个都没设置时,使用该配置项,在启动时以InitialTenuringThreshold值作为存活次数的阈值,每次GC后会动态调整。-XX: - UseAdaptiveSizePolicy表示不使用该配置

3)ParallelOldGC

通过-XX:+UseParallelOldGC参数来指定,并行回收的线程数由-XX:ParallelOldGCThreads指定(如果CPU的核数小于8,线程数可以和核数一样,如果大于8,值为3 + core * 5 / 8)。和ParallelGC只有一点不同,Full GC时清空堆中部分的垃圾对象,进行部分的空间压缩,并不是所有垃圾对象。

CMS Collector

通过-XX:+UseConcMarkSweepGC参数来指定,并发回收的线程数由-XX:ParallelCMSThreads指定(默认值为4)。该收集算法因为是并发的,所以不会导致“stop-the-world”,这一点在很多场景下是很有用的,例如WebServer,不会导致用户无法访问页面。触发规则为检查Old区或者Perm区的使用率,当达到一定比例时(CMSInitialOccupancyFraction指定)触发CMS GC,触发时会回收Old区中的内存空间。触发Full GC的两种情况:

1)Eden分配失败,Minor GC后分配到To Space,ToSpace不够再分配到Old区,Old区不够再出发Full GC

2)当CMS GC正在进行时向Old申请内存失败则会直接触发Full GC。

G1

java9默认回收器。通过-XX:+UseG1GC参数来指定。可设置预期的停顿时间,不会产生内存碎片。G1将整个堆划分成多个等大的独立区域(region),保留了新生代、老年代,但是不再是物理隔离,而是部分region的集合。G1跟踪各个Region里的垃圾回收价值大小(回收所获得的空间大小以及所需时间的经验值;维护了一个优先列表,每次根据允许的回收时间,优先回收价值最大的region(用最短的时间回收最多的垃圾),这也是Garbage-First名称的由来

如何选择垃圾回收器

官网是给出了建议:

If the application has a small data set (up to approximately 100 MB), then select the serial collector with the option -XX:+UseSerialGC.小内存使用Serial即可If the application will be run on a single processor and there are no pause-time requirements, then select the serial collector with the option -XX:+UseSerialGC.单核处理器,并且没有停顿时间要求,使用SerialIf (a) peak application performance is the first priority and (b) there are no pause-time requirements or pauses of one second or longer are acceptable, then let the VM select the collector or select the parallel collector with -XX:+UseParallelGC.追求高性能,但是对停顿时间没要求,使用ParallelIf response time is more important than overall throughput and garbage collection pauses must be kept shorter than approximately one second, then select a concurrent collector with -XX:+UseG1GC or -XX:+UseConcMarkSweepGC.响应时间比吞吐量更主要,并且停顿时间不能太长,则使用G1或者CMS;如果分配的堆内存达到6G或以上,则使用G1

关于各种垃圾回收器的调优,官网很详细,需要一点英文基础

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。