微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

2019年互联网 JVM (2.1)

1.JVM垃圾回收的时候如何确定垃圾?是否知道什么是GC Roots "/"

  • 什么是垃圾
    • 简单来说就是内存中已经不在被使用到的空间就是垃圾
  • 要进行垃圾回收,如何判断一个对象是否可以被回收?
    • 引用计数法
    • 枚举根节点做可达性分析

为了解决引用计数法的循环引用问题,Java 使用了可达性算法。

img

跟踪收集器采用的为集中式的管理方式,全局记录对象之间的引用状态,执行时从一些列GC Roots的对象做为起点,从这些节点向下开始进行搜索所有的引用链,当一个对象到GC Roots 没有任何引用链时,则证明此对象是不可达的。

图中,对象Object6、Object7、Object8虽然互相引用,但他们的GC Roots是不可到达的,所以它们将会被判定为是可回收的对象。

哪些对象可以作为 GC Roots 的对象:

  • 虚拟机栈(栈帧中的局部变量区,也叫局部变量表)中引用的对象

    • 例如: GCRootDemo t1=new GCRootDemo();   进行一次GC
      
  • 方法区中的类静态属性引用的对象

    • private static GCRootDemo2 t2; 这类静态属性引用对象,也是GC的目标
      
  • 方法去常量引用的对象

    • private static final GCRootDemo t3=new GCRootDemo3(); 常量对象的GC
      
  • 本地方法栈中 JNI (Native方法)引用的对象

  • 同步锁的对象和反射得到的对象(不清楚)

2. JVM调优和参数配置?如何查看JVM系统认值

JVM操作参数:

如何查看一个正在运行的Java程序,它的某个Jvm参数是否开启,具体值为多少?
jps -l 查看所有正在运行的进程
jinfo -flag 具体参数 java进程号
jinfo -flags   java进程编号

盘点家底查看 JVM 认值 下面三个参数 必须都背下来

查看初始认值:-XX:+PrintFlagsInitial  !!重要!!命令

查看修改更新:-XX:+PrintFlagsFinal
			= 与 := 的区别是,一个认,一个是人为改变或者 jvm 加载时改变的参数
			
打印命令行参数(可以看垃圾回收器):-XX:+PrintCommandLineFlags

JVM的参数类型

​ 标配参数

  • -version
  • -help

X 参数(了解)

  • -Xint :解释执行
  • -Xcomp:第一次使用就编译成本地代码
  • -Xmixed: 混合模式

XX 参数

  • Boolean 类型:【公式如右边】-XX:+ 或者 - 某个属性值(+ 表示开启,- 表示关闭

    • -XX:+PrintGCDetails:打印 GC 收集细节
    • -XX:-PrintGCDetails:不打印 GC 收集细节
    • -XX:+UseSerialGC:使用了串行收集器
    • -XX:-UseSerialGC:不使用了串行收集器
  • KV 设置类型:-XX:key=value

    • -XX:MetaspaceSize=128m

    • 元空间的初始值,认为21807104

      image-20210222223130027

      修改以后:

      image-20210222223323939

    • -XX:MaxTenuringThreshold=15

  • HelloGC.java ,如何查看当前运行程序的配置 jinfo.exe

    public class HelloGC {
        public static void main (String[] args) throws InterruptedException {
            System.out.println("hello GC");
            Thread.sleep(Integer.MAX_VALUE);
        }
    }
    

    我们可以使用 jps -l 命令,查出进程 id

    image-20210222222454112

    在使用 jinfo -flag PrintGCDetails 15164命令查看

    -XX:-PrintGCDetails #打印
    

    可以看出认是不打印 GC 收集细节

    添加 JVM运行时参数,再次重复上述操作,

    image-20210222222715632

结果如下: -XX:+PringtGCDetails 表示启动了该参数

image-20210222222807018

两个经典参数:-xms 和 - Xmx(如 -xms1024m)

  • -xms 等价于 -XX:InitialHeapSize 初始化堆内存
  • -Xmx 等价于 -XX:MaxHeapSize 最大堆内存

采用jinfo -flags 进程号 查看所有命令,注意是flags

image-20210222223940035

3. JVM常用基本配置参数

快速复习 堆内存

image-20210222231139050

  • -xms
    • 初始大小内存,认为物理内存 1/64
    • 等价于 -XX:InitialHeapSize
  • -Xmx
    • 最大分配内存,认为物理内存的 1/4
    • 等价于 -XX:MaxHeapSize
  • -Xss
    • 设置单个线程栈的大小,一般认为 512-1024k
    • 等价于 -XX:ThreadStackSize
  • -Xmn
    • 设置年轻代的大小
    • 整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小,持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
  • -XX:MetaspaceSize
    • 设置元空间大小(元空间的本质和永久代类似,都是对 JVM 规范中的方法区的实现,不过元空间于永久代之间最大区别在于,元空间并不在虚拟中,而是使用本地内存,因此认情况下,元空间的大小仅受本地内存限制)
    • 元空间认比较小,我们可以调大一点
  • -XX:+PrintGCDetails
    • 输出详细 GC 收集日志信息
    • u=258342367,4213737490&fm=26&gp=0

    • 设置 JVM 参数为: -xms10m -Xmx10m -XX:+PrintGCDetails
  • -XX:SurvivorRatio
    • 设置新生代中 eden 和 S0/S1 空间比例
    • 认 -XX:SurvivorRatio=8,Eden : S0 : S1 = 8 : 1 : 1
    • -XX:+PrintGCDetails -XX:+UseSerialGC -xms10m -xms10m 加了该代码如下
    • image-20210223005047586

  • -XX:NewRatio
    • 配置年轻代和老年代在堆结构的占比
    • 认 -XX:NewRatio=2 新生代占1,老年代占2,年轻代占整个堆的 1/3
  • -XX:MaxTenuringThreshold

4.强引用、软引用、弱引用、虚引用

在Java语言中,除了基本数据类型外,其他的都是指向各类对象的对象引用;Java中根据其生命周期的长短,将引用分为4类。

强引用

只要有人指着,死都不回收OOM也不回收

  • 我们平常典型编码Object obj = new Object()中的 obj 就是强引用,通过关键字new创建的对象所关联的引用就是强引用
  • 当JVM内存空间不足,JVM宁愿抛出 OutOfMemoryError 运行时错误(OOM),使程序异常终止,也不会靠随意回收具有强引用的“存活”对象来解决内存不足的问题。
  • 对于一个普通的对象,如果没有其他的引用关系,只要超过了引用的作用域或者显式地将相应强引用赋值为 null,就是可以被垃圾收集的了,具体回收时机还是要看垃圾收集策略。

代码Demo

public class StrongReferenceDemo {
    public static void main (String[] args) {
        Object o1=new Object();//强引用
        Object o2;
        o2=o1;
        o1=null;
        System.gc();
        System.out.println(o2);
    }
}

软引用

在内存充足的时候不回收,内存不够的时候回收

  • 软引用通过SoftReference类实现, 软引用的生命周期比强引用短一些。
  • 只有当 JVM 认为内存不足时,才会去试图回收软引用指向的对象:即 JVM 会确保在抛出 OutOfMemoryError 之前,清理软引用指向的对象。
  • 软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。后续,我们可以调用ReferenceQueue的poll()方法来检查是否有它所关心的对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象。
  • 应用场景:软引用通常用来实现内存敏感的缓存。如果还有空闲内存,就可以暂时保留缓存,当内存不足时清理掉,这样就保证了使用缓存的同时,不会耗尽内存。

代码验证Demo

package com.ybzn._01.jvm;

import java.lang.ref.softReference;

/**
 * 软引用
 *  在内存充足的时候不回收,内存不够的时候回收
 * @author Hugo
 * @time 2021/2/23
 */
public class SoftReferenceDemo {
    /**
     * 内存足够的时候,软引用
     */
    public static void softRef_Memory_Enough(){
        Object o1 =new Object();
        SoftReference<Object> softReference =new SoftReference <>(o1);
        System.out.println(o1);
        System.out.println(softReference.get());

        //开始折腾
        o1=null;
        System.gc();

        System.out.println(o1);
        System.out.println(softReference.get());
    }

    public static void main (String[] args) {
//        softRef_Memory_Enough();
        softRef_Memory_NoEnough();
    }

    /**
     * 内存不够 的时候软引用
     * 运行时 参数:
     *              -xms5m -Xmx5m -XX:+PrintGCDetails
     */
    private static void softRef_Memory_NoEnough () {
        Object o1 =new Object();
        SoftReference<Object> softReference =new SoftReference <>(o1);
        System.out.println(o1);
        System.out.println(softReference.get());

        //开始折腾
        o1=null;

        try {
            byte[] bytes=new byte[6000*1024*1024];
        }catch (Exception e){
            e.printstacktrace();
        }finally {
            System.out.println(o1);
            System.out.println(softReference.get());
        }
    }

}

结果: 爆OOM的时候,会回收软引用

image-20210223112641819

弱引用

  • 弱引用通过 WeakReference 类实现, 弱引用的生命周期比软引用短。

  • 垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。由于垃圾回收器是一个优先级很低的线程,因此不一定会很快回收弱引用的对象。

  • 弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

  • 应用场景:弱应用同样可用于内存敏感的缓存。

  • 代码验证Demo

  • public class WeakReferenceDemo {
        public static void main(String[] args) {
            Object obj = new Object();
            WeakReference<Object> weakReference = new WeakReference<>(obj);
            System.out.println(obj);
            System.out.println(weakReference.get());
    
            obj = null;
            System.gc();
            System.out.println("GC之后....");
            
            System.out.println(obj);
            System.out.println(weakReference.get());
        }
    }
    
  • 结果分析: 弱引用,不管内存是否充足,都会被GC垃圾回收掉!

  • image-20210223162633602

软引用和弱引用的应用场景

假如有一个应用需要读取大量的本地图片

  • 如果每次读取图片都需要从硬盘中读取,则会严重损害性能
  • 如果一次性全部加载到内存又会爆OOM 内存溢出

·此时可以采用软引用解决这个问题!

设计思路:

​ 用一个HashMap来保存图片的路径和响应图片对象关联的软引用直接的映射关系,在内存不足的时候,JVM会自动回收这些缓存图片所占的空间,从而有效避免了OOM的问题

Map<String,WeakReference<Bitmap>> weakReferenceMap =new HashMap <>();

软引用—-WeakHashMap 进行GC清理的时候,会清理WeakHashMap中的对象

但是使用HashMap 进行GC清理的时候,哪怕key=null也不会清理HashMap中的对象,因为他们是强引用

引用队列—-Demo.java

public class ReferenceQueueDemo {
    public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        WeakReference<Object> weakReference = new WeakReference<>(obj, referenceQueue);
        System.out.println(obj);
        System.out.println(weakReference.get());
        System.out.println(weakReference);

        obj = null;
        System.gc();
        Thread.sleep(500);

        System.out.println("GC之后....");
        System.out.println(obj);
        System.out.println(weakReference.get());
        System.out.println(weakReference);
    }
}

会把该对象的包装类即weakReference放入到ReferenceQueue里面,我们可以从queue中获取到相应的对象信息,同时进行额外的处理。比如反向操作,数据清理等。

输出java.lang.Object@1540e19d
java.lang.Object@1540e19d
java.lang.ref.WeakReference@677327b6
GC之后....
null
null
java.lang.ref.WeakReference@677327b6

虚引用

  • 虚引用也叫幻象引用,通过Phantomreference类来实现,无法通过虚引用访问对象的任何属性函数

  • 幻象引用仅仅是提供了一种确保对象被 finalize 以后,做某些事情的机制。

  • 如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收

  • 虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。

  • ReferenceQueue queue =new ReferenceQueue();
    Phantomreference pr =new Phantomreference(object, queue);
    
  • 程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取一些程序行动。

  • 应用场景:可用来跟踪对象被垃圾回收器回收的活动,当一个虚引用关联的对象被垃圾收集器回收之前会收到一条系统通知

5. 理解OOM

  • java.lang.StackOverflowError
  • java.lang.OutOfMemoryError : Java heap space
    • new 一个很大对象byte[] bytes=new byte[30*1024*1024]
  • java.lang.OutOfMemoryError : GC overhead limit exceeded
    • 执行垃圾收集的时间比例太大, 有效的运算量太小,认情况下,,如果GC花费的时间超过 98%, 并且GC回收的内存少于 2%, JVM就会抛出这个错误
  • java.lang.OutOfMemoryError : Direct buffer memory
    • 本地内存空间不够,这里主要用于NIO中的buffer缓冲里面实现的,有两种,一种是存放在JVM堆里面的,另外一种是存放在本地内存方法中的

配置参数:-xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m

public class DirectBufferDemo {
    public static void main(String[] args) {
        System.out.println("maxDirectMemory : " + sun.misc.VM.maxDirectMemory() / (1024 * 1024) + "MB");
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6 * 1024 * 1024);
    }
}

输出

maxDirectMemory : 5MB
[GC (System.gc()) [PSYoungGen: 1315K->464K(2560K)] 1315K->472K(9728K), 0.0008907 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[Full GC (System.gc()) [PSYoungGen: 464K->0K(2560K)] [ParOldGen: 8K->359K(7168K)] 472K->359K(9728K), [Metaspace: 3037K->3037K(1056768K)], 0.0060466 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
    at java.nio.Bits.reserveMemory(Bits.java:694)
    at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
    at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
    at com.cuzz.jvm.DirectBufferDemo.main(DirectBufferDemo.java:17)
Heap
 PSYoungGen      total 2560K, used 56K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
  eden space 2048K, 2%【"这里只用了很少的空间,但是任然爆内存了"】 used [0x00000000ffd00000,0x00000000ffd0e170,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
  to   space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
 ParOldGen       total 7168K, used 359K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
  object space 7168K, 5% used [0x00000000ff600000,0x00000000ff659e28,0x00000000ffd00000)
 Metaspace       used 3068K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 336K, capacity 388K, committed 512K, reserved 1048576K
  • java.lang.OutOfMemoryError : unable to create new native thread

    • 创建的线程太多了
    • new Thread().start()方法执行两次,爆IllegalThreadStateException 非法线程状态异常
  • java.lang.OutOfMemoryError : Metaspace

    • Java 8 之后的版本使用元空间(Metaspace)代替了永久代,元空间是方法区在 HotSpot 中的实现,它与持久代最大的区别是:元空间并不在虚拟机中的内存中而是使用本地内存
    • 元空间存放的信息:
      • 虚拟机加载的类信息
      • 常量池
      • 静态变量
      • 即时编译后的代码

6.GC垃圾回收算法和垃圾收集器的关系?分别谈谈

  • 四种GC垃圾回收算法
  • GC算法是内存回收的方法论,垃圾收集器是算法的落地实现
  • 目前还没有完美的收集算法出现,更加没有万能的收集器,只是针对具体业务需求的收集,进行分带收集。

常用的垃圾收集器

串行垃圾收集器(Serial)

针对单线程环境下设计的且使用一个线程进行垃圾回收,会暂停所有的用户线程,所以不适合服务环境

并行垃圾回收期(Parallel)

​ 多个垃圾收集线程并行工作,此时用户线程是暂停的,用于科学计算、大数据处理等弱交互场景

并发垃圾收集器(CMS)

用户线程和垃圾收集线程同时执行(不一定并行,可能交替执行),不需要停顿用户线程,互联网公司大多数采用CMS垃圾收集器,适合于对响应时间有要求的场景

G1垃圾收集器

​ G1垃圾收集器将堆内存分割成不同的区域,然后并发对其进行垃圾回收。

Java 11 新出了另外一种ZGC

7.怎么查看服务器认的垃圾回收器是那个? 生产上如何配置垃圾收集器?垃圾收集器的理解

  • 怎么查看服务器垃圾收集器是哪个?

    • java -XX:+PrintCommandLineFlags -versionTerminal
    • jps -l获取进程号---》jinfo UseSerialGC(查询GC收集器) 进程号
  • Java 的 GC 回收的类型主要有:

    • UseSerialGC,UseParallelGC,UseConcmarkSweepGC,UseParNewGC,UseParallelOldGC,UseG1GC
    • Java 8 以后基本不使用 Serial Old
  • 垃圾收集器(上面年轻代,下面老年代)
    timg

  • 参数说明

    • DefNew : Default New Generation
    • Tenured : Old
    • ParNew : Parallel New Generation
    • PSYoungGen : Parallel Scavenge
    • ParOldGen : Parallel Old Generation
  • Server/Client 模式分别是什么意思

    • 最主要的差别在于:-Server模式启动时,速度较慢,但是一旦运行起来后,性能将会有很大的提升。
    • 当虚拟机运行在-client模式的时候,使用的是一个代号为C1的轻量级编译器, 而-server模式启动的虚拟机采用相对重量级,代号为C2的编译器,C2比C1编译器编译的相对彻底,服务起来之后,性能更高。
    • 所以通常用于做服务器的时候我们用服务端模式,如果你的电脑只是运行一下java程序,就客户端模式就可以了。当然这些都是我们做程序优化程序才需要这些东西的,普通人并不关注这些专业的东西了。其实服务器模式即使编译更彻底,然后垃圾回收优化更好,这当然吃的内存要多点相对于客户端模式。
  • 新生代

    • 串行 GC (Serial/ Serital copying)
      • 单线程收集器(Stop The Work –STW),必须暂停其他线性来进行工作
    • 并行 GC (ParNew)
      • ParNew收集器其实就是Serial收集器新生代的并行多线程版本,最常见的应用场景就是配合老年代的CMS GC工作,其余行为和Serial收集器完全一样,ParNew垃圾收集器在垃圾收集过程中同样需要暂停所有其他工作线程。是很多java虚拟机运行在Server模式下新生代的垃圾收集器
      • 对应的JVM参数:-XX:+UserParNewGC 启动ParNew收集器,只是影响新生代的收集,不影响老年代,并且会启动ParNew(Young区)+Serial Old(old 区)收集器组合使用,新生代使用复制算法,老年代采用标记-整理算法
    • 并行回收 GC (Parallel/ Parallel Scanvenge)
      • Parallel Scanvenge收集器类似于ParNew也是一个新生代垃圾收集器,使用复制算法,也是一个并行的多线程的垃圾收集器,俗称吞吐量优先收集器。一句话:串行收集器在新生代和老年代的
      • 对应的JVM参数:-XX:+UseParallelGC或者-XX:+UseParallelOldGC可互相激活,使用Parallel Scanvenge收集器,开启该参数以后,新生代使用复制算法,老年代采用标记-整理算法

老年代

  • 串行 GC (Serial Old/ Serial MSC)

  • 并行 GC (Parallel Old/ Parallel MSC)

  • 并发标记清除 GC (CMS) 养老区

    • 是一种以获取最短回收停顿时间为目标的收集器,适合应用在互联网站或者 B/S 系统的服务器上,这个类应用尤其重视服务器的响应速度,希望系统停顿时间最短年轻代采用ParNew收集器

    • CMS 非常适合堆内存大、cpu 核数多服务器端应用,也是 G1 出现之前大型应用首选收集器。

    • 并发停顿比较少,并发指的是与用户线程一起执行。

    • 过程

      • 初始标记(initail mark):只是标记一下 GC Roots 能直接关联的对象,速度很快,需要暂停所有的工作线程
      • 并发标记(concurrent mark 和用户线程一起):进行 GC Roots 的跟踪过程,和用户线程一起工作,不需要暂停工作线程。
      • 重新标记(remark):为了修正在并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,仍然需要暂停所有的工作线程。
      • 并发清除(concurrent sweep 和用户线程一起):清除 GC 不可达对象,和用户线程一起工作,不需要暂停工作线程,基于标记结果,直接清除。由于耗时最长的并发标记和并发清除过程中,垃圾收集线程和用户线程可以一起并发工作,所以总体来看 CMS 收集器的内存回收和用户线程是一起并发地执行。
    • 优缺点

      • 优点:并发收集停顿低
      • 缺点:并发执行对 cpu 资源压力大,采用的标记清除算法会导致大量碎片
    • 由于并发进行, CMS 在收集与应用线程会同时增加对堆内存的占用,也就是说,CMS 必须要在老年代堆用尽之前完成垃圾回收,否者 CMS 回收失败,将触发担保机制,串行老年代收集器将会以 STW 的方式进行一次 GC,从而造成较大的停顿时间。

    • 标记清除算法无法整理空间碎片,老年代空间会随着应用时长被逐渐耗尽,最后将不得不通过担保机制对堆内存进行压缩。CMS 也提供了参数 -XX:CMSFullGCsBeForeCompaction (认0,即每次都进行内存整理) 来指定多少次 CMS 收集之后,进行一次压缩

    • 垃圾收集器配置代码总结,配置新生代收集器,老年代收集器会自动配置上。

      1558237229584

      1558237229584

    • 如何选择垃圾收集器

      • cpu 或者小内存,单机程序:-XX:UseSerialGC
      • cpu 需要最大吞吐量,如后台计算型应用:-XX:UseParallelGC 或者 -XX:UseParallelOldGC
      • cpu 追求低停顿时间,需要快速响应,如互联网应用:-XX:+UseConcmarkSweepGC

    G1 垃圾收集器你了解吗?

以前收集器的特点

  • 年轻代和老年代是各自独立且连续的内存块
  • 年轻代收集器使用 eden + S0 + S1 进行复制算法
  • 老年代收集必须扫描整个老年代区域
  • 都是以尽可能的少而快速地执行 GC 为设计原则

G1 是什么

  • G1 是一种面向服务端垃圾收集器,应用在多核处理器和大容量内存环境中,在实现高吞吐量的同时,尽可能的满足垃圾收集器的暂停时间要求。
  • 像 CMS 收集器一样,能与应用程序线程并发执行,整理空闲空间更快,需要更多的时间来预测 GC 停顿时间,不希望牺牲大量的吞吐性能,不需要更大的 JAVA Heap。
  • G1 收集器的设计目的是取代 CMS 收集器,同时与 CMS 相比,G1 垃圾收集器是一个有整理内存过程的垃圾收集器,不会产生很多内存碎片。G1 的 Stop The World 更可控,G1 在停顿上添加了预测机制用户可以指定期望的停顿时间。
  • G1 是在 2012 年才在 jdk.1.7u4 中可以呀用,在 jdk9 中将 G1 变成垃圾收集器来代替 CMS。它是以款面向服务应用的收集器。
  • 主要改变是 Eden、Survivor 和 Tenured 等内存区域不再是连续的,而是变成了一个个大小一样的 region,每个 region 从 1M 到 32M 不等,一个 region 有可能属于 Eden、Survivor 或者 Tenured 内存区域。

G1的特点

  • G1 能充分利用多 cpu、多核环境硬件优势,尽量缩短 STW。
  • G1 整体采用标记-整理算法,局部是通过是通过复制算法,不会产生内存碎片
  • 宏观上看 G1 之中不在区分年轻代和老年代,被内存划分为多个独立的子区域。
  • G1 收集器里面讲整个的内存区域混合在一起,但其本身依然在小范围内要进行年轻代和老年代的区分。保留了新生代和老年代,但她们不在是物理隔离,而是一部分 Region 的集合且不需要 Region 是连续的,也就是说依然会采用不同的 GC 方式来处理不同的区域。****
  • G1 虽然也是分代收集器,但整个内存分区不存在物理上的年轻代和老年代的区别,也不需要完全独立的 Survivor to space 堆做复制准备。G1 只有逻辑上的分代概念,或者说每个分区都可能随 G1 的运行在不同代之间前后切换。

G1的常用参数

  • -XX:+UseG1GC
  • -XX:G1HeapRegionSize=n; 设置G1区域大侠你。值是2的幂次方,范围是1M-32M,目标是根据最小的Java堆来进行划分得出的
  • -XX:MaxGCPauseMillis=n; 最大GC停顿时间,这个是软目标,JVM尽可能停顿小于该时间
  • -XX:InitiatingHeapOccupancyPercent=n; 堆占用了多少的时候就触发GC,认45
  • -XX:ConcGCThreads=n; 并发GC的使用线程数
  • -XX:G1ReservePercent=n; 设置作为空闲空间的预留内存百分比,以降低目标空间溢出的风险,认值为10%

底层原理

Region 区域化垃圾收集器:最大好处是化整为零,避免全内存扫描,只需要按照区域来进行扫描即可。

5611237-f643066bd97c7703

5611237-f643066bd97c7703

​ G1的内存结构和传统的内存空间划分有比较的不同。G1将内存划分成了多个大小相等的Region(认是512K),Region逻辑上连续,物理内存地址不连续。同时每个Region被标记成E、S、O、H,分别表示Eden、Survivor、Old、Humongous。其中E、S属于年轻代,O与H属于老年代。

H表示Humongous。从字面上就可以理解表示大的对象(下面简称H对象)。当分配的对象大于等于Region大小的一半的时候就会被认为是巨型对象。H对象认分配在老年代,可以防止GC的时候大对象的内存拷贝。通过如果发现堆内存容不下H对象的时候,会触发一次GC操作。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。

相关推荐